A Google engineer is working to remove more drudgery from Swift.
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.
SAVE 50% To celebrate WWDC23, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.