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,yandinit(_:_:)forVector. - Implement
init(integerLiteral:)to conformVectortoExpressibleByIntegerLiteralas a requirement forNumericconformance. - Conform
VectortoNumericby defining the vector’smagnitude, declaringinit(exactly:)and implementing+(lhs:rhs:),+=(lhs:rhs:),-(lhs:rhs:),-=(lhs:rhs:),*(lhs:rhs:),*=(lhs:rhs:). - Implement
descriptionto conformVectortoCustomStringConvertible.
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:).
Working with vectors is so easy in Swift 5!
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
DefaultStringInterpolationinstance 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
switchexhaustive, adddefault. - Handle
.screencastand.coursewithdefaultsince 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.
The future is bright for Swift 5 enumerations!

