Conforms to 2 protocols at the same time (where each protocol has a variable to other protocol inside)

Hi,

In VIPER pattern, we have the Presenter, and Router, etc...

I would like to have one object to conform to 2 PresenterProtocols where each PresenterProtocol defines a variable of type of RouterProtocol.

I have tried various forms but cannot get this to compile. Is this even possible?

import Foundation

// ==================================================
// A
protocol APresenterProtocol: AnyObject {
    // strong
    var router: ARouterProtocol { get }
}
protocol ARouterProtocol: AnyObject {
    // weak
    var parent: APresenterProtocol? { get set }
}
// ==================================================
// B
protocol BPresenterProtocol: AnyObject {
    // strong
    var router: BRouterProtocol { get  }
}
protocol BRouterProtocol: AnyObject {
    // weak
    var parent: BPresenterProtocol? { get set }
}

// ==================================================
// Aliases and extra protocols I added to *glue* things without success
// Ideally I would like NOT to need to re-define things like these (They didn't work so far anyways)

typealias ABPresenterProtocolAlias = APresenterProtocol & BPresenterProtocol
typealias ABRouterProtocolAlias = ARouterProtocol & BRouterProtocol

protocol ABProtocol: APresenterProtocol, BPresenterProtocol {
    var router: ABRouterProtocolAlias { get }
}

protocol ABRouterProtocol: ARouterProtocol, BRouterProtocol {
    var parent: ABPresenterProtocolAlias? { get set }
}

// ==================================================
// I want to have:
// 1 presenter class that conforms to both A and B presenter protocols
// 1 router class that conforms to both A and B router protocols
// router instance will be used inside presenter

class RealABRouter: ABRouterProtocol {
    var parent: ABPresenterProtocolAlias?
}

class RealABPresenter: ABProtocol {
    var router: ABRouterProtocol

    init(router: ABRouterProtocol) {
        self.router = router
    }
}

I tried to compile this but I cannot fix errors like below. :frowning: I run out of ideas. If someone can help me it would be greatly appreciated.

Protocols.swift:44:7: error: type 'RealABRouter' does not conform to protocol 'ARouterProtocol'
class RealABRouter: ABRouterProtocol {
^

Protocols.swift:45:9: note: candidate has non-matching type 'ABPresenterProtocolAlias?' (aka 'Optional<APresenterProtocol & BPresenterProtocol>')
var parent: ABPresenterProtocolAlias?
^

Protocols.swift:44:7: error: type 'RealABRouter' does not conform to protocol 'BRouterProtocol'
class RealABRouter: ABRouterProtocol {
^

Protocols.swift:45:9: note: candidate has non-matching type 'ABPresenterProtocolAlias?' (aka 'Optional<APresenterProtocol & BPresenterProtocol>')
var parent: ABPresenterProtocolAlias?
^

Protocols.swift:11:9: note: protocol requires property 'parent' with type 'APresenterProtocol?'; do you want to add a stub?
var parent: APresenterProtocol? { get set }
^

Protocols.swift:48:7: error: type 'RealABPresenter' does not conform to protocol 'ABProtocol'
class RealABPresenter: ABProtocol {
^

Protocols.swift:49:9: note: candidate has non-matching type 'ABRouterProtocol'
var router: ABRouterProtocol
^

Protocols.swift:48:7: error: type 'RealABPresenter' does not conform to protocol 'APresenterProtocol'
class RealABPresenter: ABProtocol {
^

Protocols.swift:49:9: note: candidate has non-matching type 'ABRouterProtocol'
var router: ABRouterProtocol
^

Protocols.swift:48:7: error: type 'RealABPresenter' does not conform to protocol 'BPresenterProtocol'
class RealABPresenter: ABProtocol {
^

Protocols.swift:49:9: note: candidate has non-matching type 'ABRouterProtocol'
var router: ABRouterProtocol
^

Protocols.swift:32:9: note: protocol requires property 'router' with type 'ABRouterProtocolAlias' (aka 'ARouterProtocol & BRouterProtocol'); do you want to add a stub?
var router: ABRouterProtocolAlias { get }
^

Protocols.swift:60:8: error: value of type 'RealABRouter' has no member 'presenter'
router.presenter = presenter
~~~~~~ ^~~~~~~~~

Thanks in advance

Nacho

Although you cannot simply declare two properties with identical name inside a type (like you can do method overloading easily), you can cheat with extensions containing computed properties (afaik β€” never tried it ;-).
So in your type, you would define two properties (aRouter, bRouter), and then add two protocol conformances which return the correct instance variable for each conformance.

1 Like

@Tino Thank you for the suggestion. I have tried and it works!. (I have to modify my protocols a bit but it should be no problem) Here is the code that compiles :slightly_smiling_face:

// A and B Presenter and Router Protocols =====================
protocol APresenterProtocol: AnyObject {
    var aRouter: ARouterProtocol { get }
}
protocol ARouterProtocol: AnyObject {
    var aPresenter: APresenterProtocol? { get }
}
protocol BPresenterProtocol: AnyObject {
    var bRouter: BRouterProtocol { get }
}
protocol BRouterProtocol: AnyObject {
    var bPresenter: BPresenterProtocol? { get }
}
// Real classes ===============================================
class RealABRouter: ARouterProtocol, BRouterProtocol {
    var aPresenter: APresenterProtocol? { presenter }
    var bPresenter: BPresenterProtocol? { presenter }
    weak var presenter: (APresenterProtocol & BPresenterProtocol)!
}
class RealABPresenter: APresenterProtocol, BPresenterProtocol {
    var aRouter: ARouterProtocol { router }
    var bRouter: BRouterProtocol { router }
    let router: (ARouterProtocol & BRouterProtocol)
    init(router: ARouterProtocol & BRouterProtocol) { self.router = router }
}
func test() {
    let router = RealABRouter()
    let presenter = RealABPresenter(router: router)
    router.presenter = presenter
}

still, I have one doubt,

Although you cannot simply declare two properties with identical name inside a type (like you can do method overloading easily),

I get the point. At the same time, I thought the compiler would realize that the variable with identical name (for example router) is OK. Yes this is not exactly the same type declared as in APresenterProtocol or BPresenterProtocol, however is a type that fulfills both requirements.

I wonder why the compiler does not realize this?. :thinking:

Do you know if this is a bug or non-implemented behaviour yet ? or this is not the way it is supposed to work?

class RealABPresenter: APresenterProtocol & BPresenterProtocol {

    //  Yes, `router ` is not exactly the same type declared as in 
    //  APresenterProtocol or BPresenterProtocol, however is a type that 
    //  fulfills both requirements. Why the compiler does not realize this?
    var router: ARouterProtocol & BRouterProtocol 

    init(router: ARouterProtocol & BRouterProtocol) {
        self.router = router
    }
}

You must fulfill the requirements of the protocol with the exact type that’s required:

protocol Q { }
protocol QQ: Q { }

protocol P { var q: Q { get } }
struct S: P { var q: QQ } // error: type β€˜S’ does not conform to protocol β€˜P’

@xwu Thank you for pointing out this. I think I understand a bit better now.