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

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.

Note: If you need a short and sweet review of the Swift 3.0 highlights, check out our article on What’s New in Swift 3.

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.

Note: An alternative design used throwing initializers instead of failable ones. The community chose failable ones to be a better, more ergonomic design.

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.

Note: You may use trailing closure syntax in this case:
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!

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.