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ă.
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
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 4.2?
30 mins
- Getting Started
- Language Improvements
- Generating Random Numbers
- Dynamic Member Lookup
- Enumeration Cases Collections
- New Sequence Methods
- Testing Sequence Elements
- Conditional Conformance Updates
- Hashable Improvements
- Removing Elements From Collections
- Toggling Boolean States
- New Compiler Directives
- New Pointer Functions
- Memory Layout Updates
- Inline Functions in Modules
- Miscellaneous Bits and Pieces
- Swift Package Manager Updates
- Removing Implicitly Unwrapped Optionals
- Where to Go From Here?
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!
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:
- You use
random(in:)
to generate random digits from ranges. -
randomElement()
returnsnil
if the range is empty, so you unwrap the returnedInt?
withif let
. - You use
random(in:)
to generate a randomDouble
,Float
orCGFloat
andrandom()
to return a randomBool
.
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:
- You use
arc4random_uniform(_:)
to determine the shuffling order of theplaylist
and returnshuffledPlaylist
withsorted(_:_:)
. - You then shuffle
names
in place withsort(_:_:)
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!
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:
- You mark
Person
as@dynamicMemberLookup
to enable dot syntax for its custom subscripts. - You conform to
@dynamicMemberLookup
by implementingsubscript(dynamicMember:)
for the class. - 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:
- You annotate
Random
with@dynamicMemberLookup
to enable dot syntax for its subscripts. - You extend the protocol and make it conform to
@dynamicMemberLookup
by implementingsubscript(dynamicMember:)
. The subscript usesrandom(in:)
to return a random digit between 0 and 9. - You extend
Int
and make it conform toRandom
. - You use dot syntax to generate a random digit and filter it out from
number
.