What’s New in Swift 4.1?
Swift 4.1 is here! What does it mean for you? In this article, you’ll learn about the most significant changes introduced in Swift 4.1 and the impact they will have on your code. 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.1?
25 mins
- Getting Started
- Language Improvements
- Conditional Conformance
- Convert Between Camel Case and Snake Case During JSON Encoding
- Equatable and Hashable Protocols Conformance
- Hashable Index Types
- Recursive Constraints on Associated Types in Protocols
- Weak and Unowned References in Protocols
- Index Distances in Collections
- Structure Initializers in Modules
- Platform Settings and Build Configuration Updates
- Build Imports
- Target Environments
- Miscellaneous Bits and Pieces
- Compacting Sequences
- Unsafe Pointers
- New Playground Features
- Where to Go From Here?
Xcode 9.3 and Swift 4.1 are finally out of beta! This release contains some long-awaited improvements to the standard library and language itself. If you haven’t been following the Swift Evolution Process closely, keep reading.
In this tutorial, you’ll learn about the most significant changes introduced in Swift 4.1.
This article requires Xcode 9.3, so make sure you have it installed and ready to go before getting started.
Getting Started
Swift 4.1 is source-compatible with Swift 4, so the new features won’t break your code if you’ve already migrated your project to Swift 4 using the Swift Migrator in Xcode.
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 dig into the full details of each particular change. I recommend you try out the features in a playground so you have a better understanding of everything that changes as you work.
To start, fire up Xcode 9.3 and select File ▸ New ▸ Playground. Choose iOS as the platform and Blank as its template. Name and save it as you like. To get the most out of this tutorial, try out each feature in your new playground as you work.
Language Improvements
There are a number of language improvements in this release, including conditional conformance, recursive constraints on associated types in protocols and more.
Conditional Conformance
Conditional conformance enables protocol conformance for generic types where the type arguments satisfy certain conditions [SE-0143]. This is a powerful feature that makes your code more flexible. You can see how it works with a few examples.
Conditional conformance in the standard library
In Swift 4, you could compare arrays, dictionaries and optionals as long as their elements were Equatable
. This worked absolutely fine for basic scenarios such as:
// Arrays of Int
let firstArray = [1, 2, 3]
let secondArray = [1, 2, 3]
let sameArray = firstArray == secondArray
// Dictionaries with Int values
let firstDictionary = ["Cosmin": 10, "George": 9]
let secondDictionary = ["Cosmin": 10, "George": 9]
let sameDictionary = firstDictionary == secondDictionary
// Comparing Int?
let firstOptional = firstDictionary["Cosmin"]
let secondOptional = secondDictionary["Cosmin"]
let sameOptional = firstOptional == secondOptional
Using the ==
operator to test equality in these examples worked since Int
is Equatable
in Swift 4. However, comparing collections of optionals was a common situation you might have run into with Swift 4 since optionals do not conform to Equatable
. Swift 4.1 fixes this issue using conditional conformance, letting optional types with underlying Equatable
types to be compared:
// Array of Int?
let firstArray = [1, nil, 2, nil, 3, nil]
let secondArray = [1, nil, 2, nil, 3, nil]
let sameArray = firstArray == secondArray
// Dictionary with Int? values
let firstDictionary = ["Cosmin": 10, "George": nil]
let secondDictionary = ["Cosmin": 10, "George": nil]
let sameDictionary = firstDictionary == secondDictionary
// Comparing Int?? (Optional of Optional)
let firstOptional = firstDictionary["Cosmin"]
let secondOptional = secondDictionary["Cosmin"]
let sameOptional = firstOptional == secondOptional
Int?
is Equatable
in Swift 4.1, so the ==
operator works for [Int?]
, [String: Int?]
and Int??
.
A similar problem has been solved when comparing arrays of arrays (e.g. [[Int]]
). In Swift 4, you could only compare arrays of sets (e.g. [Set<Int>]
), since sets conform to Equatable
. Swift 4.1 solves this, since arrays (and dictionaries) are Equatable
as long as their underlying values are, too.
let firstArrayOfSets = [Set([1, 2, 3]), Set([1, 2, 3])]
let secondArrayOfSets = [Set([1, 2, 3]), Set([1, 2, 3])]
// Will work in Swift 4 and Swift 4.1
// since Set<Int> is Equatable
firstArrayOfSets == secondArrayOfSets
let firstArrayOfArrays = [[1, 2, 3], [3, 4, 5]]
let secondArrayOfArrays = [[1, 2, 3], [3, 4, 5]]
// Caused an error in Swift 4, but works in Swift 4.1
// since Arrays are Equatable in Swift 4.1
firstArrayOfArrays == secondArrayOfArrays
Generally, Swift 4.1’s Optional
, Array
and Dictionary
now conform to Equatable
and Hashable
whenever their underlying values or elements conform to these protocols.
This is how conditional conformance works in the standard library. Next, you will implement it in your own code.
Conditional conformance in code
You’re going to use conditional conformance to create your own band of musical instruments. Add the following block of code at the bottom of the playground to get started:
// 1
class LeadInstrument: Equatable {
let brand: String
init(brand: String) {
self.brand = brand
}
func tune() -> String {
return "Standard tuning."
}
static func ==(lhs: LeadInstrument, rhs: LeadInstrument) -> Bool {
return lhs.brand == rhs.brand
}
}
// 2
class Keyboard: LeadInstrument {
override func tune() -> String {
return "Keyboard standard tuning."
}
}
// 3
class Guitar: LeadInstrument {
override func tune() -> String {
return "Guitar standard tuning."
}
}
Here’s what’s this does step-by-step:
-
LeadInstrument
conforms toEquatable
. It has a certain brand and a method named tune() that you’ll eventually use to tune the instrument. - You override
tune()
inKeyboard
to return keyboard standard tuning. - You do the same thing for
Guitar
.
Next, declare the band of instruments:
// 1
class Band<LeadInstrument> {
let name: String
let lead: LeadInstrument
init(name: String, lead: LeadInstrument) {
self.name = name
self.lead = lead
}
}
// 2
extension Band: Equatable where LeadInstrument: Equatable {
static func ==(lhs: Band<LeadInstrument>, rhs: Band<LeadInstrument>) -> Bool {
return lhs.name == rhs.name && lhs.lead == rhs.lead
}
}
Here’s what you’re doing step-by-step:
- You create a class called
Band
with a generic type –LeadInstrument
. Each band has an unique name and lead instrument. - You use
where
to constrainBand
to conform toEquatable
as long asLeadInstrument
does. Your ability to conform theBand
‘s genericLeadInstrument
toEquatable
is exactly where conditional conformance comes into play.
Next, define your favorite bands and compare them:
// 1
let rolandKeyboard = Keyboard(brand: "Roland")
let rolandBand = Band(name: "Keys", lead: rolandKeyboard)
let yamahaKeyboard = Keyboard(brand: "Yamaha")
let yamahaBand = Band(name: "Keys", lead: yamahaKeyboard)
let sameBand = rolandBand == yamahaBand
// 2
let fenderGuitar = Guitar(brand: "Fender")
let fenderBand = Band(name: "Strings", lead: fenderGuitar)
let ibanezGuitar = Guitar(brand: "Ibanez")
let ibanezBand = Band(name: "Strings", lead: ibanezGuitar)
let sameBands = fenderBand == ibanezBand
In this piece of code, you create two Keyboard
s and Guitar
s along with their appropriate Band
s. You then compare the bands directly, thanks to the conditional conformance you defined earlier.
Conditional conformance in JSON parsing
Arrays, dictionaries, sets and optionals conform to Codable
if their elements conform to Codable
in Swift 4.1. Add the following code to your playground to try this:
struct Student: Codable, Hashable {
let firstName: String
let averageGrade: Int
}
let cosmin = Student(firstName: "Cosmin", averageGrade: 10)
let george = Student(firstName: "George", averageGrade: 9)
let encoder = JSONEncoder()
// Encode an Array of students
let students = [cosmin, george]
do {
try encoder.encode(students)
} catch {
print("Failed encoding students array: \(error)")
}
// Encode a Dictionary with student values
let studentsDictionary = ["Cosmin": cosmin, "George": george]
do {
try encoder.encode(studentsDictionary)
} catch {
print("Failed encoding students dictionary: \(error)")
}
// Encode a Set of students
let studentsSet: Set = [cosmin, george]
do {
try encoder.encode(studentsSet)
} catch {
print("Failed encoding students set: \(error)")
}
// Encode an Optional Student
let optionalStudent: Student? = cosmin
do {
try encoder.encode(optionalStudent)
} catch {
print("Failed encoding optional student: \(error)")
}
You use this code to encode [Student]
, [String: Student]
, Set<Student>
and Student?
. This works smoothly in Swift 4.1 since Student
is Codable
, which makes these collection types conform to it as well.