Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using protocol's associated type in generic functions

I'm trying to write a simple MVP pattern to follow in my app, so I've written two porotocols to define View Controller and Presenters:

protocol PresenterType: class {
    associatedtype ViewController: ViewControllerType

    var viewController: ViewController? { get set }

    func bind(viewController: ViewController?)
}

protocol ViewControllerType: class {
    associatedtype Presenter: PresenterType

    var presenter: Presenter { get }

    init(presenter: Presenter)
}

After having those defined I started writing some RootViewController and RootViewPresenter. The latter looks like:

protocol RootViewControllerType: ViewControllerType {
}

final class RootPresenter<VC: RootViewControllerType>: PresenterType {
    weak var viewController: VC?

    func bind(viewController: VC?) {
        self.viewController = viewController
    }
}

Up to this point everything complies and is fine, but when I start implementing View Controller like this:

protocol RootPresenterType: PresenterType {
}

final class RootViewController<P: RootPresenterType>: UIViewController, ViewControllerType {
    let presenter: P

    init(presenter: Presenter) {
        self.presenter = presenter
        super.init(nibName: nil, bundle: nil)
    }

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

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.bind(viewController: self)
    }
}

Immediately I get the following error message:

Cannot convert value of type 'RootViewController

' to expected argument type '_?'

I know that protocols with associated types can introduce some limitations, but this example is pretty straightforward and I can't make it work. Is it possible to achieve something that I want, or do I have to look for some other, less Swifty pattern?

like image 552
cojoj Avatar asked Feb 18 '26 21:02

cojoj


1 Answers

I don't think what you're trying to achieve is possible due to the circular dependency between the respective associated types of the PresenterType and ViewControllerType protocols.

Consider for a moment if the suspect code above did compile ... how would you go about instantiating either the RootPresenter or RootViewController classes? Because both depend on one another, you'll end up with errors like the following:

Generic parameter error 1. Generic parameter error 2.

As you can see, the compiler can't fully resolve the generic parameters due to the associated types.

I think your best bet is to remove the associated type from one or both of the protocols. For example, removing the associated type from the PresenterType protocol and updating the RootPresenter class breaks the circular dependency and allows your code to compile normally.

protocol PresenterType: class {
    var viewController: UIViewController? { get set }

    func bind(viewController: UIViewController?)
}

final class RootPresenter: PresenterType {
    weak var viewController: UIViewController?

    func bind(viewController: UIViewController?) {
        self.viewController = viewController
    }
}
like image 161
ryanecrist Avatar answered Feb 20 '26 18:02

ryanecrist



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!