Protocol extending Encodable (or Codable) does not conform to it

天大地大妈咪最大 提交于 2019-12-02 01:09:48

问题


I have 2 protocols, Filters and Parameters, both of which extend Encodable

protocol Filters: Encodable {
    var page: Int { get }
}

protocol Parameters: Encodable {
    var type: String { get }
    var filters: Filters { get }
}

I create structs conforming to these protocols, thusly…

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)

Which fails because…

error: type 'BankAccountParamters' does not conform to protocol 'Encodable'

note: cannot automatically synthesize 'Encodable' because 'Filters' does not conform to 'Encodable'

Filters clearly does conform to Encodable (at least it seems that way to me). Is there a way around this?


回答1:


As discussed in Protocol doesn't conform to itself?, a protocol does not conform to itself, or to a protocol that it inherits from. In your case, Filters does not conform to Encodable.

A possible solution is to make struct BankAccountParamters and protocol Parameters generic:

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
}

Now var filters has type T, which conforms to Filters and consequently, to Encodable.

This compiles and produces the expected result:

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}}



回答2:


You cannot have protocol reference in the struct as the compiler will not be able to know the type at the time of encoding. Here is the bug reported SR-5853.

What you can do is create a type erasure for your protocol and use the erasure in place of protocol.

Something like this:

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)!)

Here you will get the output:

{"type":"Bank","filters":{"isWithdrawal":true,"page":1}}


来源:https://stackoverflow.com/questions/50346052/protocol-extending-encodable-or-codable-does-not-conform-to-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!