iosswiftswift-protocolsassociated-types

Swift protocol with associatedtype as a parameter type


So here I have this protocol

public protocol UseCase {

    associatedtype ResponseType
    associatedtype Parameters

    func build(params: Parameters) -> Single<ResponseType>

}

public extension UseCase {

    func execute(params: Parameters) -> Single<ResponseType> {
        return build(params: params)
                .subscribeOn(ConcurrentDispatchQueueScheduler(qos: DispatchQoS.background))
                .observeOn(MainScheduler.instance)
    }

}

And I have a Struct that implement the UseCase protocol like this

public struct CreateNewAccount: UseCase {

    private let repository: AuthRepository

    public init(repository: AuthRepository) {
        self.repository = repository
    }

    public func build(params: Params) -> Single<User> {
        return repository.register(params: params)
    }

    public struct Params: RequestParams {
        ...
    }
}

And I want to use this CreateNewAccount on another class, but I don't want to directly use CreateNewAccount and I want to pass it as a UseCase instead, because since It's a protocol It can be easily mocked for testing. But when I do something like this

class RegisterViewModel: ViewModel {

    private let createNewAccount: UseCase // Error on this line

    init(createNewAccount: UseCase) { // Error on this line
        self.createNewAccount = createNewAccount
    }
}

That gave me an error like this

Error:(34, 35) protocol 'UseCase' can only be used as a generic constraint because it has Self or associated type requirements

So, is there's something that I can change from my code to make this kind of case works? Thanks in advance.


Solution

  • You cannot use protocols with associated type as a field.

    You have to use them only as an implementation of classes. In most cases, those classes should are generic.

    For instance, this code is allowed:

    public struct CreateNewAccount<T, K>: UseCase {
        public typealias ResponseType = T
        public typealias Parameters = K
    }
    

    and so,

    private let createNewAccount: CreateNewAccount<YouClass1,YouClass2>
    

    or wrap it somehow by another protocol.