UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

What's new in Swift 3.1

Paul Hudson    January 26th 2017    @twostraws

Swift 3.1 is a small, backwards-compatible update to Swift 3.0, cleaning up a few advanced features in preparation for more serious changes coming when Swift 4.0 is announced in June. Although it took a lot of behind-the-scenes work to make these changes happen (see here and here if you'd like some detail), the end result for developers is a few extra features that we'll be able to start using soon enough.

In this article I'm going to explain three of the most useful changes with code examples, and hopefully this will give you some chance to be prepared to update your code when the time comes.

WARNING: Although it is not a feature of Swift 3.1, I couldn't publish this article without mentioning that Xcode 8.3 drops support for Swift 2.3. So, if you were holding back on your migration to Swift 3.x, now is the time to make the switch!

If you liked this article, you might also enjoy these:

Concrete constrained extensions

Swift lets us extend types using constraints, which is a powerful and expressive way to add functionality. To demonstrate this, let's look at a worked example in Swift 3.0 that modifies collections to do something trivial:

extension Collection where Iterator.Element: Comparable {
    func lessThanFirst() -> [Iterator.Element] {
        guard let first = self.first else { return [] }
        return self.filter { $0 < first }
    }
}

let items = [5, 6, 10, 4, 110, 3].lessThanFirst()
print(items)

That adds a new method called lessThanFirst(), which returns all items in a collection that are less than the first item. So, using it with the array [5, 6, 10, 4, 110, 3] will return [4, 3].

That code extends a protocol (Collection) only where it matches a constraint: elements in the collection must conform to another protocol, Comparable. This alone is powerful stuff, but let's take it back a step: what if we wanted something a bit more specific? Swift 3.0 lets us extend a concrete type rather than the protocol Collection, so instead we could write this:

extension Array where Element: Comparable {
    func lessThanFirst() -> [Element] {
        guard let first = self.first else { return [] }
        return self.filter { $0 < first }
    }
}

let items = [5, 6, 10, 4, 110, 3].lessThanFirst()
print(items)

That extends a concrete type (only Array) but still using a protocol for its constraint. What if we wanted to go even more specific – extend a concrete type with a concrete constraint, for example only arrays that contains integers? Well, it turns out that isn't possible in Swift 3.0, which usually strikes people as odd: if Swift 3.0 can handle extending protocols with another protocol as a constraint, then surely extending a specific type with a specific constraint should be a cinch?

Fortunately, this discrepancy has been removed in Swift 3.1, which means we can now write code like this:

extension Array where Element == Int {
    func lessThanFirst() -> [Int] {
        guard let first = self.first else { return [] }
        return self.filter { $0 < first }
    }
}

let items = [5, 6, 10, 4, 110, 3].lessThanFirst()
print(items)

That extends a concrete type (only Array) and uses a concrete constraint (only where the elements are Int).

Now, obviously we're using a trivial example here – in your own code this is going to be significantly more useful when you want to extend arrays containing your own custom structs.

Generics with nested types

Swift 3.0's support for nested types is useful to help you organize your data and increase encapsulation, but Swift 3.1 takes them to the next level by adding support for generics. Let's look at a simple example again, just to start with:

struct Message {
    struct Attachment {
        var contents: String
    }

    var title: String
    var attachment: Attachment
}

That creates a Message struct that has an Attachment struct inside it – a nested type. I've added two String properties, because messages will have some text and attachments will hold some text.

Now, what if we wanted either Message or Attachment to have different kinds of data – perhaps Int or Data? Well, that requires generics, so you might have found yourself writing something like this:

struct Message<T> {
    struct Attachment {
        var contents: String
    }

    var title: T
    var attachment: Attachment
}

That tells Swift we want Message to work across several data types, and whatever data type gets used to create the struct should also be used for the title property. Or at least that's what it would tell Swift, if such code were actually legal – Swift 3.0 does not allow you to mix nested type with generics. Fortunately, this is exactly what Swift 3.1 allows, because nested types can now appear inside generic types.

Not content to stop there, Swift 3.1 takes this a step further: nested types can also be generic, either using their own generic type or by inheriting the generic type of their parent. For example:

struct Message<T> {
    struct Attachment<T> {
        var contents: T
    }

    var title: T
    var attachment: Attachment<T>
}

With that code, the Message struct will have a specific type assigned to it, and the Attachment struct will always have the same type – you can't use String for one and Int for the other. So, this code will work fine:

let msg = Message(title: "Hello", attachment: Message.Attachment(contents: "World"))

Helpfully, if your goal is to make the nested type and its container use the same generic type, you don't even need to declare the nested type as generic – Swift makes the outer type available to the nested type, so in fact you can just write this:

struct Message<T> {
    struct Attachment {
        var contents: T
    }

    var title: T
    var attachment: Attachment
}

Generics are great and so are nested types, so I'm really pleased to see Swift 3.1 bring them together at last.

Sequences get prefix(while:) and drop(while:) methods

Two useful new methods have been added to the Sequence protocol: prefix(while:) and drop(while:). The former returns the longest subsequence that satisfies a predicate, which is a fancy way of saying that you give it a closure to run on every item, and it will go through all the elements in the sequence and return those that match the closure – but will stop as soon as it finds a non-matching element.

Let's take a look at a code example:

let names = ["Michael Jackson", "Michael Jordan", "Michael Caine", "Taylor Swift", "Adele Adkins", "Michael Douglas"]
let prefixed = names.prefix { $0.hasPrefix("Michael") }
print(prefixed)

That uses the hasPrefix() method to return the subsequence ["Michael Jackson", "Michael Jordan", "Michael Caine" – the first three elements in the sequence. It won't include "Michael Douglas", because that comes after the first non-Michael. If you wanted all the Michaels regardless of their position, you should use filter() instead.

The second new method, drop(while:) is effectively the opposite: it finds the longest subsequence that satisfies your predicate, then returns everything after it. For example:

let names = ["Michael Jackson", "Michael Jordan", "Michael Caine", "Taylor Swift", "Adele Adkins", "Michael Douglas"]
let dropped = names.drop { $0.hasPrefix("Michael") }
print(dropped)

That will return the subsequence ["Taylor Swift", "Adele Adkins", "Michael Douglas"] – everything after the initial Michaels.

Looking forward to Swift 4.0…

Swift 4.0 is almost certainly going to see the light of day in June, with a full release a few months afterwards. Although a major goal is to provide source stability for Swift 3 code, some breaking changes are almost inevitable.

If you want to take a peek into the future, you should read the String Processing For Swift document from two of Apple's Swift engineers – yes, code will break, but hopefully this will result in strings being significantly easier for developers to work with. If you worked through the string problems in my Swift Coding Challenges book, you'll know how tricky strings can be right now.

One feature we're all hoping for is true ABI compatibility, which would allow developers to distribute compiled libraries – one of the few key missing features that remain in Swift today. It would also reduce the size of every Swift app because iOS could store Swift's runtime libraries rather than them being distributed with every Swift app, so we get faster uploads to iTunes Connect, faster downloads to users, and slimmer apps all around. Hurray!

Want more like this?

If you thought this article was good, wait until you see my books:

  • Server-Side Swift: Become a full-stack Swift developer and write your own web back ends!
  • Hacking with macOS: get 18 complete projects that teach you all the major features of macOS
  • Hacking with Swift: over 1300 pages of hands-on iOS tutorials and tips for $30
  • Pro Swift: Get over six hours of advanced Swift tutorial videos and power up your skills!

I also have a low-volume Swift newsletter – click here to sign up now and I'll send a $5 discount on the Hacking with Swift e-book.

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.