NEW! Master Swift design patterns with my latest book! >>

I tried SE-0185 and it’s a thing of beauty

Paul Hudson       @twostraws

Even though Swift is for the most part a clean, modern language (let’s just pretend @objc doesn’t exist, OK?) it still has a few areas that are unnecessarily clunky.

For example, before Swift 4 came along it used to be mind-numbingly dull making your data types conform to NSCoding – it always meant writing near-identical boilerplate code to read and write all the properties you wanted to store.

Thanks to Swift 4’s Codable protocol that pain point has gone away, but work is underway to solve another annoyance: the need to write functions to make trivial data types conform to Equatable and Hashable.

To demonstrate the problem, consider this struct:

struct Person: {
    var firstName: String
    var lastName: String
    var age: Int
    var city: String
    var likesSinging: Bool
}

There’s nothing fancy there – a few strings, an integer, and a boolean. If we wanted to create a couple of instances of those structs, Swift provides us with a memberwise initializer so we can write code like this:

let taylor = Person(firstName: "Taylor", lastName: "Swift", age: 27, city: "Nashville", likesSinging: true)
let notTaylor = Person(firstName: "Taylor", lastName: "Swift", age: 27, city: "London", likesSinging: true)

The problem comes if we want to compare those two instances. They are almost the same, but not quite: one lives in London and the other in Nashville.

If we wanted to compare the two values to make sure they were different we would need to make Person conform to the Equatable protocol. In practice that means implementing the == function so that it returns true if two values match, which usually goes something like this:

struct Person: Equatable { 
    var firstName: String
    var lastName: String
    var age: Int
    var city: String
    var likesSinging: Bool

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.firstName == rhs.firstName
            && lhs.lastName == rhs.lastName
            && lhs.age == rhs.age
            && OH GOD I'VE LOST THE WILL TO LIVE
    }
}

Like I said: drudgery.

Well, there’s some good news: Tony Allevato from Google wrote a Swift proposal called SE-0185: Synthesizing Equatable and Hashable conformance, and it proposes exactly what its name suggests – if it’s possible for the compiler to automatically generate a complete implementation of the Hashable and Equatable protocols, then allow the user to opt into it.

Even better, Allevato submitted a large pull request that implemented his proposal in full, and it’s already undergoing review by the rest of the Swift team.

I was keen to see if SE-0185 lived up to my hopes, so I cloned Allevato’s Swift fork, activated his synthesize-equatable-hashable branch, then compiled Swift based on that branch.

The result? Well, it’s almost magic, quite frankly. With his change in place, we can make our Person struct conform to Equatable like this:

struct Person: Equatable {
    var firstName: String
    var lastName: String
    var age: Int
    var city: String
    var favoriteColor: String
}

Yes – all you need to do is add : Equatable, and after that the compiler takes care of the rest.

As you can see, this change is opt in – you need to state that your data type conforms to Equatable otherwise the compiler won’t synthesize the appropriate functions. This means by default no extra code will get added to your app unless you actually request it, which is a sensible move.

Of course, you’re a smart Swift developer, so you watched Doug Gregor’s Building Better Apps with Value Types in Swift talk at WWDC 15, where he emphasized the importance of Equatable:

When you're building a value type, it's extremely important that it conform to the Equatable protocol. Because every value type out there should be equatable. That means it has the == operator to do a comparison, but that operator has to behave in a sensible way.

Well, with SE-0185 you get to have all the benefits of Equatable (and Hashable too!), without having to go through all the grunt work – a huge improvement and a win all around. And if you decide you want a custom equality check, you can go ahead and write it because any user-defined == function will automatically override what the compiler would have generated.

Allevato’s pull request implementing SE-0185 remains in active review by the Swift team, but I’m hopeful it will be ready in time for Swift 4.1. Regardless of when it finally ships, I’m looking forward to ripping out hundreds if not thousands of lines of boilerplate Equatable and Hashable code and thinking about it rarely if ever again.

 

MASTER SWIFT NOW
Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Practical iOS 11 Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

About the author

Paul Hudson is the creator of Hacking with Swift, the most comprehensive series of Swift books in the world. He's also the editor of Swift Developer News, the maintainer of the Swift Knowledge Base, and Mario Kart world champion. OK, so that last part isn't true. If you're curious you can learn more here.

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>