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

What 2018 has in store for Swift

Paul Hudson    March 26th 2018    @twostraws

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…

A simple way to get all enum cases

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:

  • Automatic synthesis will only take place for enums that do not use associated values; adding those wouldn’t make sense.
  • If you mark any cases as unavailable they will be excluded from the allCases array.
  • You need to add 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.

Easier boolean toggling

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.

Warning and error compiler directives

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

Removing collection elements efficiently

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!

Dynamic member look up

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.

And there’s more…

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.

 

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.

Click here to visit the Hacking with Swift store >>