Swift implement multiple protocols with a delegate
Asked Answered
A

3

6

I'm trying to implement a protocol that itself inherits multiple protocols that both have a delegate member. Is there a clean way to do this without needing different names for the delegate of each protocol?

protocol ProtocolOne {
    var delegate: ProtocolOneDelegate?
}

protocol ProtocolTwo {
    var delegate: ProtocolTwoDelegate?
}

protocol CombinedProtocol: ProtocolOne, ProtocolTwo {

}

protocol CombinedDelegate: ProtocolOneDelegate, ProtocolTwoDelegte {

}

class ProtocolImpl: CombinedProtocol {
    // How can I implement delegate here?
    // I've tried the following options without success:
    var delegate: CombinedDelegate?
    var delegate: protocol<ProtocolOneDelegate, ProtocolTwoDelegate>?
}
Alanealanine answered 23/11, 2014 at 3:3 Comment(2)
A delegate is a object, that conforms to a protocol. I cannot think of a reason why it has a delegate.Sluggard
@Sluggard I can see your point that a delegate belongs to an implementation rather than a protocol. For the moment I just removed the delegate properties from the protocols and only have the protocol declared in the implementation. If you want to post this as an answer I would be happy to accept it.Alanealanine
S
21

You should be able to combine them in one:

var delegate: (ProtocolOneDelegate & ProtocolTwoDelegate)?

You can now use both protocols.

Siegbahn answered 19/12, 2017 at 19:14 Comment(1)
Are there any compiler limitations where this will not work? I hasve a slightly more complex example where ProtocolImpl, in this case, act as a delegate itself, and redirects delegate property to self, and this doesn't compile. Here is an example. gist.github.com/danylokos/0f5498a3d0ee0034aa836017fe7d20c3Calista
T
2

In your code, delegate is just a normal property. You can have multiple protocols declaring a property with the same name and same type, and have a class directly or indirectly implement it.

If different protocols define a property with the same name but different type, you won't be able to make it compile, because the compiler will complain for redeclaration of a property and class not confirming to one of the protocols.

There are 2 possible solution. The most obvious one is to avoid using names having high probability of being used in other protocols - delegate is a typical case. Use a different naming convention, such as protocol1Delegate, dataSourceDelegate, apiCallDelegate, etc.

The 2nd solution consists of replacing properties with methods. For example:

protocol P1 {
    func test() -> String?
}

protocol P2 {
    func test() -> Int?
}

protocol P3: P1, P2 {

}

class Test : P3 {
    func test() ->  String? { return nil }
    func test() -> Int? { return nil }
}

Swift consider functions with the same parameters list but different return type as overloads. Note however that if 2 protocols use the same function signature (name, parameters and return type), when implementing in the class you will implement that function once - that might be the wanted behavior in some cases, but unwanted in other cases.

Trodden answered 23/11, 2014 at 9:58 Comment(1)
I really want to avoid having delegates with various names, I've done this is the past and it doesn't work out well. Also, I wasn't aware that you could overload the return on functions in Swift. However, I tried overloading the property and that didn't work. This means that even though the method name externally would be the same that internally I would still need two properties with different names to track them both.Alanealanine
H
2

A solution might be to use protocol extensions (check extension Combined). The benefit is that Combined only declares delegate and oneDelegate and twoDelegate are computed cross-implementation. Unfortunately, it's a requirement to have the three variables exposed out of the class, that might be inconvenient.

// MARK: - Delegates protocols

protocol OneDelegate {
    func oneDelegate(one: One)
}
protocol TwoDelegate {
    func twoDelegate(two: Two)
}
protocol CombinedDelegate: OneDelegate, TwoDelegate {
    func combinedDelegate(combined: Combined)
}


// MARK: - Model protocols

protocol One: class {
    var oneDelegate: OneDelegate? { get }
}

protocol Two: class {
    var twoDelegate: TwoDelegate? { get }
}

protocol Combined: One, Two {
    var delegate: CombinedDelegate? { get }
}

extension Combined {
    var oneDelegate: OneDelegate? {
        return delegate
    }
    var twoDelegate: TwoDelegate? {
        return delegate
    }
}


// MARK: - Implementations

class Delegate: CombinedDelegate {
    func oneDelegate(one: One) {
        print("oneDelegate")
    }

    func twoDelegate(two: Two) {
        print("twoDelegate")
    }

    func combinedDelegate(combined: Combined) {
        print("combinedDelegate")
    }
}

class CombinedImpl: Combined {
    var delegate: CombinedDelegate?

    func one() {
        delegate?.oneDelegate(self)
    }

    func two() {
        delegate?.twoDelegate(self)
    }

    func combined() {
        delegate?.combinedDelegate(self)
    }
}


// MARK: - Usage example

let delegate = Delegate()
let protocolImpl = CombinedImpl()
protocolImpl.delegate = delegate
protocolImpl.one()
protocolImpl.two()
protocolImpl.combined()
Howes answered 15/12, 2015 at 22:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.