tiam
(Tiam)
1
Hi,
Here I'm trying to get Bar to conform to both BarProtocol and Codable:
// ModuleA
public protocol FooProtocol {
var fooValue: Int { get }
}
public protocol BarProtocol {
var foos: [FooProtocol] { get }
}
public func load(bar: BarProtocol) {
for foo in bar.foos { /* ... */ }
}
// ModuleB
import ModuleA
struct Foo: Codable, FooProtocol {
var fooValue: Int
}
struct Bar: Codable, BarProtocol { // ❌ Type 'Bar' does not conform to protocol 'BarProtocol'
var foos: [Foo]
}
func test() throws {
let data = """
{"foos" : [ {"fooValue" : 42} ] }
""".data(using: .utf8)!
let bar = try JSONDecoder().decode(Bar.self, from: data)
ModuleA.load(bar: bar)
}
Unfortunately the var foos: [FooProtocol] requirement cannot be fulfilled with var foos: [Foo].
Also if I change Bar to this, then I lose conformance to Codable:
struct Bar: Codable, BarProtocol { // ❌ Type 'Bar' does not conform to protocol 'Decodable'
var foos: [FooProtocol]
}
I found one way to get Bar to conform to both protocols like this:
struct Bar: Codable, BarProtocol {
var _foos: [Foo]
var foos: [FooProtocol] { return _foos }
}
Is there another way to achieve that without using that intermediary _foo property?
Maybe by changing something inside Bar implementation, or maybe inside the BarProtocol declaration?
Thank you.
ole
(Ole Begemann)
2
One way to make it work is to not use existentials and give BarProtocol an associated type instead:
public protocol BarProtocol {
associatedtype FooType: FooProtocol
var foos: [FooType] { get }
}
This then requires your load function to become generic (at least until SE-0309: Unlock existentials for all protocols lands):
public func load<BarType: BarProtocol>(bar: BarType) {
for foo in bar.foos { /* ... */ }
}
This makes your code compile. Whether it’s a workable solution for you is another question and depends on how you intend BarProtocol to be used.
2 Likes
tiam
(Tiam)
3
I followed your advice and made it generic. Thank you for your help!
1 Like