Search code examples
iosswiftprotocolsswift4equatable

How to implement Equatable protocol for a protocol based on the identity of two instances that implement this protocol?


I am trying to implement the Equatable protocol for a protocol based on the left' and right's operand identity. I other words: How do I implement the Equatable protocol for a protocol to determine if two instances that implement this protocol (in my case iNetworkSubscriber) are identical (same object reference). Like that (error message is include in the code below):

protocol iNetworkSubscriber : Equatable {

    func onMessage(_ packet: NetworkPacket)

}

func ==(lhs: iNetworkSubscriber, rhs: iNetworkSubscriber) -> Bool {     // <- Protocol 'iNetworkSubscriber' can only be used as a generic constraint because it has Self or associated type requirements
    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)               // <- Cannot invoke initializer for type 'ObjectIdentifier' with an argument list of type '(iNetworkSubscriber)'
}

... and I also tried it with the identity operator itself:

func ==(lhs: iNetworkSubscriber, rhs: iNetworkSubscriber) -> Bool {     // <- Protocol 'iNetworkSubscriber' can only be used as a generic constraint because it has Self or associated type requirements
    return lhs === rhs                                                  // <- Binary operator '===' cannot be applied to two 'iNetworkSubscriber' operands
}

Somebody an idea how to solve this issue?


Solution

  • There are two problems here. The first is that you can't use ObjectIdentifier on value types. So you must declare this protocol to require reference (class) types:

    protocol NetworkSubscriber : class, Equatable {
        func onMessage(_ packet: NetworkPacket)
    }
    

    (Please do not add a lowercase i to the beginning of protocols. This is confusing in several ways in Swift.)

    Then, you cannot use this protocol as a type. It describes a type (because it relies on Self via Equatable). So functions that accept it must be generic.

    func ==<T: NetworkSubscriber>(lhs: T, rhs: T) -> Bool {
        return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
    }
    

    Given that NetworkSubscriber must be a class, you should ask very carefully whether you should be using inheritance here rather than a protocol. Protocols with associated types are very complex to use, and mixing classes and protocols creates even more complexity. Class inheritance is much simpler if you're already using classes.