I have two interfaces Controllerable and Interactorable which should work together to achieve something. Following are the protocols:

protocol Controllerable {
    associatedtype Job: Decodable
    func getJob() -> Job
    func control(job: Job)
}

protocol Interactorable {
    associatedtype Job: Decodable
    func interact() -> Job
}

Here are the concrete types which conform to above protocols: One thing to note here, Controller has an Interactor dependency to make things work.

struct ActualJob: Decodable {
    var id: String
}

class Controller<I: Interactorable>: Controllerable {
    typealias Job = ActualJob
    private var interactor: I

    init(interactor: I) {
        self.interactor = interactor
    }

    // MARK: Controllerable
    func control(job: ActualJob) {
        print("Controlling job: \(job.id)")
    }

    func getJob() -> ActualJob {
        return interactor.interact() //***** Line of contention ****
    }
}

class Interactor: Interactorable {
    typealias Job = ActualJob

    // MARK: Interactorable
    func interact() -> Job {
        return ActualJob(id: "xyz")
    }
}

Now, let's say there's a driver entity which drives the integration.

struct Someguy<C: Controllerable> {

    private var controller: C

    init(controller: C) {
        self.controller = controller
    }

    func doSomething() {
        let job = controller.getJob()
        controller.control(job: job)
    }
}

If you put all of this in the playground, you'd the see the following in the console:

playground:30:27: error: cannot convert return expression of type 'I.Job' to return type 'ActualJob'

return interactor.interact()

~~~~~~~~~~~^~~~~~~~~~

as! ActualJob

So, the question is, why, if Interactable.Job and Controllerable.Job conform to the same types, should one get the above error?

EDIT If I change the line of contention to

return interactor.interact() as! ActualJob

The error goes away, but why should this be needed? Does it not defeat the purpose of protocols?

1

There are 1 best solutions below

3
On BEST ANSWER

Just add a constraint:

class Controller<I: Interactorable>: Controllerable where I.Job == ActualJob {

Without the constraint, I.Job can be anything, can't it? It could be SomeOtherJob, which is not convertible to ActualJob.