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?
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}}
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}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With