0

Could someone explain why won't the following example compile? Am I missing something trivial?

protocol Coordinator {}

protocol Coordinatable {
    var coordinator: Coordinator { get set }
}

class ExampleCoordinator: Coordinator {}

class ViewController: UIViewController, Coordinatable {
    var coordinator: ExampleCoordinator!
}

The error is:

Type 'ViewController' does not conform to protocol 'Coordinatable'

Thanks!

Balázs Vincze
  • 3,550
  • 5
  • 29
  • 60
  • 1
    Does this answer your question? [Protocol variable implementing another protocol](https://stackoverflow.com/questions/49554288/protocol-variable-implementing-another-protocol) (also note that you are providing an _optional_ when the protocol requires a non-optional) – Alladinian May 20 '20 at 20:58
  • It also does, thank you! – Balázs Vincze May 21 '20 at 08:19

2 Answers2

1

Replace var coordinator: ExampleCoordinator! with var coordinator: Coordinator. You are trying to conform to Coordinatable so you have to implement it's properties.

EDIT:

import UIKit
protocol Coordinator {}

protocol Coordinatable {
    var coordinator: Coordinator { get set }
}

class ExampleCoordinator: Coordinator {}

class ViewController: UIViewController, Coordinatable {
    var coordinator: Coordinator

    init(coordinator: Coordinator) {
        self.coordinator = coordinator
        super.init(nibName: nil, bundle: nil)
    }

    // ignore this, it has nothing to do with the question/answer
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

let testVC = ViewController(coordinator: ExampleCoordinator()) // works
RedX
  • 461
  • 1
  • 4
  • 14
  • But `ExampleCoordinator` implements `Coordinator`, so I am implicitly implementing the required property. – Balázs Vincze May 20 '20 at 20:46
  • Well the protocol requires you to implement a `Coordinator` not an `ExampleCoordinator`. So do what I described above and then pass an `ExampleCoordinator` during the initialisation and set `self.coordinator` to this very `ExampleCoordinator` – RedX May 20 '20 at 20:58
  • I updated my answer a bit to make it more clear :) (Just paste it into a playground a play with it) – RedX May 20 '20 at 21:01
0

The reason that it doesn't work

Assuming your code would work and we write additionally

class ExampleCoordinator2: Coordinator {}

val viewController = ViewController()
viewController.coordinator = ExampleCoordinator2()

The assignment to coordinator cannot work since ExampleCoordinator2 is not a subclass of ExampleCoordinator.

So, just considering the types the following code should work but the Swift compiler doesn't allow it (this might work in the future):

protocol Coordinatable {
    var coordinator: Coordinator { get } // removing the `set`
}

class ViewController: UIViewController, Coordinatable {
    var coordinator: ExampleCoordinator! // still an error
}

Possible solutions

Using var coordinator: Coordinator in ViewController

class ViewController: UIViewController, Coordinatable {

    var coordinator: Coordinator

    init(coordinator: Coordinator) {
        self.coordinator = coordinator
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Using only { get } in Coordinatable

protocol Coordinatable {
    var coordinator: Coordinator { get } // removing the `set`
}

class ViewController: UIViewController, Coordinatable {
    // computed property
    var coordinator: Coordinator {
        // implicit return
        exampleCoordinator
    }
    var exampleCoordinator: ExampleCoordinator!
}

Using an associatedtype in Coordinatable

protocol Coordinatable {
    associatedtype C: Coordinator
    var coordinator: C { get set }
}

class ViewController: UIViewController, Coordinatable {
    
    var coordinator: ExampleCoordinator
    
    init(coordinator: ExampleCoordinator) {
        self.coordinator = coordinator
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

This has however a big disadvantage that you cannot have a heterogenous array of type [Coordinatable] since Coordinatable has an associatedtype. And still, you cannot assign a value of type ExampleCoordinator2.

You can only write generic code over <C: Coordinatable>. E.g.

func printCoordinators<C: Coordinatable>(coordinatables: [C]) {
    
    coordinatables.forEach {
        print($0.coordinator)
    }
}
Community
  • 1
  • 1
Qbyte
  • 12,753
  • 4
  • 41
  • 57