Synthesized conditional conformances in Swift 4.2
Discover page available: GenericsDespite being a minor release, Swift 4.2 contains a lot of really useful features that'll help us remove boilerplate and write more robust and dynamic code. While we'll dive much deeper into many of those new features in upcoming articles on the main weekly blog, in today's WWDC update, let's take a look at a nice improvement to conditional conformances.
Swift 4.1 introduced the ability for types to declare conformance to a protocol only under certain conditions, with a feature called Conditional Conformances. Another feature that was introduced in Swift 4.1 is that the compiler will now automatically synthesize implementations of the Equatable
and Hashable
protocols, given that all stored members of a type also conform to the same protocol - which helps remove a ton of boilerplate and error-prone manual implementations.
Let's take a look at how Swift 4.2 combines those two features to enable synthesis of conditional conformances to Equatable
and Hashable
.
The Result type
In Swift it's very common to use an enum to declare a Result
type - a type that can either contain a value or an error, being the result of some form of asynchronous operation. While some developers prefer to call these types Either
or Outcome
, the gist is the same, they usually look something like this:
enum Result<Value, Error: Swift.Error> {
case value(Value)
case error(Error)
}
As you can see above, our result type has two generic types; Value
and Error
. Making Result
a generic this way enables us to use it in many different contexts while still retaining full type safety for both the value type and the error type.
Conditionally synthesized
With the introduction of conditional conformances, we can conditionally make Result
conform to Equatable
, given that its generic types - Value
and Error
- also conform to that same protocol:
extension Result: Equatable
where Value: Equatable, Error: Equatable {}
That's really useful, especially since it wouldn't make sense to make all Result
variants equatable, or force the Value
type to always have to conform to Equatable
- which were our only two options prior to Swift 4.1.
However, in Swift 4.1, we'd have to implement Equatable
ourselves when using this technique. While that may not seem like a big deal, once you get used to the compiler doing the work of creating Equatable
implementations for you, going back to doing them manually seems a bit backwards.
Thankfully, in Swift 4.2, the compiler will conditionally synthesize the implementation we need, and only for the constraints that we specified. The cool thing is that this also works for Hashable
. All we have to do is add a conditional conformance, and the compiler takes care of the rest:
extension Result: Hashable
where Value: Hashable, Error: Hashable {}
Pretty sweet! 🍭
Great for testing
One big benefit of having an equatable result type is that it makes it much easier to verify that a result is correct in our tests. For example, here we've loaded an article result, and are able to simply use XCTAssertEqual
to verify that the loaded result matches our expectation:
let expectedArticle = Article(title: "Title", text: "Text")
XCTAssertEqual(loadedResult, .value(expectedArticle))
Especially if we don't need our result type to be equatable in our production code, having it be automatically synthesized by the compiler makes our tests less error prone and quicker to write - big win!
Conclusion
The generic system in Swift keeps getting more and more powerful, and features like synthesis of conditional conformances to Equatable
and Hashable
are small but important improvements to how generics can make some of our types more useful.
For a more complete overview of what's new in Swift 4.2, I recommend checking out Ted Kremenek and Slava Pestov's excellent WWDC session "What's new in Swift".
What do you think? Are you excited about Swift 4.2 and the improvements it brings to things like the generics system? Let me know - along with your questions, comments and feedback - on Twitter @johnsundell.
Thanks for reading! 🚀