New features in Swift 4.2
Yesterday, Apple officially released the latest version of Swift programming language - Swift 4.2. It is a major release with a lot of improvements that include faster build times and features aimed towards removing boilerplate code and improving efficiency. It is also a significant step towards ABI stability planned for Swift 5.
There were many proposals that were accepted in this release. In this blog post, I will go through the ones that I find interesting.
Hashable enhancements
To be able to use a custom type in Set
or as a key in Dictionary
, it has to conform to Hashable
protocol. In Swift 4.1, Hashable
protocol looked like this:
Each custom type that implements Hashable
has to provide a hashValue
. Calculating the hashValue
is not as simple as it seems. If the algorithm is not good enough, the cost is pretty high, because it impacts the performance of Dictionary
and Set
. That is why Hashable
protocol was redesigned in Swift 4.2:
To implement the new Hashable
protocol, we have to call the hash function on the conforming type’s properties that we want to use in hash calculation and pass in the hasher as an argument. As an example, we can implement it on Person
struct:
Note that there is also a generic Hasher.combine<H>(_:)
method which is completely equivalent to Hasher.hash(into:)
method and can be used in the same way.
Diagnostic directives - #warning and #error
An interesting feature implemented in this release are #warning
and #error
compiler diagnostic directives. As their names already say, #warning
will cause Xcode to emit a warning when building code and #error
will cause a compile error.
#warning
is very useful for marking code that is unfinished and it is a much more robust solution than marking the code with FIXME
or TODO
.
#error
can be used, for example, to notify the user that a library is not available on certain platforms:
CaseIterable protocol
Before Swift 4.2, there was no standard way of listing all cases in enums. What one could do is create an allCases
static property that returns a collection of all enum cases:
This solution, however, is not optimal, because if we later add another case in the enum we have to add that case to allCases
property.
This is where CaseIterable
protocol comes into play. Our enum only has to conform to the protocol and the allCases
property will be generated for us at compile time. Cases are ordered in order of their declaration.
Note that compiler synthesis only works for enums without associated values.
Upgrading self from a weak to strong reference
One common pattern when working with closures in Swift is capturing self
weakly to avoid retain cycles. The only mechanism for upgrading weak self
to a strong reference is to unwrap it using if let
or guard let
statements. The problem is that we have to use a variable with an arbitrary name like strongSelf
because we can’t use self
for variable’s name. This introduces a lot of inconsistencies in the codebase.
Now, as of Swift 4.2, we can use self
for naming the variable used for unwrapping like this:
Conclusion
I’ve covered some of the features that were introduced in Swift 4.2 that were interesting to me. There are a lot of changes that I didn’t cover. You can read more about them on Apple’s official Swift blog.