我有两个协议,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'不符合协议'Encodable'
注意:无法自动合成“可编码”,因为“过滤器”不符合“可编码”
Filters
显然 符合Encodable
(至少对我来说似乎是这样)。有办法解决这个问题吗?
答案 0 :(得分:5)
如Protocol doesn't conform to itself?中所述,协议不符合自身,或符合
它继承的协议。在您的情况下,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}}
答案 1 :(得分:2)
您不能在结构中使用协议引用,因为编译器在编码时无法知道类型。这是报告的错误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}}
答案 2 :(得分:0)
为了在不使用泛型的情况下获得您想要的结果,因为在您的代码中使用泛型会带来多个问题,您可以为结构体 encode
提供您自己的 BankAccountParameters
方法实现。< /p>
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)
的自定义实现。