What’s New in Swift 5?
Swift 5 is finally available in Xcode 10.2! This release brings ABI stability and improves the language with some long-awaited features. See what’s new! 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?
25 mins
- Getting Started
- Language Improvements
- Testing Integer Multiples
- Escaping Raw Strings
- Using New Character Properties
- Using New Unicode Scalar Properties
- Removing Subsequences
- Dictionary Updates
- Compacting Dictionaries
- Renaming Dictionary Literals
- Numeric Protocol Updates
- String Interpolation Updates
- Handling Future Enumeration Cases
- Adding Result to the Standard Library
- Conforming Never to Equatable and Hashable
- Dynamically Callable Types
- Swift Package Manager Updates
- Platform Deployment Settings
- Target Build Settings
- Dependency Mirroring
- Miscellaneous Bits and Pieces
- Making Codable Ranges
- Flattening Nested Optionals
- Removing Customization Points From Collections
- Identity Key Paths
- Initializing Literals Through Coercion
- Build Configuration Updates
- Using Variadic Parameters for Enumeration Cases With Associated Values
- Deprecating String Index Encoded Offsets
- New Pointer Methods
- SIMD Vector Updates
- Where to Go From Here?
Removing Subsequences
Swift 4.2 returns SubSequence
from Sequence
customization points as follows:
extension Sequence {
func remove(_ s: String) -> SubSequence {
guard let n = Int(s) else {
return dropLast()
}
return dropLast(n)
}
}
let sequence = [5, 2, 7, 4]
sequence.remove("2") // [5, 2]
sequence.remove("two") // [5, 2, 7]
In this case, remove(_:)
drops the last n
elements from the sequence if s
is an Int
or the last element.
Swift 5 replaces SubSequence
with concrete types in sequences [SE-0234]:
extension Sequence {
func remove(_ s: String) -> [Element] {
guard let n = Int(s) else {
return dropLast()
}
return dropLast(n)
}
}
In this code, remove(_:)
returns [Element]
since dropLast()
and dropLast(_:)
return [Element]
.
Dictionary Updates
Swift 5 brings some long-awaited improvements to dictionaries:
Compacting Dictionaries
Swift 4.2 uses mapValues
, filter
and reduce
to filter nil
values from dictionaries as follows:
let students = ["Oana": "10", "Nori": "ten"]
let filterStudents = students.mapValues(Int.init)
.filter { $0.value != nil }
.mapValues { $0! }
let reduceStudents = students.reduce(into: [:]) { $0[$1.key] = Int($1.value) }
This code uses mapValues
with filter
or reduce
to determine the students with valid grades from students
. Both approaches require multiple dictionary passes and complicate your code.
Swift 5 uses compactMapValues(_:)
for a more efficient solution [SE-0218]:
let mapStudents = students.compactMapValues(Int.init)
It accomplishes the same thing in much fewer lines of code, neat!
Renaming Dictionary Literals
Swift 4.2 uses DictionaryLiteral
to declare dictionaries as follows:
let pets: DictionaryLiteral = ["dog": "Sclip", "cat": "Peti"]
DictionaryLiteral
isn’t a dictionary or a literal. It’s a list of key-value pairs.
Swift 5 renames DictionaryLiteral
to KeyValuePairs
[SE-0214]:
let pets: KeyValuePairs = ["dog": "Sclip", "cat": "Peti"]
Numeric Protocol Updates
Swift 4.2 implements Numeric
for vectors:
// 1
struct Vector {
let x, y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
// 2
extension Vector: ExpressibleByIntegerLiteral {
init(integerLiteral value: Int) {
x = value
y = value
}
}
// 3
extension Vector: Numeric {
var magnitude: Int {
return Int(sqrt(Double(x * x + y * y)))
}
init?<T>(exactly value: T) {
x = value as! Int
y = value as! Int
}
static func +(lhs: Vector, rhs: Vector) -> Vector {
return Vector(lhs.x + rhs.x, lhs.y + rhs.y)
}
static func +=(lhs: inout Vector, rhs: Vector) {
lhs = lhs + rhs
}
static func -(lhs: Vector, rhs: Vector) -> Vector {
return Vector(lhs.x - rhs.x, lhs.y - rhs.y)
}
static func -=(lhs: inout Vector, rhs: Vector) {
lhs = lhs - rhs
}
static func *(lhs: Vector, rhs: Vector) -> Vector {
return Vector(lhs.x * rhs.y, lhs.y * rhs.x)
}
static func *=(lhs: inout Vector, rhs: Vector) {
lhs = lhs * rhs
}
}
// 4
extension Vector: CustomStringConvertible {
var description: String {
return "(\(x) \(y))"
}
}
Here’s how this code works:
- Declare
x
,y
andinit(_:_:)
forVector
. - Implement
init(integerLiteral:)
to conformVector
toExpressibleByIntegerLiteral
as a requirement forNumeric
conformance. - Conform
Vector
toNumeric
by defining the vector’smagnitude
, declaringinit(exactly:)
and implementing+(lhs:rhs:)
,+=(lhs:rhs:)
,-(lhs:rhs:)
,-=(lhs:rhs:)
,*(lhs:rhs:)
,*=(lhs:rhs:)
. - Implement
description
to conformVector
toCustomStringConvertible
.
The code above enables you to work with vectors easily:
var first = Vector(1, 2) // (1,2)
let second = Vector(3, 4) // (3,4)
let third = first + second // (4,6)
first += second // (4,6)
let fourth = first - second // (1,2)
first -= second // (1,2)
Swift 5 implements AdditiveArithmetic
for vectors, because you can’t define the cross product of 2D vectors [SE-0233]. It doesn’t require ExpressibleByIntegerLiteral
conformance:
extension Vector: AdditiveArithmetic {
static var zero: Vector {
return Vector(0, 0)
}
static func +(lhs: Vector, rhs: Vector) -> Vector {
return Vector(lhs.x + rhs.x, lhs.y + rhs.y)
}
static func +=(lhs: inout Vector, rhs: Vector) {
lhs = lhs + rhs
}
static func -(lhs: Vector, rhs: Vector) -> Vector {
return Vector(lhs.x - rhs.x, lhs.y - rhs.y)
}
static func -=(lhs: inout Vector, rhs: Vector) {
lhs = lhs - rhs
}
}
In this code, you conform Vector
to AdditiveArithmetic
by defining a zero
and implementing +(lhs:rhs:)
, +=(lhs:rhs:)
, -(lhs:rhs:)
, -=(lhs:rhs:)
.
String Interpolation Updates
Swift 4.2 implements string interpolation by interpolating segments:
let language = "Swift"
let languageSegment = String(stringInterpolationSegment: language)
let space = " "
let spaceSegment = String(stringInterpolationSegment: space)
let version = 4.2
let versionSegment = String(stringInterpolationSegment: version)
let string = String(stringInterpolation: languageSegment, spaceSegment, versionSegment)
In this code, the compiler first wraps each literal segment and then interpolates one with init(stringInterpolationSegment:)
. Then, it wraps all segments together with init(stringInterpolation:)
.
Swift 5 takes a completely different approach [SE-0228]:
// 1
var interpolation = DefaultStringInterpolation(
literalCapacity: 7,
interpolationCount: 1)
// 2
let language = "Swift"
interpolation.appendLiteral(language)
let space = " "
interpolation.appendLiteral(space)
let version = 5
interpolation.appendInterpolation(version)
// 3
let string = String(stringInterpolation: interpolation)
Here’s what this code does:
- Define a
DefaultStringInterpolation
instance with a certain capacity and interpolation count. - Call
appendLiteral(_:)
orappendInterpolation(_:)
to add literals and interpolated values tointerpolation
. - Produce the final interpolated string by calling
init(stringInterpolation:)
.
Handling Future Enumeration Cases
Swift 4.2 doesn’t handle new enumeration cases properly, as you can see below:
// 1
enum Post {
case tutorial, article, screencast, course
}
// 2
func readPost(_ post: Post) -> String {
switch post {
case .tutorial:
return "You are reading a tutorial."
case .article:
return "You are reading an article."
default:
return "You are watching a video."
}
}
// 3
let screencast = Post.screencast
readPost(screencast) // "You are watching a video."
let course = Post.course
readPost(course) // "You are watching a video."
Here’s what happens in the code above:
- Define all types of blog posts on the website.
- To make
switch
exhaustive, adddefault
. - Handle
.screencast
and.course
withdefault
since screencasts and courses are videos.
The following is how handling podcasts works in Swift 4.2:
enum Post {
case tutorial, article, podcast, screencast, course
}
let podcast = Post.podcast
readPost(podcast) // "You are watching a video."
In this code, you handle .podcast
with default
, even though podcasts aren’t videos. Swift 4.2 doesn’t warn you about this because the switch
is exhaustive.
Swift 5 takes care of added enumeration cases [SE-0192]:
func readPost(_ post: BlogPost) -> String {
switch post {
case .tutorial:
return "You are reading a tutorial."
case .article:
return "You are reading an article."
@unknown default:
return "You are reading a blog post."
}
}
readPost(screencast) // "You are reading a blog post."
readPost(course) // "You are reading a blog post."
readPost(podcast) // "You are reading a blog post."
In this code, you mark default
as @unknown
, and Swift warns you that switch
isn’t exhaustive. default
handles .screencast
, .course
and .podcast
because screencasts, courses and podcasts are blog posts.