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?
If you haven’t been following the Swift Evolution Process closely, keep reading – this article is for you!
In this article, I’ll highlight the most significant changes in Swift 3.1 which will have a major impact on your code. Let’s dive in! :]
Getting Started
Swift 3.1 is source-compatible with Swift 3.0, so the new features won’t break your code if you’ve already migrated your project to Swift 3.0 using Edit\Convert\To Current Swift Syntax… in Xcode. However, Apple has dropped support for Swift 2.3 in Xcode 8.3. So if you haven’t migrated from Swift 2.3 yet, now is the time to do so!
In the sections below, you’ll see linked tags such as [SE-0001]. These are Swift Evolution proposal numbers. I’ve included the link to each proposal so you can discover the full details of each particular change. I recommend you try out the features we discuss in a playground, so you have a better understanding of everything that changes.
So, fire up Xcode, select File\New\Playground…. Choose iOS as the platform, call it whatever you want, and save it wherever you want. While you’re reading this article, try out each feature in this playground.
Language Improvements
First, let’s take a look at the language improvements in this release, including failable initializers for numeric types, new sequence functions, and more.
Failable Numeric Conversion Initializers
Swift 3.1 implements failable initializers for all numeric types (Int
, Int8
, Int16
, Int32
, Int64
, UInt
, UInt8
, UInt16
, UInt32
, UInt64
, Float
, Float80
, Double
) which either complete successfully without loss of information or simply return nil
[SE-0080].
This feature is useful, for example, when dealing with loosely typed data conversions from external sources in a safe and recoverable manner. For example, this is how you might process a JSON array of students:
class Student {
let name: String
let grade: Int
init?(json: [String: Any]) {
guard let name = json["name"] as? String,
let gradeString = json["grade"] as? String,
let gradeDouble = Double(gradeString),
let grade = Int(exactly: gradeDouble) // <-- 3.1 feature here
else {
return nil
}
self.name = name
self.grade = grade
}
}
func makeStudents(with data: Data) -> [Student] {
guard let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),
let jsonArray = json as? [[String: Any]] else {
return []
}
return jsonArray.flatMap(Student.init)
}
let rawStudents = "[{\"name\":\"Ray\", \"grade\":\"5.0\"}, {\"name\":\"Matt\", \"grade\":\"6\"},
{\"name\":\"Chris\", \"grade\":\"6.33\"}, {\"name\":\"Cosmin\", \"grade\":\"7\"},
{\"name\":\"Steven\", \"grade\":\"7.5\"}]"
let data = rawStudents.data(using: .utf8)!
let students = makeStudents(with: data)
dump(students) // [(name: "Ray", grade: 5), (name: "Matt", grade: 6), (name: "Cosmin", grade: 7)]
You use a failable initializer to convert the grade
property from Double
to Int
inside the Student
class designated failable initializer like so:
let grade = Int(exactly: gradeDouble)
If gradeDouble
is a fractional value, such as 6.33
, it will fail. If it can be represented exactly with an Int
, such as 6.0
, it will succeed.
New Sequence Functions
Swift 3.1 adds two new functions for data filtering to the standard library’s Sequence
protocol: prefix(while:)
and drop(while:)
[SE-0045].
Consider the Fibonacci infinite sequence:
let fibonacci = sequence(state: (0, 1)) {
(state: inout (Int, Int)) -> Int? in
defer {state = (state.1, state.0 + state.1)}
return state.0
}
In Swift 3.0 you simply specified the iteration count to iterate through the fibonacci
sequence:
// Swift 3.0
for number in fibonacci.prefix(10) {
print(number) // 0 1 1 2 3 5 8 13 21 34
}
Swift 3.1 lets you use prefix(while:)
and drop(while:)
with a condition to get all elements of the sequence between two given values, like so:
// Swift 3.1
let interval = fibonacci.prefix(while: {$0 < 1000}).drop(while: {$0 < 100})
for element in interval {
print(element) // 144 233 377 610 987
}
prefix(while:)
returns the longest subsequence which satisfies a certain predicate. It starts at the beginning of the sequence and stops at the first element for which the given closure returns false.
drop(while:)
does the opposite: It returns the subsequence that begins with the first element for which the given closure returns false and finishes at the end of the sequence.
let interval = fibonacci.prefix{$0 < 1000}.drop{$0 < 100}
let interval = fibonacci.prefix{$0 < 1000}.drop{$0 < 100}
Concrete Constrained Extensions
Swift 3.1 lets you extend a generic type with a concrete type constraint. Previously, you couldn't extend a type like this because the constraint had to be a protocol. Let's see an example.
For example, Ruby on Rails provides a very useful isBlank
method for checking for user input. Here is how you would implement it in Swift 3.0 as a computed property on the String
data type extension:
// Swift 3.0
extension String {
var isBlank: Bool {
return trimmingCharacters(in: .whitespaces).isEmpty
}
}
let abc = " "
let def = "x"
abc.isBlank // true
def.isBlank // false
If you want the isBlank
computed property to work with optional strings as well, you would do the following in Swift 3.0:
// Swift 3.0
protocol StringProvider {
var string: String {get}
}
extension String: StringProvider {
var string: String {
return self
}
}
extension Optional where Wrapped: StringProvider {
var isBlank: Bool {
return self?.string.isBlank ?? true
}
}
let foo: String? = nil
let bar: String? = " "
let baz: String? = "x"
foo.isBlank // true
bar.isBlank // true
baz.isBlank // false
This creates a StringProvider
protocol for String
to adopt. Which in turn you use to extend Optional
when the wrapped type is a StringProvider
, to add the isBlank
method.
Swift 3.1 lets you extend a concrete type instead of a protocol like this:
// Swift 3.1
extension Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}
This provides the same functionality as before, in much fewer lines of code!