What’s New in Swift 5.1?

Swift 5.1 is finally out! This article will take you through the advancements and changes the language has to offer in its latest version. By Cosmin Pupăză.

Leave a rating/review
Download materials
Save for later
Share

Good news: Swift 5.1 is now available in Xcode 11 beta! This release brings module stability and improves the language with important features. In this tutorial, you’ll learn about what’s new in Swift 5.1. You’ll need Xcode 11 beta to work with Swift 5.1, so go ahead and install it before getting started.

Getting Started

Swift 5.1 is source compatible with Swift 5. It’s also binary compatible with Swift 5 and future releases of Swift thanks to ABI stability.

Swift 5.1 adds module stability on top of ABI stability introduced in Swift 5. While ABI stability takes care of app compatibility at runtime, module stability enables library compatibility at compile time. This means you can use a third-party framework with any compiler version instead of only the one it was built with.

Each tutorial section contains Swift Evolution proposal numbers such as [SE-0001]. You can explore each change by clicking the linked tag of each proposal.

I recommend you follow the tutorial by trying out the new features in a playground. Start Xcode 11 and go to File ▸ New ▸ Playground. Choose iOS for the platform and Blank as its template. Name it and save it where you want. Time to get started!

Note: Need a refresher of the Swift 5 highlights? Check out the Swift 5 tutorial: What’s New in Swift 5?

Language Improvements

There are a number of language improvements in this release, including opaque result types, function builders, property wrappers and more.

Opaque Result Types

You may use protocols as return types for functions in Swift 5.

With your new Playground open, open the Project Navigator by navigating to View ▸ Navigators ▸ Show Project Navigator. Right-click on the Sources folder, select New File and name the file BlogPost. Replace the contents of the new file with the definition of a new protocol named BlogPost.

public protocol BlogPost {
  var title: String { get }
  var author: String { get }
}

Right-click on the top level playground and select New Playground Page. Rename the new playground page Opaque Tutorials and paste this in it:

// 1
struct Tutorial: BlogPost {
  let title: String
  let author: String
}

// 2
func createBlogPost(title: String, author: String) -> BlogPost {
  guard !title.isEmpty && !author.isEmpty else {
    fatalError("No title and/or author assigned!")
  }
  return Tutorial(title: title, author: author)
}

// 3
let swift4Tutorial = createBlogPost(title: "What's new in Swift 4.2?",
                                    author: "Cosmin Pupăză")
let swift5Tutorial = createBlogPost(title: "What's new in Swift 5?", 
                                    author: "Cosmin Pupăză")

Going over this step by step:

  1. Declare title and author for Tutorial since Tutorial implements BlogPost.
  2. Check if title and author are valid and return Tutorial from createBlogPost(title:author:) if the test succeeds.
  3. Use createBlogPost(title:author:) to create swift4Tutorial and swift5Tutorial.

You can reuse the prototype and logic of createBlogPost(title:author:) to create screencasts as well because screencasts are blog posts under the hood too.

Right-click on the top level playground and select New Playground Page. Rename the new playground page Opaque Screencasts and paste this in it:

struct Screencast: BlogPost {
  let title: String
  let author: String
}

func createBlogPost(title: String, author: String) -> BlogPost {
  guard !title.isEmpty && !author.isEmpty else {
    fatalError("No title and/or author assigned!")
  }
  return Screencast(title: title, author: author)
}

let swift4Screencast = createBlogPost(title: "What's new in Swift 4.2?", 
                                      author: "Josh Steele")           
let swift5Screencast = createBlogPost(title: "What's new in Swift 5?", 
                                      author: "Josh Steele")

Screencast implements BlogPost, so you return Screencast from createBlogPost(title:author:) and use createBlogPost(title:author:) to create swift4Screencast and swift5Screencast this time.

Navigate to BlogPost.swift in the Sources folder and make BlogPost conform to Equatable.

public protocol BlogPost: Equatable {
  var title: String { get }
  var author: String { get }
}

At this point, you’ll get an error that BlogPost can only be used as a generic constraint. This is because Equatable has an associated type called Self. Protocols with associated types are not types, even though they look like types. Instead, they’re kind of like type placeholders saying “this can be any concrete type that conforms to this protocol”.

Swift 5.1 lets you use these protocols as regular types with opaque result types [SE-0244].

In the Opaque Tutorials page, add some to the return type of createBlogPost, saying that it returns a concrete implementation of BlogPost.

func createBlogPost(title: String, author: String) -> some BlogPost {

Similarly, in the Opaque Screencasts page, use some to tell the compiler createBlogPost returns some type of BlogPost.

func createBlogPost(title: String, author: String) -> some BlogPost {

You may return any concrete type that implements BlogPost from createBlogPost: Tutorial or Screencast in this case.

Now, you can check if the previously created tutorials and screencasts are the same. At the bottom of Opaque Tutorials, paste the following to check if swift4Tutorial and swift5Tutorial are the same.

let sameTutorial = swift4Tutorial == swift5Tutorial

At the bottom of Opaque Screencasts, paste the following to check if swift4Screencast and swift5Screencast are the same.

let sameScreencast = swift4Screencast == swift5Screencast

Implicit Returns From Single-Expression Functions

You use return in single-expression functions in Swift 5:

extension Sequence where Element == Int {
  func addEvenNumbers() -> Int {
    return reduce(0) { $1.isMultiple(of: 2) ? $0 + $1 : $0 }
  }

  func addOddNumbers() -> Int {
    return reduce(0) { $1.isMultiple(of: 2) ? $0 : $0 + $1 }
  }
}

let numbers = [10, 5, 2, 7, 4]
let evenSum = numbers.addEvenNumbers()
let oddSum = numbers.addOddNumbers()

You use reduce(_:_:) in addEvenNumbers() and addOddNumbers() to determine the sum of even and odd numbers in Sequence.

Swift 5.1 drops return in single-expression functions so they behave like single-line closures in this case [SE-0255]:

extension Sequence where Element == Int {
  func addEvenNumbers() -> Int {
    reduce(0) { $1.isMultiple(of: 2) ? $0 + $1 : $0 }
  }

  func addOddNumbers() -> Int {
    reduce(0) { $1.isMultiple(of: 2) ? $0 : $0 + $1 }
  }
}

The code is cleaner and easier to follow this time.

Note: Want to learn more about how reduce(_:_:) works in Swift? Check out the functional programming tutorial: An Introduction to Functional Programming in Swift.