What’s New in Swift 3.1?

Great news: Xcode 8.3 and Swift 3.1 is now out of beta! This article highlights the most significant changes in Swift 3.1. By Cosmin Pupăză.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Nested Generics

Swift 3.1 allows you to mix nested types with generics. As an exercise, consider this (not too crazy) example. Whenever a certain team lead at raywenderlich.com wants to publish a post on the blog, he assigns a team of dedicated developers to work on it so that it meets the website’s high quality standards:

class Team<T> {
  enum TeamType {
    case swift
    case iOS
    case macOS
  }
  
  class BlogPost<T> {
    enum BlogPostType {
      case tutorial
      case article
    }
    
    let title: T
    let type: BlogPostType
    let category: TeamType
    let publishDate: Date
    
    init(title: T, type: BlogPostType, category: TeamType, publishDate: Date) {
      self.title = title
      self.type = type
      self.category = category
      self.publishDate = publishDate
    }
  }
  
  let type: TeamType
  let author: T
  let teamLead: T
  let blogPost: BlogPost<T>
  
  init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost<T>) {
    self.type = type
    self.author = author
    self.teamLead = teamLead
    self.blogPost = blogPost
  }
}

You nest the BlogPost inner class within its corresponding Team outer class and make both classes generic. This is how the teams look for the tutorials and articles I’ve published on the website so far:

Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix", 
     blogPost: Team.BlogPost(title: "Pattern Matching", type: .tutorial, 
     category: .swift, publishDate: Date()))

Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix", 
     blogPost: Team.BlogPost(title: "What's New in Swift 3.1?", type: .article, 
     category: .swift, publishDate: Date()))

But actually, in this case you can simplify that code a bit more. If the nested inner type uses the generic outer one, it inherits the parent’s type by default. Therefore you don’t have to declare it, like so:

class Team<T> {
  // original code 
  
  class BlogPost {
    // original code
  }  
  
  // original code 
  let blogPost: BlogPost
  
  init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost) {
    // original code   
  }
}
Note: If you want to learn more about generics in Swift, read our recently updated tutorial on getting started with Swift generics.

Availability by Swift Version

You may check for a specific Swift version with the #if swift(>= N) static construct like this:

// Swift 3.0
#if swift(>=3.1)
  func intVersion(number: Double) -> Int? {
    return Int(exactly: number)
  }
#elseif swift(>=3.0)
  func intVersion(number: Double) -> Int {
    return Int(number)
  }
#endif

However, this approach has one major drawback when used in something like the Swift standard library. It requires compiling the standard library for each and every supported old language version. That's because when you run the Swift compiler in backwards compatibility mode, to say you want Swift 3.0 behaviour for example, it would need to use a version of the standard library compiled for that specific compatibility version. If you used one compiled with in version 3.1 mode, you simply wouldn't have the right code there.

So, Swift 3.1 extends the @available attribute to support specifying Swift version numbers in addition to its existing platform versions [SE-0141]:

// Swift 3.1

@available(swift 3.1)
func intVersion(number: Double) -> Int? {
  return Int(exactly: number)
}

@available(swift, introduced: 3.0, obsoleted: 3.1)
func intVersion(number: Double) -> Int {
  return Int(number)
}

This new feature gives the same behaviour in terms of which intVersion method is available under which Swift version. However it allows libraries like the standard library to be compiled only once. The compiler then simply picks the features that are available for the given compatibility version selected.

Note: If you want to learn more about the availability attributes in Swift, check out our tutorial on availability attributes in Swift.

Convert Non-Escaping Closures to Escaping Ones

Closure arguments to functions were made non-escaping by default in Swift 3.0 [SE-0103]. However, part of that proposal was not implemented at the time. In Swift 3.1, you can convert non-escaping closures to escaping ones temporarily by using the new withoutActuallyEscaping() helper function.

Why would you ever want to do this? It's probably not often but consider the example from the proposal.

func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void,
             on queue: DispatchQueue) {
  withoutActuallyEscaping(f) { escapableF in     // 1
    withoutActuallyEscaping(g) { escapableG in
      queue.async(execute: escapableF)           // 2
      queue.async(execute: escapableG)     

      queue.sync(flags: .barrier) {}             // 3
    }                                            // 4
  }
}

This function runs two closures simultaneously and then returns after both are done.

  1. f and g come in as non-escaping and are converted to escapableF and escapableG.
  2. async(execute:) calls require escaping closures. Fortunately you have those because of the previous step.
  3. By running sync(flags: .barrier), you ensure that the async(execute:) methods are completely done and the closures won't be called later on.
  4. Scope limits the use of escapableF and escapableG.

If you squirrel either temporary escaping closures away (i.e. actually escaped them) it would be a bug. Future versions of the Standard Library may be able to detect this and trap if you tried to call them.

Swift Package Manager Updates

Ah, the long-awaited updates to the Swift Package Manager have arrived!

Editable Packages

Swift 3.1 adds the concept of editable packages to the Swift Package Manager [SE-0082].

The swift package edit command takes an existing package and converts it to an editable one. The editable package replaces all of the canonical package’s occurrences in the dependency graph. Use the --end-edit command to revert the package manager back to the canonical resolved package.

Version Pinning

Swift 3.1 adds the concept of version pinning package dependencies to specific versions to the Swift Package Manager [SE-0145]. The pin command pins one or all dependencies like this:

$ swift package pin --all      // pins all the dependencies
$ swift package pin Foo        // pins Foo at current resolved version
$ swift package pin Foo --version 1.2.3  // pins Foo at 1.2.3

Use the unpin command to revert to the previous package version:

$ swift package unpin —all
$ swift package unpin Foo

The package manager stores the active version pin information for each package in Package.pins. If the file doesn’t exist, the package manager creates it automatically based on the requirements specified in the package manifest, as part of the automatic pinning process.

Cosmin Pupăză

Contributors

Cosmin Pupăză

Author

Chris Belanger

Editor

Matt Galloway

Final Pass Editor

Ray Fix

Team Lead

Over 300 content creators. Join our team.