Make Your Types Equatable
The first solution for the archetypical problem is also a really simple
one. Instead of enforcing Equatable
on your custom protocol
, you can
simply require your full fledged, final, types to conform to the
Equatable
protocol instead of your custom protocol. Consider the
previously defined Bookmarkable
protocol:
protocol Bookmarkable {
}
struct Bookmark: Bookmarkable, Equatable {
var identifier: Int
}
func ==(lhs: Bookmark, rhs: Bookmark) -> Bool {
return lhs.identifier == rhs.identifier
}
var myBookmarks: [Bookmark] = []
In the example above, the Equatable
requirement actually stems from
the Bookmark
type conforming to the Equatable
protocol, not the
Bookmarkable
protocol itself. The actual Equatable
information,
however, lies in the new identifier
property, which has been added to
the Bookmark
struct
. As you can easily see, this also requires you
to make the myBookmarks
array require only elements of type
Bookmark
. A serious disgression if you're used to using protocols
like partially anonymous types. A better solution, if your design allows
for it, goes one step further by enforcing the new property
which we
introduced in this example.
Equatable Properties
Here, the idea is that we take one of the types that already implement
Equatable
in a proper way (i.e. Int
, String
, ...) and add a new
property
requirement to our Bookmarkable
protocol. Then, we can use
this property
to add Equatable
support without actually implementing
Equatable
:
protocol Bookmarkable {
var identifier: Int { get }
}
struct Bookmark: Bookmarkable {
var identifier: Int
}
var myBookmarks: [Bookmarkable] = []
The main change, compared to the code above, is that the
var identifier
moved to the Bookmarkable
protocol and that we
removed the func ==
.
While this works better, it still has a major deficit. Since
Bookmarkable
does not directly comply with Equatable
, you will not
gain the standard library's methods that specifically deal with
Equatable
types. So instead of being able to call Array.contains
like this:
let ourBookmark = Bookmark(identifier: 0)
let result = myBookmarks.contains(ourBookmark)
You will have to use the more verbose closure-based version:
let ourBookmark = Bookmark(identifier: 0)
let result = myBookmarks.contains { (bookmark) -> Bool in
return bookmark.identifier == ourBookmark.identifier
}