Search code examples
swiftgenericsprotocols

Swift: Generic's type protocol not being recognized


Long time listener, first time caller.

I'm getting the following error:

Cannot convert value of type MyClass<Model<A>, OtherClass> to expected argument type MyClass<Protocol, OtherClass>

Despite the fact that MyClass<T> conforms to Protocol

I've attached a snippet that can be run in Playgrounds that resembles what I am actually trying to achieve.

protocol DisplayProtocol {
    var value: String { get }
}

class DataBundle<T: CustomStringConvertible>: DisplayProtocol {
    var data: T
    var value: String {
        return data.description
    }

    init(data: T) {
        self.data = data
    }
}

class Mapper<DisplayProtocol, Data> {
    // ...
}

class MapperViewModel<Data> {
    let mapper: Mapper<DisplayProtocol, Data>

    init(mapper: Mapper<DisplayProtocol, Data>) {
        self.mapper = mapper
    }
}

let dataBundle = DataBundle<Int>(data: 100)
let mapper = Mapper<DataBundle<Int>, Bool>()
let viewModel = MapperViewModel<Bool>(mapper: mapper) // <- This fails w/error

Is this the expected behavior? If it is it feels like its breaking the contract of allowing me to have the DisplayProtocol as a type in Mapper.


Solution

  • This is caused by the fact that Swift generics are invariant in respect to their arguments. Thus MyClass<B> is not compatible with MyClass<A> even if B is compatible with A (subclass, protocol conformance, etc). So yes, unfortunately the behaviour is the expected one.

    In your particular case, if you want to keep the current architecture, you might need to use protocols with associated types and type erasers.