What's New in Swift 3 - Part 1

Curious about what’s new in Swift 3? In this first part of a three-post series, Daniel discusses Enums and Parameters.

You can find the other parts here:


Introduction

 

I am Daniel Steinberg, and welcome to What’s New in Swift 3! We are going to take a look at some of the proposals that have been accepted for Swift 3.

Swift was open-sourced on December 3, 2015. You can follow along on swift.org, releases are called out on the swift.org blog. You can also follow on the actual source code by going to github.com/apple/swift, and you can see the actual source code for Swift as it is developed in the open. In addition, on GitHub, you can find Swift Evolution and follow the Swift Evolution proposals to see what is coming and to participate in the discussion. There are already over 120 proposals; seven of them were shipped in Swift 2.2, and about 80 of them have been approved and will be a part of Swift 3. In August 2016, the discussion moved to prepare for Swift 4.

We are going to look at what is new in Swift 3. The goals were to stabilize the Swift Source post 3.0, so going forward you would not have to make many changes to your source code. Many of the improvements take a look at your code from the perspective of the Calls site. When you create these functions, methods, and these other Swift constructs, most of your time is going to be spent using them, not creating them; emphasis has been paid into the contract from the calls site.

As many have noted, the Swift 3 transition is painful. The attitude has been: let’s break it now, so we do not have to break it again. Many of these changes are breaking changes; they put off additive changes and syntactic sugar for future releases.

I have grouped the proposals into categories to help organize our thoughts a bit. The code examples that you see are taken from the actual proposals on GitHub, meaning that the specific syntax from the proposals might have changed from when they were accepted. Often in the rationale, there are some changes proposed. The language might be a bit different, and you might need to make slight modifications before applying them.

I have not included all of the accepted proposals in these videos. For example, many of them have to do with Tools, and for the Swift Package Manager, I have not explicitly explained those. The Swift 3 proposals that you will see in this series include:

  • Enums
  • Parameters
  • Functions and Closures
  • Collections
  • API Guidelines
  • Living the Guidelines
  • Clarity
  • Good Looking C

Enjoy!

Enums

 

Import Objective-C Constants as Swift types (Proposal 0033)

In Swift 3, several of the new proposals have to do with Enums. One of my favorites is this one, which specifies how to import Objective-C Constants as Swift Types:

HK_EXTERN NSString * const HKQuantityTypeIdentifierBodyMassIndex;
HK_EXTERN NSString * const HKQuantityTypeIdentifierBodyFatPercentage;
HK_EXTERN NSString * const HKQuantityTypeIdentifierHeight;
HK_EXTERN NSString * const HKQuantityTypeIdentifierBodyMass;
HK_EXTERN NSString * const HKQuantityTypeIdentifierLeanBodyMass;

Here we have a family of Objective-C Constants; they are all pointers to NSStrings. For years, Apple’s been advising us that if we have a family of related constants, we should give them a common prefix, so these HealthKit constants are all prefixed HKQuantityTypeIdentifier.

With this proposal, these are imported into Swift as an enum with the same name:

enum HKQuantityTypeIdentifier : String {
    case BodyMassIndex
    case BodyFatPercentage
    case Height
    case BodyMass
    case LeanBodyMass
}

This enum is backed by a String to correspond to the pointers to NSString. Individual types correspond to the end of each constant’s name (BodyMassIndex, BodyFatPercentage, etc.).

API Design Guidelines (Proposal 0023)

When we combine this with one of the recommendations from the API Design Guidelines, this needs to change slightly. Instead of beginning with an uppercase letter, each one of these now begins with a lowercase letter. All enum cases now begin with lowercase letters.

enum HKQuantityTypeIdentifier : String {
    case bodyMassIndex
    case bodyFatPercentage
    case height
    case bodyMass
    case leanBodyMass
}

Import Objective-C Constants as Swift types (Proposal 0033)

When we have a fixed collection of constants and we want to group those as a Swift Type we bring them in as an enum. But, sometimes we have a group of constants (like this one; see video) and you might want to be able to add to them:

FOUNDATION_EXPORT NSString * const NSCocoaErrorDomain;
FOUNDATION_EXPORT NSString * const NSPOSIXErrorDomain;
FOUNDATION_EXPORT NSString * const NSOSStatusErrorDomain;
FOUNDATION_EXPORT NSString * const NSMachErrorDomain;

We will bring them in as a Swift Type, in this case, a struct:

struct NSErrorDomain : RawRepresentable {
    typealias RawValue = String
    
    init(rawValue: RawValue)
    var rawValue: RawValue { get }
    
    static var Cocoa: NSErrorDomain { get }
    static var POSIX: NSErrorDomain { get }
    static var OSStatus: NSErrorDomain { get }
    static var Mach: NSErrorDomain { get }
}

Again we notice the common naming. These are all NSErrorDomain types. You do not tend to see this much in structs, but if an enum is backed by a String, or an Int, or some other type, it conforms to RawRepresentable. In this example we have a struct that conforms to RawRepresentable; we have to specify that the RawValue in this case is a String, and we have to give you a way of creating an element of this struct given a String.

With enums, the individual constants are brought in as cases; with structs they are brought in as static properties. We have Cocoa, POSIX, OSStatus, and Mach.

API Design Guidelines - Allow (most) keywords in member references (Proposal 0071)

I mentioned one of the changes with enums is that we are using lowercase letters to specify the individual cases. That can cause a problem if you have an API that used to use words that are fine with uppercase beginning letters, but turn into keywords when changed to lowercase letters:

enum FakeExample {
    case default
    case private
    case repeat
}

Proposal 0071 allows most keywords in member references so that we could have a case as default or private (or other keywords). Previously, we had to escape these types using a slash forward check (e.g. \default``). Now we can use them as they are without escaping them in any way.

Require Leading Dot Prefixes for Enum Instance Member Implementations (Proposal 0036)

This proposal takes care of an oddity that many people never noticed. Outside of an enum we always had to refer to the cases using a . before the case type. But inside of an enum instance, we did not have to. If we have a Coin enum with cases head and tails, within the enum instance itself (say inside of a switch statement), we could refer to them either with the leading “dot”, or without:

enum Coin {
    case heads, tails
    func printMe() {
        switch self {
        case heads: print("Heads")
        case .tails: print("Tails")
        }
        if self == .heads {}
        if self == tails {}
    }
    
    int() {
        let cointoss = arc4random_uniform(2) == 0
        self = cointoss ? .heads : tails
    }
}

You can see that we have both .heads and heads, and .tails and tails. Whether you use the “dot” or not, did not use to be enforced. For consistency, we will not be able to refer to the cases without using the leading .; these cases become .heads and .tails:

enum Coin {
    case heads, tails
    func printMe() {
        switch self {
        case .heads: print("Heads")
        case .tails: print("Tails")
        }
        if self == .heads {}
        if self == .tails {}
    }
    
    int() {
        let cointoss = arc4random_uniform(2) == 0
        self = cointoss ? .heads : .tails
    }
}

Declare variables in ‘case’ labels with multiple patterns (Proposal 0043)

Suppose we have an enum like this MyEnum, which has two different cases. In Case1 we have an Int and a Float; in Case2 we have Float and then an Int.

enum MyEnum {
    case Case1(Int, Float)
    case Case2(Float, Int)
}
switch value {
case let .Case1(x, 2), let .Case2(2, x):
    print(x)
case .Case1, .Case2:
    break
}

Notice in Case1 and Case2, we have these Int and these Float that might match. We would like to bind to the two Int, or bind to the two Float at once, depending on which case we are in. Previously it would take two different cases in a switch statement to accomplish this. Now we can bind them at the same time. In this case, we are binding to the Int; in Case1 we are binding x to the first element in the associated value, and in Case2 we are binding x to the second element in the associated value. We are doing this in a single case statement.

Similarly, we could bind two letters at once:

enum MyEnum {
    case Case1(Int, Float)
    case Case2(Float, Int)
}

case let .Case1(x, _), let .Case2(_, x):
case let .Case1(y, x), let .Case2(x, y):
case let .Case1(x), let .Case2(x):
case .Case1(let x, _), .Case2(_, let x):
}

y is bound to the Int in both Case1 and Case2, and x is bound to the Float in Case1 and Case2. We can also use x to bind to the tuple in both cases. Finally, we can move the let inside of the associated value in both cases, to explicitly bind x to the first element in Case1, and the second element in Case2.

Parameters

 

Remove explicit use of let from function parameters (Proposal 0053)

When you call a function or a method in Swift, the function makes an immutable copy of the parameters that you pass to it. What you may not have known was that you were able to use the let keyword to emphasize this.

func foo(let x: Int) {
    //...
}

Here the let keyword is redundant as this is the default behavior anyway. In Swift 3 you will not be allowed to use the let keyword anymore. This proposal removes the explicit use of let from function parameters. If x is an immutable copy, inside the function we cannot change x’s value by doing x += 1. Up until now, we could mark the parameter x as being a mutable copy by using var:

func foo(var x: Int) {
    x += 1
}

That allows you to change x locally within the function, but it does not change the x that is being passed in to you because it is still a mutable copy.

Up until now, you were able to label x as being inout, which is not creating a mutable copy; I actually want to change the x being passed to me.

func foo(inout x: Int) {
    x += 1
}

Adjusting inout declarations for type decoration (Proposal 0031)

In Swift 3, you will still be able to do this, but the position where inout goes is changing to decorating the type:

func foo(x: inout Int) {
    x += 1
}

Remove var from function parameters (Proposal 0003)

var is going away. You will no longer be able to decorate x as being var. If you need to mutate x within the body of the function, you are going to need to create a mutable copy of it:

func foo(x: Int) {
    var localX = x
    localX += 1
}

In this case, we have created localX as a mutable copy of x, and now we mutate localX. That makes it clear that x is not being changed, but the mutable copy is. It makes your code a little more verbose, but it also makes your intent completely clear.

Allow (most) keywords as argument labels (Proposal 0001)

The first ever proposal in the Swift evolution process was to allow most keywords as argument labels:

index(for: value, in: collection)

Even though for and in are keywords, we can use them here. You are going to find the use of prepositions very handy. I am going to create an index for something in some collection. If you look at this from the call side, this makes it very descriptive.

Establish consistent label behavior across all parameters including first labels (Proposal 0046)

The role of the first label has been very confusing since the beginning of Swift. The rules for functions and methods were different. The rules for init’s were different. Now all that is being normalized in Swift 3. There will be consistent label behavior across all the parameters, and this now includes the first label. For example, up until now if you had this function, you would have called it like this:

func foo(a: T, b: U, c: V) {
    // ...
}

foo(4, b: 3.2, c: "seven")

You would have specified the second and third labels b and c, but you would not have specified a anywhere.

Starting with Swift 3, you now are expected to specify a as well:

func foo(a: T, b: U, c: V) {
    // ...
}

foo(a: 4, b: 3.2, c: "seven")

For APIs where you deliberately do not want to use the first label at the call site, you can still use _ to suppress it:

func foo(_ a: T, b: U, c: V) {
    // ...
}

foo(4, b: 3.2, c: "seven")

And, as you would expect, you are allowed to create a different external label than the internal label for a parameter:

func foo(externalA a: T, b: U, c: V) {
    // ...
}

foo(externalA: 4, b: 3.2, c: "seven")

Enforcing order of defaulted parameters (Proposal 0060)

It has always been true that if you have a function with required arguments, you have to call them in the same order as they are listed in the function declaration. For example:

func requiredArguments(a: Int, b: Int, c: Int) {}

requiredArguments(a: 0, b: 1, c: 2)

You could not switch their order; that would be illegal. What you may not know is in Swift 2, if you had parameters that took default values, you could call them in the order in which they were given, but you were not required to:

func defaultArguments(a: Int = 0, b: Int = 0, c: Int = 0) {}

defaultArguments(a: 0, b: 1, c: 2)
defaultArguments(b: 0, a: 1, c: 2)

You can see I could switch the order of the b and the a because they both took default values. In Swift 3, this is going away: it is confusing, inconsistent. You will have to call the default arguments in the standard order. You are still able to drop out any of the arguments that you want along the way. You could call this with default arguments and just list a and c, if you wanted:

func defaultArguments(a: Int = 0, b: Int = 0, c: Int = 0) {}

defaultArguments(a: 0, c: 2)

Distinguish between single-tuple and multiple argument function types (Proposal 0110)

Here is another thing you may not have known from Swift 2:

let fn1 : (Int, Int) -> Void = { x in 
    // ...
}

let fn2 : (Int, Int) -> Void = { x, y in 
    // ...
}

In this case, we have two functions with what looks like identical signatures.

  • In the first, in the closure, we have x. x refers to the tuple of two Int.
  • In the second one, we have x, y - x refers to the first Int and y refers to the second Int.

Until now there was no way to distinguish between a single tuple and multiple arguments function types as long as those types followed the same order as the tuples.

Starting in Swift 3, in this first case, where x refers to the tuple, you are going to have to put another set of parentheses around that tuple to indicate that it is a tuple of two Int, and not just two different Int arguments:

let fn1 : ((Int, Int)) -> Void = { x in 
    // ...
}

let fn2 : (Int, Int) -> Void = { x, y in 
    // ...
}

Remove implicit tuple splat behavior from function applications (Proposal 0029)

Also in Swift 3, implicit tuples splat behavior has been removed from function applications.

Conclusion

This is part 1 of 3 on “What’s New with Swift 3”, so stay tuned for the next ones!

Resources

  • Proposal 0001 - Allow (most) keywords as argument labels
  • Proposal 0003 - Removing var from Function Parameters
  • Proposal 0023 - API Design Guidelines
  • Proposal 0029 - Remove implicit tuple splat behavior from function applications
  • Proposal 0031 - Adjusting inout Declarations for Type Decoration
  • Proposal 0033 - Import Objective-C Constants as Swift Types
  • Proposal 0036 - Requiring Leading Dot Prefixes for Enum Instance Member Implementations
  • Proposal 0043 - Declare variables in ‘case’ labels with multiple patterns
  • Proposal 0046 - Establish consistent label behavior across all parameters including first labels
  • Proposal 0053 - Remove explicit use of let from Function Parameters
  • Proposal 0060 - Enforcing order of defaulted parameters
  • Proposal 0071 - Allow (most) keywords in member references
  • Proposal 0110 - Distinguish between single-tuple and multiple-argument function types

Daniel Steinberg

Daniel Steinberg

Daniel is the author of the books 'A Swift Kickstart' and 'Developing iOS 7 Apps for iPad and iPhone', the official companion book to the popular iTunes U series from Stanford University. Daniel presents iPhone, Cocoa, and Swift training and consults through his company Dim Sum Thinking. He is also the host of the CocoaConf Podcast.