Search code examples
iosswiftgenericsswift-protocols

Swift - Injecting and storing protocols


I have a mapper as follows

protocol MapperProtocol {
    associatedtype T
    associatedtype U
    func map(item: T) -> U?
}

And I want to inject it to a class as follows

protocol ParserProtocol {
    associatedtype T
    associatedtype U
    func parse(from: T) -> U?
}

class TrackParser: ParserProtocol {
    typealias T = String
    typealias U = Track
    
    private let mapper: MapperProtocol
    
    init(mapper: MapperProtocol) {
        self.mapper = mapper
    }
    
    func parse(from path: String) -> Track? {
        guard let data = try? Data(contentsOf: URL(filePath: path)) else { return nil }
        return mapper.map(item: data)
    }
}

TrackParser will be initialised from somewhere else so it doesn't need to know the concrete type of the mapper.

When I want to implement it that way I get the following error. Error screenshot Any ideas how to fix it?


Solution

  • Notice that the parse implementation requires that the mapper has T == Data and U == Track, but you haven't specified those constraint anywhere in TrackParser.

    We can make T and U the primary associated types of MapperProtocol, so that the same-type requirements can be specified very easily as MapperProtocol<Data, Track>.

    protocol MapperProtocol<T, U> { // Notice the "<T, U>"
        associatedtype T
        associatedtype U
        func map(item: T) -> U?
    }
    

    Also, starting from Swift 5.7, existential types are required to be prefixed with "any", so you would write:

    private let mapper: any MapperProtocol<Data, Track>
    
    init(mapper: any MapperProtocol<Data, Track>) {
        self.mapper = mapper
    }