Mastering Swift's Development Blog

Follow Us On Twitter
  • Jon Hoffman

Comparable Protocol: Protocol Oriented Design within Swift


In a previous post we looked at how the Equatable protocol is used. We also noted that this protocol is a good example of how Swift uses a protocol-oriented design for the language itself. In this weeks post we are going to take a look at another, similar, protocol that is built into the Swift language and how we can use it. This protocol is the Comparable protocol.


Where the Equatable protocol enables us to compare two instances of a type using the equal to (==) and not equal too (!=) operators, the Comparable protocol enables us to compare instance of a type using the greater than (>), less than (<), greater than or equals to (>=) or the less than or equals to (<=) operators. Just like the Equatable protocol we can enable this comparison within our custom type because this capacity is defined in the type system, using protocols.


Let’s say that we had the same Person type that we defined in the previous post, which looks like this:

struct Person {
    var firstName: String
    var lastName: String
    var age: Int
}

In order to make our Person type adopt the Comparable protocol, we need to implement a static function for the less than (<) operator. We can add this function to the Person type itself but we usually prefer to add it with an extension like this:

extension Person: Comparable {
    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.lastName < rhs.lastName
    }
}

With this extension, when we compare two instances of the Person type, we are only comparing the last name. If we wanted to compare the first name, if the last names were the same, our extension would look like this.

extension Person: Comparable {
    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.lastName < rhs.lastName ||
        (lhs.lastName == rhs.lastName && lhs.firstName < rhs.firstName)
    }
}

Notice that we first compare the last names only. We then see if the last names are the same, and then compare the first names. We separate these two comparisons with the logical or operator.


Now lets create several instances of the Person type like this:

let person1 = Person(firstName: "Jon", lastName: "Hoffman", age: 54)
let person2 = Person(firstName: "Pedro", lastName: "Martinez", age: 50)
let person3 = Person(firstName: "Larry", lastName: "Bird", age: 65)
let person4 = Person(firstName: "Dan", lastName: "Marino", age: 61)

With the Person type adopting the comparable protocol, we can compare two instances like this:

if (person1 <= person2) {
    print("Less Than")
} else {
    print("Greater Than")
}

When we run this code we will see that the “Less Than” message is printed to the screen because the last name “Hoffman” is considered less than the last name “Martinez” in the alphabetic order. That is quite useful but what is really useful is when we want to sort or order several instances of the Person protocol. Lets see how this would work but looking at the following code:

let persons = [person1, person2, person3, person4]
let sortedPersons = persons.sorted()
for person in sortedPersons {
    print(person)
}

In this code we start off by putting all the previous instances of the Person type into an array. We then use the sorted() method of the array to sort this instances and finally print out the sorted list. The results look like this:

Person(firstName: "Larry", lastName: "Bird", age: 65)
Person(firstName: "Jon", lastName: "Hoffman", age: 54)
Person(firstName: "Dan", lastName: "Marino", age: 61)
Person(firstName: "Pedro", lastName: "Martinez", age: 50)

As we can see the sortedPersons array is sorted in alphabetic order. Being able to use the comparison operators by adopting the Comparable protocol shows how powerful a good protocol-oriented design can be even if a simple comparison was all we could do but as we saw it also enables other functionality.


I mentioned this at the end of the last post but it is worth mentioning again. The functionality that we automatically gain by adopting a protocol such as the Comparable protocol is the results of a good protocol-oriented design within the Swift language itself. By having functionality tied to protocols, rather than types or functions, then any type that adopts a protocol will gain that functionality. We should keep this in mind when we are designing our own APIs, frameworks and applications.

masteringSwift.jpeg

Mastering Swift 5.3

The sixth edition of this bestselling book, updated to cover through version 5.3 of the Swift programming language

Amazon

Packt

pop.jpeg

Protocol Oriented Programming

Embrace the Protocol-Oriented design paradigm, for better code maintainability and increased performance, with Swift.

Amazon

Packt