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ă.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
What’s New in Swift 3.1?
15 mins
- Getting Started
- Language Improvements
- Failable Numeric Conversion Initializers
- New Sequence Functions
- Concrete Constrained Extensions
- Nested Generics
- Availability by Swift Version
- Convert Non-Escaping Closures to Escaping Ones
- Swift Package Manager Updates
- Editable Packages
- Version Pinning
- Other Bits
- Miscellaneous Bits and Pieces
- Multiple-Return Functions
- Disable Auto-Linking
- Where to Go From Here?
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
}
}
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.
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.
-
f
andg
come in as non-escaping and are converted toescapableF
andescapableG
. -
async(execute:)
calls require escaping closures. Fortunately you have those because of the previous step. - By running
sync(flags: .barrier)
, you ensure that theasync(execute:)
methods are completely done and the closures won't be called later on. - Scope limits the use of
escapableF
andescapableG
.
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.