我有 2 个协议,Filters
和Parameters
,两者都扩展Encodable
protocol Filters: Encodable {
var page: Int { get }
}
protocol Parameters: Encodable {
var type: String { get }
var filters: Filters { get }
}
我创建了符合这些协议的结构,因此...
struct BankAccountFilters: Filters {
var page: Int
var isWithdrawal: Bool
}
struct BankAccountParamters: Parameters {
let type: String = "Bank"
var filters: Filters
}
let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParamters(filters: baf)
失败是因为...
错误:类型"BankAccountParamters"不符合协议"可编码">
注意:无法自动合成"可编码",因为"过滤器"不符合"可编码">
Filters
显然符合Encodable
(至少在我看来是这样(。有没有办法解决这个问题?
正如协议不符合自身?中所讨论的,协议不符合自身,或者 它继承自的协议。在您的情况下,Filters
不符合Encodable
。
一个可能的解决方案是使struct BankAccountParamters
和protocol Parameters
通用:
protocol Filters: Encodable {
var page: Int { get }
}
protocol Parameters: Encodable {
associatedtype T: Filters
var type: String { get }
var filters: T { get }
}
struct BankAccountFilters: Filters {
var page: Int
var isWithdrawal: Bool
}
struct BankAccountParamters<T: Filters>: Parameters {
let type: String = "Bank"
var filters: T
}
现在var filters
有类型T
,它符合Filters
,因此符合Encodable
。
这将编译并生成预期的结果:
let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParamters(filters: baf)
let data = try! JSONEncoder().encode(bap)
print(String(data: data, encoding: .utf8)!)
// {"type":"Bank","filters":{"isWithdrawal":true,"page":1}}
结构中不能有协议引用,因为编译器在编码时将无法知道类型。这是报告的 SR-5853 错误。
您可以做的是为协议创建类型纠删,并使用纠删来代替协议。
像这样:
更新:正如@MartinR回答的那样,这里不需要类型擦除。
protocol Filters: Encodable {
var page: Int { get }
}
protocol Parameters: Encodable {
associatedtype T: Filters
var type: String { get }
var filters: T { get }
}
struct BankAccountFilters: Filters {
var page: Int
var isWithdrawal: Bool
}
struct BankAccountParamters<T: Filters>: Parameters {
let type: String = "Bank"
var filters: T
}
let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParamters(filters: baf)
let encoder = JSONEncoder()
let data = try! encoder.encode(bap)
print(String(data: data, encoding: .utf8)!)
在这里,您将获得输出:
{"type":"Bank","filters":{"isWithdrawal":true,"page":1}}
要在不使用泛型的情况下获得所需的内容,由于在代码中使用泛型会带来多个问题,您可以为结构BankAccountParameters
提供自己的encode
方法实现。
extension BankAccountParamters {
enum CodingKeys: String, CodingKey {
case type, filters
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey: .type)
if let bankAccountFilters = filters as? BankAccountFilters {
try container.encode(bankAccountFilters, forKey: .filters)
} else {
assertionFailure("Type conforming to Filters is not encoded properly")
}
}
这样,您的类型就不必具有泛型约束。但另一方面,您必须确保将来符合Filters
的所有类型都正确编码,这就是 else 块上的assertionFailure
可能会有所帮助的地方。
同样,如果要解码BankAccountParameters
结构,则必须提供init(from: Decoder)
的自定义实现。