· 3 min read

Comparing Objects in Swift Using the Equatable Protocol

How to improve object comparison in Swift using the Equatable protocol? See it in concrete examples.

How to improve object comparison in Swift using the Equatable protocol? See it in concrete examples.

Conforming to the Equatable protocol enables object comparison in Swift for equality or inequality using operators (==) and (!=). This allows us to perform specific operations depending on whether objects match or not.

Equatable is very easy to implement, and better yet, most basic types in Swift’s standard library already conform to it. However, when creating your own types, you’ll need to implement this protocol yourself. Similarly, we might encounter this problem when comparing objects from external libraries that haven’t implemented this protocol.

Let’s look at how we can solve these problems 👇

How to Ensure Equatable Conformance?

To make a type comparable, you just need to implement the Equatable protocol, like this:

struct Dog: Equatable {
    let id = UUID()
    let name: String
}

Now we can freely compare objects of this structure:

let dog1 = Dog(name: "Doggo")
let dog2 = Dog(name: "Carly")

// Result: false
print(dog1 == dog2) 

That’s all we need to do thanks to Swift compiler’s automatically synthesized conformance.

How does this work? 🤷‍♂️

Automatically Synthesized Conformance

With Swift 4.1, the compiler added the ability to synthesize Equatable and Hashable protocols, specifically here: SE-0185. Thanks to this, to compare two objects of the same type, it’s enough that each member of this type implements the Equatable protocol.

For example, the structure below conforms to this protocol because id (UUID) and name (String) are types that inherently support and conform to this protocol.

struct Dog: Equatable {
    let id = UUID()
    let name: String
}

But let’s consider another example 🤔

struct Address {
    let city: String
}

struct Person: Equatable {
    let id = UUID()
    let name: String
    let address: Address
}

Here we have a problem because while the Person structure implements the Equatable protocol, Address doesn’t, and then Xcode will give us an error saying:

Type ‘Person’ does not conform to protocol ‘Equatable’

To fix this error, the Address structure must also implement this protocol.

External Libraries

What if we don’t have direct access to the structure or class code? For example, when using types provided by external libraries?

That’s where extensions come to the rescue. Let’s say we want to extend some structure with Equatable. All we need to write is:

// This is some type provided by an external library
extension StructFrom3rdPartyLibrary: Equatable { }

Custom Comparison Logic

What if we want to compare objects in a specific way?

struct Phone: Equatable {
    let id = UUID()
    let model: String
    let vendor: String
}

Let’s say we wanted to compare phone objects solely based on the manufacturer, in this case the vendor field.

let phone1 = Phone(model: "Galaxy S10", vendor: "Samsung")
let phone2 = Phone(model: "Galaxy S20", vendor: "Samsung")

We can write our own implementation of the comparison function to achieve the desired effect:

extension Phone: Equatable {
    static func == (lhs: Phone, rhs: Phone) -> Bool {
        lhs.vendor == rhs.vendor
    }
}

// Result: true
print(phone1 == phone2)

What About Enums?

No worries, enums are automatically synthesized, so we don’t have to worry about anything. Well, as long as all their values and properties are comparable, which is worth remembering.

Summary

The takeaway? If you want to compare objects of the same type, apply the Equatable protocol as shown here. You can rely on the default implementation of this protocol or write your own object comparison logic.

Share:
Back to Blog

Related articles

View all posts »