Swift Protocol Implements Equatable
1) Allow two Cacheable
s of the same type to be compare
protocol Cacheable: Equatable {
//....//
func identifier() -> String
}
func ==<T : Cacheable>(lhs: T, rhs: T) -> Bool {
return lhs.identifier() == rhs.identifier()
}
Pros
This is the simplest solution.
Cons
You can only compare two Cacheable
objects of the same type. This means that code below will fail and in order to fix it you need to make Animal
conform to Cacheable
:
class Animal {
}
class Dog: Animal,Cacheable {
func identifier() -> String {
return "object"
}
}
class Cat: Animal,Cacheable {
func identifier() -> String {
return "object"
}
}
let a = Dog()
let b = Cat()
a == b //such comparison is not allowed
2) Allow Cacheable
s of any type to be compared
protocol Cacheable:Equatable {
//....//
func identifier() -> String
}
func ==<T:Cacheable>(lhs: T, rhs: T) -> Bool {
return lhs.identifier() == rhs.identifier()
}
func !=<T:Cacheable>(lhs: T, rhs: T) -> Bool {
return lhs.identifier() != rhs.identifier()
}
func ==<T:Cacheable, U:Cacheable>(lhs: T, rhs: U) -> Bool {
return lhs.identifier() == rhs.identifier()
}
func !=<T:Cacheable, U:Cacheable>(lhs: T, rhs: U) -> Bool {
return lhs.identifier() != rhs.identifier()
}
Pros
Removes limitations described above for solution 1. Now you can easily compare Dog
and Cat
.
Cons
- Implementation is longer. Actually I am not sure why specifying only
==
functions is not sufficient - this might be a bug with a compiler. Anyway, you have to provide the implementation for both==
and!=
. - In some cases the benefit of this implementation may also pose a problem as you are allowing the comparison between absolutely different objects and compiler is totally OK with it.
3) Without conforming to Equatable
protocol Cacheable {
//....//
func identifier() -> String
}
func ==(lhs: Cacheable, rhs: Cacheable) -> Bool {
return lhs.identifier() == rhs.identifier()
}
func !=(lhs: Cacheable, rhs: Cacheable) -> Bool {
return lhs.identifier() != rhs.identifier()
}
Pros
You can use Cacheable
as type without needing any generics. This introduces a whole new range of possibilities. For example:
let c:[Cacheable] = [Dog(),RaceCar()]
c[0] == c[1]
c[0] != c[1]
With solutions 1 and 2 such code would fail and you would have to use generics in your classes. However, with the latest implementation Cacheable
is treated as a type, so you are allowed to declare an array of type [Cacheable]
.
Cons
You no longer declare conformance to Equatable
so any functions which accept Equatable
parameters will not accept Cacheable
. Obviously, apart from ==
and !=
as we declared them for Cacheable
s.
If this is not a problem in your code I would actually prefer this solution. Being able to treat protocol as a type is super useful in many cases.
How to implement Equatable protocol for a protocol based on the identity of two instances that implement this protocol?
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.
Swift protocol forcing the Equatable protocol
If you're familiar with Java or C#, Swift protocols are about halfway between generics and interfaces. One thing that you can do in a protocol, for instance, is this:
protocol Foo {
func compareWith(foo: Self)
}
Classes that implement this protocol will have a method compareWith
that accept an object of their own type (and not an object of type Foo
).
This is what the compiler calls "Self or associated type requirements", and this is how Equatable is defined (it needs an operator==
that accepts two Self
operands). The downside of these protocols, though, is that you can only use them as generic constrains: you can't use them as an expression type.
The solution is to use generics. In this case, you'd make your ProtocolBuilder
protocol generic, with a constraint that the type implements NameProtocol
.
public protocol NameProtocol : Equatable {
var name: String { get }
}
public protocol BuilderProtocol {
typealias T: NameProtocol
func build() -> T?
init()
}
Make Swift protocol conform to Equatable on associated type
Add -> Bool
. Just a bad error message. Sometimes writing function declaration across multiple lines doesn't make it more readable.
public func ==<Node1 : GraphNode, Node2 : GraphNode where Node1.Content == Node2.Content>(lhs: Node1, rhs: Node2) -> Bool {
return (lhs.content == rhs.content)
}
How to properly implement the Equatable protocol in a class hierarchy?
After lots of research and some trial and error I finally came up with a working solution. The first step was moving the ==
operator from inside the class to the global scope. This fixed the errors about static
and final
.
For the base class this became:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
And for the subclass:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
return true
}
class Subclass : Base {
var y : String
}
Now the only part left is figuring out how to call the ==
operator of the base class from the ==
operator of the subclass. This led me to the final solution:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
That first if
statement results in a call to the ==
operator in the base class.
The final solution:
Base.swift:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
Subclass.swift:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
class Subclass : Base {
var y : String
}
Related Topics
What Makes a Property a Computed Property in Swift
How to Embed Third Party Framework on Ionic Capacitor Custom Plugin
Save & Retrieve Tableviewcell Checkmark Using Nsuserdefaults in Swift
Executing Text-To-Speech in Order
How to Read a Property List from Data in Swift 3
Suppressing Implicit Returns in Swift
Swift- Variable Not Initialized Before Use (But It's Not Used)
Swift: Programmatically Enumerate Outgoing Segues from a Uiviewcontroller
Wkwebview Auto Fill Login Form Swift 2
Flip Arfaceanchor from Left-Handed to Right-Handed Coordinate System
Nsdata Contentsofurl Constructor Returns Nil
Environmentobject VS Singleton in Swiftui
How to Reset Intent Extension Configurations in Widgetkit