扩展 Encodable(或 Codable)的协议不符合它
Protocol extending Encodable (or Codable) does not conform to it
我有 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)
失败是因为……
error: type 'BankAccountParamters' does not conform to protocol 'Encodable'
note: cannot automatically synthesize 'Encodable' because 'Filters' does not conform to 'Encodable'
Filters
显然 符合 Encodable
(至少在我看来是这样)。有解决办法吗?
您不能在结构中引用协议,因为编译器在编码时无法知道类型。这是报告的错误 SR-5853。
您可以做的是为您的协议创建类型擦除并使用擦除代替协议。
像这样:
Update: As @MartinR answered there is no need of type erasure here.
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}}
如 中所述,协议不符合自身,或
它继承自的协议。在您的情况下,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}}
为了在不使用泛型的情况下得到你想要的东西,因为在你的代码中使用泛型会带来很多问题,你可以为结构 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)
.
的自定义实现
我有 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)
失败是因为……
error: type 'BankAccountParamters' does not conform to protocol 'Encodable'
note: cannot automatically synthesize 'Encodable' because 'Filters' does not conform to 'Encodable'
Filters
显然 符合 Encodable
(至少在我看来是这样)。有解决办法吗?
您不能在结构中引用协议,因为编译器在编码时无法知道类型。这是报告的错误 SR-5853。
您可以做的是为您的协议创建类型擦除并使用擦除代替协议。
像这样:
Update: As @MartinR answered there is no need of type erasure here.
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}}
如 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}}
为了在不使用泛型的情况下得到你想要的东西,因为在你的代码中使用泛型会带来很多问题,你可以为结构 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)
.