What’s New in Swift 4.2?

Swift 4.2 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 4.2 is now available in Xcode 10 beta! This release updates important Swift 4.1 features and improves the language in preparation for ABI stability.

This tutorial covers the most significant changes in Swift 4.2. It requires Xcode 10, so make sure you download and install the latest beta of Xcode before getting started.

Getting Started

Swift 4.2 is source compatible with Swift 4.1, but isn’t binary compatible with any other releases. Apple designed Swift 4.2 to be an intermediate step towards achieving ABI stability in Swift 5, which should enable binary compatibility between applications and libraries compiled with different Swift versions. The ABI features receive plenty of time for feedback from the community before integration into the final ABI.

This tutorial’s sections contain Swift Evolution proposal numbers such as [SE-0001]. You can explore the details of each change by clicking the linked tag of each proposal.

You’ll get the most out of this tutorial if you try out the changes in a playground. Start Xcode 10 and go to File ▸ New ▸ Playground. Select iOS for the platform and Blank for the template. Name it whatever you like and save it anywhere you wish. You’re now ready to get started!

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

Language Improvements

There are quite a few language features in this release such as random number generators, dynamic member lookup and more.

Generating Random Numbers

Swift 4.1 imports C APIs to generate random numbers, as in the snippet below:

let digit = Int(arc4random_uniform(10))

arc4random_uniform(_:) returned a random digit between 0 and 9. It required you to import Foundation, and didn’t work on Linux. On the other hand, all Linux-based approaches introduced modulo bias, which meant that certain numbers were generated more often than others.

Swift 4.2 solves these problems by adding a random API to the standard library [SE-0202]:

// 1  
let digit = Int.random(in: 0..<10)

// 2
if let anotherDigit = (0..<10).randomElement() {
  print(anotherDigit)
} else {
  print("Empty range.")
}

// 3
let double = Double.random(in: 0..<1)
let float = Float.random(in: 0..<1)
let cgFloat = CGFloat.random(in: 0..<1)
let bool = Bool.random()

Here’s what this does:

  1. You use random(in:) to generate random digits from ranges.
  2. randomElement() returns nil if the range is empty, so you unwrap the returned Int? with if let.
  3. You use random(in:) to generate a random Double, Float or CGFloat and random() to return a random Bool.

Generating random numbers like a pro in Swift 4.2!

Generating random numbers like a pro in Swift 4.2!

Swift 4.1 also used C functions for generating random values from arrays:

let playlist = ["Nothing Else Matters", "Stairway to Heaven", "I Want to Break Free", "Yesterday"]
let index = Int(arc4random_uniform(UInt32(playlist.count)))
let song = playlist[index]

Swift 4.1 used arc4random_uniform(_:) to generate a valid index from playlist and return the corresponding song. This solution required you to cast between Int and UInt32 and also had all the previously mentioned issues.

Swift 4.2 takes a more straightforward approach:

if let song = playlist.randomElement() {
  print(song)
} else {
  print("Empty playlist.")
}

randomElement() returns nil if playlist is empty, so you unwrap the returned String?.

Swift 4.1 didn’t contain any collection shuffling algorithms, so you had to use a roundabout way to achieve the intended result:

// 1
let shuffledPlaylist = playlist.sorted{ _, _ in arc4random_uniform(2) == 0 }

// 2
var names = ["Cosmin", "Oana", "Sclip", "Nori"]
names.sort { _, _ in arc4random_uniform(2) == 0 }

Here’s what you’re doing in this code:

  1. You use arc4random_uniform(_:) to determine the shuffling order of the playlist and return shuffledPlaylist with sorted(_:_:).
  2. You then shuffle names in place with sort(_:_:) using the previous technique.

Swift 4.2 provides more efficient, and arguably more elegant, shuffling algorithms:

let shuffledPlaylist = playlist.shuffled()
names.shuffle()

In 4.2, you simply use shuffled() to create a shuffled playlist and shuffle names on the spot with shuffle(). Boom!

Shuffling playlists has never been easier thanks to Swift 4.2!

Shuffling playlists has never been easier thanks to Swift 4.2!

Dynamic Member Lookup

Swift 4.1 used the following square brackets syntax for custom subscript calls:

class Person {
  let name: String
  let age: Int
  private let details: [String: String]
  
  init(name: String, age: Int, details: [String: String]) {
    self.name = name
    self.age = age
    self.details = details
  }
  
  subscript(key: String) -> String {
    switch key {
      case "info":
        return "\(name) is \(age) years old."
      default:
        return details[key] ?? ""
    }
  }
}

let details = ["title": "Author", "instrument": "Guitar"]
let me = Person(name: "Cosmin", age: 32, details: details)
me["info"]   // "Cosmin is 32 years old."
me["title"]  // "Author"

The subscript in this case returns contents from a private data store or a custom message based on the person’s name and age.

Swift 4.2 uses dynamic member lookup to provide dot syntax for subscripts instead in [SE-0195]:

// 1
@dynamicMemberLookup
class Person {
  let name: String
  let age: Int
  private let details: [String: String]
  
  init(name: String, age: Int, details: [String: String]) {
    self.name = name
    self.age = age
    self.details = details
  }
  
  // 2
  subscript(dynamicMember key: String) -> String {
    switch key {
      case "info":
        return "\(name) is \(age) years old."
      default:
        return details[key] ?? ""
    }
  }
}


// 3
me.info   // "Cosmin is 32 years old." 
me.title  // "Author"

Taking it comment-by-comment:

  1. You mark Person as @dynamicMemberLookup to enable dot syntax for its custom subscripts.
  2. You conform to @dynamicMemberLookup by implementing subscript(dynamicMember:) for the class.
  3. You call the previously implemented subscript using dot syntax.

The compiler evaluates the subscript call dynamically at runtime, which lets you to write type-safe code much like you would in scripting languages like Python or Ruby.

Dynamic member lookup doesn’t mess up your class properties:

me.name // "Cosmin"
me.age // 32

You use dot syntax to call name and age instead of the subscript in this case.

Further, derived classes inherit dynamic member lookup from their base ones:

@dynamicMemberLookup
class Vehicle {
  let brand: String
  let year: Int
  
  init(brand: String, year: Int) {
    self.brand = brand
    self.year = year
  }
  
  subscript(dynamicMember key: String) -> String {
    return "\(brand) made in \(year)."
  }
}

class Car: Vehicle {}

let car = Car(brand: "BMW", year: 2018)
car.info  // "BMW made in 2018."

You can use dot syntax to call the car’s subscript, since any Car is a Vehicle and Vehicle implements @dynamicMemberLookup.

You can add dynamic member lookup to existing types with protocol extensions:

// 1
@dynamicMemberLookup
protocol Random {}

// 2
extension Random {
  subscript(dynamicMember key: String) -> Int {
    return Int.random(in: 0..<10)
  }
}

// 3
extension Int: Random {}

// 4
let number = 10
let randomDigit = String(number.digit)
let noRandomDigit = String(number).filter { String($0) != randomDigit }

Here’s the play-by-play:

  1. You annotate Random with @dynamicMemberLookup to enable dot syntax for its subscripts.
  2. You extend the protocol and make it conform to @dynamicMemberLookup by implementing subscript(dynamicMember:). The subscript uses random(in:) to return a random digit between 0 and 9.
  3. You extend Int and make it conform to Random.
  4. You use dot syntax to generate a random digit and filter it out from number.
Cosmin Pupăză

Contributors

Cosmin Pupăză

Author

Bhagat Singh

Tech Editor

Chris Belanger

Editor

Vladyslav Mytskaniuk

Illustrator

James Frost

Final Pass Editor

Richard Critz

Team Lead

Over 300 content creators. Join our team.