New syntax, methods, compiler directives, and more!
Swift is not a language that likes to stay still, and even though Swift 4.1 still isn’t released extensive work is already underway to add new features for the following release.
In case you missed it, the new features in Swift 4.1 included synthesized Equatable
and Hashable
, conditional conformances, and a renamed compactMap()
method, along with a new key decoding strategy property that aims to make Codable
easier to work with.
But this time, Apple has gone further and specifically outlined a release process for Swift 4.2 as a stop-gap ahead of Swift 5.0. Some have speculated this might mean Swift 5.0 won’t be coming at WWDC, but given that 4.2 is due for final branching in April there’s still quite some time yet. (Note: Apple has previously announced major Swift versions at WWDC and shipped them as final releases in September.)
So, apart from ABI stability, what does 2018 have in store for Swift? Let’s take a look…
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
SE-0194 introduces a new CaseIterable
protocol that automatically generates an array property of all cases in an enum.
For example:
enum Direction: CaseIterable {
case north, south, east, west
}
for direction in Direction.allCases {
print(direction)
}
The allCases
property is a simple array of all values in your enum, so in the code above it is equivalent to [Direction]
.
There are three provisos here:
allCases
array.CaseIterable
to the original declaration of your enum rather than an extension.SE-0194 has been marked as implemented in Swift 4.2, so we should see this arriving soon.
SE-0199 introduces a new toggle()
method to booleans that flip them between true and false. This caused a lot of discussion in the Swift community, partly because some thought it too trivial for inclusion, but partly also because the Swift Forums discussion veered out of control at times.
The entire code to implement proposal is only a handful of lines of Swift:
extension Bool {
mutating func toggle() {
self = !self
}
}
However, the end result makes for much more natural Swift code:
var loggedIn = false
loggedIn.toggle()
As noted in the proposal, this is particularly useful in more complex data structures: myVar.prop1.prop2.enabled.toggle()
avoids the potential typing errors that could be caused using manual negation.
The proposal makes Swift easier and safer to write, and is purely additive, so it’s good to see that it has already been implemented ready for Swift 4.2.
Objective-C developers are used to the idea of adding #warning
and #error
directives to their code, forcing compilers to flag problems at build time. Well, Swift 4.2 will include those same directives in Swift too, allowing us to write code like this:
#warning Incomplete implementation, return the number of sections
Xcode actually includes that line if you create a new UITableViewController
subclass, although it’s commented out – clearly Xcode’s developers have wanted this feature as long as we have!
#warning
and #error
work alongside the existing #if
compiler directive, and will only be triggered if the condition being evaluated is true. For example:
#if os(macOS)
#error("MyLibrary is not supported on macOS.")
#endif
If you have a collection of items, how do you remove all elements that match a certain filter? You might start by writing something like this:
var pythons = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
noTerry = pythons.filter { !$0.hasPrefix("Terry") }
However, that doesn’t use memory very efficiently, and more advanced in-place solutions come with a range of complexities that are off-putting to novices.
SE-0197 introduces a new remove(where:)
method that seeks to solve this problem once and for all. You give it a closure condition to run, and it will perform a high-performance, in-place removal of all objects that fail the condition.
Ben Cohen, the author of SE-0197, gave a talk at dotSwift 2018 where he discussed the implementation of this proposal in more detail – if you’re keen to learn why it’s so efficient, you should start there!
SE-0195 introduces a way to bring Swift closer to scripting languages such as Python, but in a type-safe way.
I covered SE-0195 in a separate article – How to use Dynamic Member Lookup in Swift – but I’ll repeat the key parts just briefly.
We could create a Person struct that reads its values from a dictionary like this:
@dynamicMemberLookup
struct Person {
subscript(dynamicMember member: String) -> String {
let properties = ["name": "Taylor Swift", "city": "Nashville"]
return properties[member, default: ""]
}
}
The @dynamicMemberLookup
attribute requires the type to implement a subscript(dynamicMember:)
method to handle the actual work of dynamic member lookup. As you can see, I’ve written one that accepts the member name as string and returns a string, and internally it just looks up the member name in a dictionary and returns its value.
That struct allows us to write code like this:
let taylor = Person()
print(taylor.name)
print(taylor.city)
print(taylor.favoriteIceCream)
That will compile cleanly and run, even though name
, city
, and favoriteIceCream
do not exist as properties on the Person
type. Instead, they are all looked up at runtime: that code will print “Taylor Swift” and “Nashville” for the first two calls to print()
, then an empty string for the final one because our dictionary doesn’t store anything for favoriteIceCream
.
One proposal I’m keen to see accepted is SE-0202, which introduces random number generation and shuffling into Swift. In its most basic form it looks like this:
let randomInt = Int.random(in: 0 ..< 10)
That will generate a integer in the range 0 through 9, inclusive.
You can also use randomness to pull out a random item from a collection:
let pythons = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
let randomPython = pythons.random()
In that example, randomPython
will be an optional string because it’s possible the array could be empty.
Sequences are also gaining shuffle()
and shuffled()
methods to randomize the order of their elements, with the former being in-place:
let pythons = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
let shuffledPythons = pythons.shuffled()
Shuffling uses the Fisher-Yates algorithm, which ought to mean that Nate Cook can retire his long-lived Stack Overflow answer on this very topic.
Another hot topic right now is SE-0200, which introduces raw strings to Swift. The syntax for this is currently being bikeshedded to death over on the Swift Forums.
The principle here is nice and simple: when you have to use backslashes to escape things in strings it gets hard to read, and this is doubly true when dealing with regular expressions where backslashes must themselves be escaped.
Raw strings are strings that do not allow special characters such as backslash to have any meaning – they are simple string literals. So, string interpolation, new lines, and so on all do nothing.
SE-0200 was originally proposed using r"string goes here"
, but in discussion is moving more towards #raw("string goes here")
. It’s still up in the air whether this one will get accepted or not because reaction on the forums has been mixed – all being well we should know soon enough.
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.