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ă.
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 5.1?
25 mins
- Getting Started
- Language Improvements
- Opaque Result Types
- Implicit Returns From Single-Expression Functions
- Function Builders
- Property Wrappers
- Synthesizing Default Values for Initializers in Structures
- Self for Static Members
- Creating Uninitialized Arrays
- Diffing Ordered Collections
- Static and Class Subscripts
- Dynamic Member Lookup for Keypaths
- Keypaths for Tuples
- Equatable and Hashable Conformance for Weak and Unowned Properties
- Ambiguous Enumeration Cases
- Matching Optional Enumerations Against Non-optionals
- New Features for Strings
- Contiguous Strings
- Miscellaneous Bits and Pieces
- Converting Tuple Types
- Tuples with Duplicate Labels
- Overloading Functions With Any Parameters
- Type Aliases for Autoclosure Parameters
- Returning Self From Objective-C methods
- Stable ABI Libraries
- Where to Go From Here?
Static and Class Subscripts
Swift 5.1 lets you declare static and class subscripts in classes [SE-0254]:
// 1
@dynamicMemberLookup
class File {
let name: String
init(name: String) {
self.name = name
}
// 2
static subscript(key: String) -> String {
switch key {
case "path":
return "custom path"
default:
return "default path"
}
}
// 3
class subscript(dynamicMember key: String) -> String {
switch key {
case "path":
return "custom path"
default:
return "default path"
}
}
}
// 4
File["path"]
File["PATH"]
File.path
File.PATH
This is how it all works:
- Mark
File
as@dynamicMemberLookup
to enable dot syntax for custom subscripts. - Create a static subscript that returns the default or custom path for
File
. - Define the class version of the previous subscript using dynamic member lookup.
- Call both subscripts with the corresponding syntax.
Dynamic Member Lookup for Keypaths
Swift 5.1 implements dynamic member lookup for keypaths [SE-0252]:
// 1
struct Point {
let x, y: Int
}
// 2
@dynamicMemberLookup
struct Circle<T> {
let center: T
let radius: Int
// 3
subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U {
center[keyPath: keyPath]
}
}
// 4
let center = Point(x: 1, y: 2)
let circle = Circle(center: center, radius: 1)
circle.x
circle.y
Going over all of this step-by-step:
- Declare
x
andy
forPoint
. - Annotate
Circle
with@dynamicMemberLookup
to enable dot syntax for its subscripts. - Create a generic subscript which uses keypaths to access
center
properties fromCircle
. - Call
center
properties oncircle
using dynamic member lookup instead of keypaths.
Keypaths for Tuples
You can use keypaths for tuples in Swift 5.1:
// 1
struct Instrument {
let brand: String
let year: Int
let details: (type: String, pitch: String)
}
// 2
let instrument = Instrument(brand: "Roland",
year: 2019,
details: (type: "acoustic", pitch: "C"))
let type = instrument[keyPath: \Instrument.details.type]
let pitch = instrument[keyPath: \Instrument.details.pitch]
Here what's going on:
- Declare
brand
,year
anddetails
forInstrument
. - Use keypaths to get
type
andpitch
fromdetails
ininstrument
.
Equatable and Hashable Conformance for Weak and Unowned Properties
Swift 5.1 automatically synthesizes Equatable
and Hashable
conformance for structures with weak
and unowned
stored properties.
Suppose you have two classes:
class Key {
let note: String
init(note: String) {
self.note = note
}
}
extension Key: Hashable {
static func == (lhs: Key, rhs: Key) -> Bool {
lhs.note == rhs.note
}
func hash(into hasher: inout Hasher) {
hasher.combine(note)
}
}
class Chord {
let note: String
init(note: String) {
self.note = note
}
}
extension Chord: Hashable {
static func == (lhs: Chord, rhs: Chord) -> Bool {
lhs.note == rhs.note
}
func hash(into hasher: inout Hasher) {
hasher.combine(note)
}
}
Key
and Chord
both conform to Equatable
and Hashable
by implementing ==(lhs:rhs:)
and hash(into:)
.
If you use those classes in a struct, Swift 5.1 will be able to synthesize Hashable
conformance:
struct Tune: Hashable {
unowned let key: Key
weak var chord: Chord?
}
let key = Key(note: "C")
let chord = Chord(note: "C")
let tune = Tune(key: key, chord: chord)
let chordlessTune = Tune(key: key, chord: nil)
let sameTune = tune == chordlessTune
let tuneSet: Set = [tune, chordlessTune]
let tuneDictionary = [tune: [tune.key.note, tune.chord?.note],
chordlessTune: [chordlessTune.key.note,
chordlessTune.chord?.note]]
Tune
is Equatable
and Hashable
because key
and chord
are Equatable
and Hashable
.
Because it's Hashable
, you can compare tune
with chordlessTune
, add them to tuneSet
and use them as keys for tuneDictionary
.
Ambiguous Enumeration Cases
Swift 5.1 generates warnings for ambiguous enumeration cases:
// 1
enum TutorialStyle {
case cookbook, stepByStep, none
}
// 2
let style: TutorialStyle? = .none
Here’s how this works:
- Define different styles for
TutorialStyle
. - Swift fires a warning since it’s not clear to the compiler what
.none
means in this case:Optional.none
orTutorialStyle.none
.
Matching Optional Enumerations Against Non-optionals
You use the optional pattern to match non-optionals with optional enumerations in Swift 5:
// 1
enum TutorialStatus {
case written, edited, published
}
// 2
let status: TutorialStatus? = .published
switch status {
case .written?:
print("Ready for editing!")
case .edited?:
print("Ready to publish!")
case .published?:
print("Live!")
case .none:
break
}
The above code does the following:
- Declare all possible states for
TutorialStatus
. - Use the optional pattern to switch on
status
since you define it as an optional.
Swift 5.1 removes optional pattern matching in this case:
switch status {
case .written:
print("Ready for editing!")
case .edited:
print("Ready to publish!")
case .published:
print("Live!")
case .none:
break
}
The code is cleaner and easier to understand this time.
New Features for Strings
Swift 5.1 adds some much-needed features to strings [SE-0248]:
UTF8.width("S")
UTF8.isASCII(83)
Here you determine the UTF-8 encoding width of the Unicode scalar value and check if the given code unit represents an ASCII scalar. Have a look at the proposal for other APIs you can use.
Contiguous Strings
Swift 5.1 implements important changes to contiguous strings [SE-0247]:
var string = "Swift 5.1"
if !string.isContiguousUTF8 {
string.makeContiguousUTF8()
}
You check if the UTF-8 encoded string is contiguous with isContiguousUTF8
and use makeContiguousUTF8()
to make it so, if not. Take a look at the proposal to see what else you can do with contiguous strings.
Miscellaneous Bits and Pieces
There are a few other features in Swift 5.1 you should know about:
Converting Tuple Types
Swift 5.1 improves conversion of tuple types:
let temperatures: (Int, Int) = (25, 30)
let convertedTemperatures: (Int?, Any) = temperatures
You assign temperatures
to convertedTemperatures
because you can convert (Int, Int)
to (Int?, Any)
in this case.
Tuples with Duplicate Labels
You can declare tuples with duplicate labels in Swift 5:
let point = (coordinate: 1, coordinate: 2)
point.coordinate
It’s not clear if coordinate
returns the first or second element from point
in this case, so Swift 5.1 removes duplicate labels for tuples.