Kurt Horimoto
Kurt Horimoto

Reputation: 99

Returning a protocol with associatedtype from another protocol API

I have a Session protocol with an Output associated type:

public protocol SessionAPI {
  associatedtype Output: Equatable
  var output: Output { get }
}

And a concrete implementation of the protocol that returns a String:

public final class StringSession: SessionAPI {
  public typealias Output = String
  public let output: String
}

Let's assume that the implementation of StringSession is very complex and touches many modules, and I don't want to add dependencies to those modules in classes that use the SessionAPI. So I have another protocol that vends StringSessions using a generic factory method:

public protocol SessionFactoryAPI {
  func createStringFactory<T: SessionAPI>() -> T where T.Output == String
}

All of this compiles fine. However, when I try to implement the factory API, I get a compilation error:

public final class SessionFactory: SessionFactoryAPI { public func createStringFactory<T: SessionAPI>() -> T where T.Output == String { // Error: Cannot convert value of type 'StringSession' to expected argument type 'T' return StringSession() } }

Does anyone have any suggestions on how to get this to work?

Upvotes: 1

Views: 58

Answers (1)

AnderCover
AnderCover

Reputation: 2671

Error: Cannot convert value of type 'StringSession' to expected argument type 'T' return

Means that the compiler doesn't know that T should be SessionAPI.

In SessionFactoryAPI:

public protocol SessionFactoryAPI {
  func createStringFactory<T: SessionAPI>() -> T where T.Output == String
}

you are only specifying what T.Output should be (ie. String)

If you really need the constraint Output == String, you can try declaring a StringSessionAPI protocol:

public protocol StringSessionAPI: SessionAPI where Output == String { }
public final class StringSession: StringSessionAPI {
    public typealias Output = String

    public let output: String
    public init(output: String) {
        self.output = output
    }
}

and return any StringSessionAPI

public protocol SessionFactoryAPI {
    func createStringFactory() -> any StringSessionAPI
}

struct MyFactory: SessionFactoryAPI {
    func createStringFactory() -> any StringSessionAPI {
        StringSession(output: "output")
    }
}

let factory = MyFactory()
let stringSession: any StringSessionAPI = factory.createStringFactory()
print(stringSession.output) // prints "output"

Or you can try using by using an associated type, instead of a generic function:

public protocol SessionFactoryAPI {
    associatedtype T: SessionAPI where T.Output == String
    func createStringFactory() -> T
}

struct MyFactory: SessionFactoryAPI {
    func createStringFactory() -> StringSession {
        .init(output: "output")
    }
}

let factory = MyFactory()
let stringSession: StringSession = factory.createStringFactory()
print(stringSession.output) // prints "output"

Upvotes: 0

Related Questions