What's new in Swift 5, 5.1 and 5.2

In this post I will describe what has been added to Swift 5, 5.1 and 5.2
Language improvements
Testing Integer Multiples
To check if a number is a multiple of another one, you can use the '%' operator, but you also need to check that the dividend is not 0:
let firstNumber = 4
let secondNumber = 2
if secondNumber != 0 && firstNumber % secondNumber == 0 {
print("\(secondNumber) * \(firstNumber / secondNumber) = \(firstNumber)")
}
Now you can simply use the isMultiple method, that also checks that the dividend is not 0:
if firstNumber.isMultiple(of: secondNumber) {
print("\(secondNumber) * \(firstNumber / secondNumber) = \(firstNumber)")
}
Escaping raw strings
Now you can use '#' to create a raw string, that allows you to use baskslashes and quotes without any issue:
let raw = #"You can create "raw"\"plain" strings in Swift 5."#
let multiline = #"""
You can create """raw"""\"""plain""" strings
on multiple lines
in Swift 5.
"""#
If you use string interpolation in raw strings, you must use the pound operator before the backslash:
let track = "Nothing Else Matters"
print(#"My favorite tune\song is \#(track)."#)
If your string needs to contain pounds, then you can use more pounds at the beginning and end of the string:
let hashtag = ##"You can use the Swift "hashtag" #swift in Swift 5."##
Using New Character Properties
Character has the new isNumber property:
let id = "ID10"
var digits = 0
id.forEach { digits += $0.isNumber ? 1 : 0 }
print("Id has \(digits) digits.")
Using New Unicode Scalar Properties
Unicode scalar characters have new properties:
let username = "bond007"
var letters = 0
username.unicodeScalars.forEach { letters += $0.properties.isAlphabetic ? 1 : 0 }
print("Username has \(letters) letters.")
Dictionary Updates
Compacting Dictionaries
Swift introduce Dictionary.compactMapValues:
let students = ["Oana": "10", "Nori": "ten"]
let mapStudents = students.compactMapValues(Int.init)
Renaming Dictionary Literals
The DictionaryLiteral type has been renamed to KeyValuePairs.
Numeric Protocol Updates
There is a new protocol AdditiveArithmetic to allow Vectors to implement sums and subtractions, but not products.
Handling Future Enumeration Cases
To receive warning when doing switch to enums, add @unknown default to receive warnings when the switch doesn't deal with all cases:
// 1
enum Post {
case tutorial, article, podcast, 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."
@unknown 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."
Adding Result to the Standard Library
There is Result added to the standard library:
// 1
enum ConnectionError: Error {
case noNetwork, noDatabase
}
// 2
let networkSuccess = Result<String, ConnectionError>.success("Network connected!")
let databaseSuccess = Result<String, ConnectionError>.success("Database connected!")
let networkFailure = Result<String, ConnectionError>.failure(.noNetwork)
let databaseFailure = Result<String, ConnectionError>.failure(.noDatabase)
let sameSuccess = networkSuccess == databaseSuccess
let sameFailure = networkFailure == databaseFailure
let success: Set = [networkSuccess, databaseSuccess]
let failure: Set = [networkFailure, databaseFailure]
let successDictionary = [
networkSuccess: try! networkSuccess.get(),
databaseSuccess: try! databaseSuccess.get()
]
let failureDictionary = [
networkFailure: ConnectionError.noNetwork,
databaseFailure: ConnectionError.noDatabase
]
Conforming Never to Equatable and Hashable
Never is now conforming to Equatable and Hashable:
let alwaysSucceeds = Result<String, Never>.success("Network connected!")
let neverFails = Result<String, Never>.success("Database connected!")
let alwaysFails = Result<Never, ConnectionError>.failure(.noNetwork)
let neverSucceeds = Result<Never, ConnectionError>.failure(.noDatabase)
let sameValue = alwaysSucceeds == neverFails
let sameError = alwaysFails == neverSucceeds
let alwaysSuccess: Set = [alwaysSucceeds, neverFails]
let alwaysFailure: Set = [alwaysFails, neverSucceeds]
let alwaysSuccessDictionary = [
alwaysSucceeds: try! alwaysSucceeds.get(),
neverFails: try! neverFails.get()
]
let alwaysFailureDictionary = [
alwaysFails: ConnectionError.noNetwork,
neverSucceeds: ConnectionError.noDatabase
]
Dynamically Callable Types
Now it's possible to define dynamic types:
// 1
@dynamicCallable
class DynamicFeatures {
// 2
func dynamicallyCall(withArguments params: [Int]) -> Int? {
guard !params.isEmpty else {
return nil
}
return params.reduce(0, +)
}
func dynamicallyCall(withKeywordArguments params: KeyValuePairs<String, Int>) -> Int? {
guard !params.isEmpty else {
return nil
}
return params.reduce(0) { $1.key.isEmpty ? $0 : $0 + $1.value }
}
}
// 3
let features = DynamicFeatures()
features() // nil
features(3, 4, 5) // 12
features(first: 3, 4, second: 5) // 8
Miscellaneous Bits and Pieces
Making Codable Ranges
Ranges are now Codable:
let temperature = 0...10
let encoder = JSONEncoder()
let data = try! encoder.encode(temperature)
let decoder = JSONDecoder()
let temperatureRange = try! decoder.decode(ClosedRange<Int>.self, from: data)
Identity Key Paths
It's now possible to update more properties of an object in a single step with identity key paths:
class Tutorial {
let title: String
let author: String
init(title: String, author: String) {
self.title = title
self.author = author
}
}
var tutorial = Tutorial(title: "What's New in Swift 5.0?", author: "Cosmin Pupaza")
// old way
tutorial.self = Tutorial(title: "What's New in Swift 5?", author: "Cosmin Pupăză")
// new way
tutorial[keyPath: \.self] = Tutorial(
title: "What's New in Swift 5?",
author: "Cosmin Pupăză")
Opaque Result Types
Let's consider the following code:
public protocol BlogPost: Equatable {
var title: String { get }
var author: String { get }
}
// 1
struct Tutorial: BlogPost {
let title: String
let author: String
}
// 2
func createBlogPost(title: String, author: String) -> BlogPost {
guard !title.isEmpty && !author.isEmpty else {
fatalError("No title and/or author assigned!")
}
return Tutorial(title: title, author: author)
}
// 3
let swift4Tutorial = createBlogPost(title: "What's new in Swift 4.2?",
author: "Cosmin Pupăză")
let swift5Tutorial = createBlogPost(title: "What's new in Swift 5?",
author: "Cosmin Pupăză")
It doesn't compile because you get the following error: "Protocol 'BlogPost' can only be used as a generic constraint because it has Self or associated type requirements".
The issue is that the Equatable protocol has a reference to Self.
To fix it, the method must be modified to return some BlogPost:
func createBlogPost(title: String, author: String) -> some BlogPost {
...
}
Implicit Returns From Single-Expression Functions
Even for single instruction functions, it's possible to omit the return keyword like for closures:
extension Sequence where Element == Int {
func addEvenNumbers() -> Int {
reduce(0) { $1.isMultiple(of: 2) ? $0 + $1 : $0 }
}
func addOddNumbers() -> Int {
reduce(0) { $1.isMultiple(of: 2) ? $0 : $0 + $1 }
}
}
Function Builders
Function builder are at the base of SwiftUI and they allow to apply a function to a list of values/expressions:
@_functionBuilder
struct SumBuilder {
static func buildBlock(_ numbers: Int...) -> Int {
return numbers.reduce(0, +)
}
}
func getSum(@SumBuilder builder: () -> Int) -> Int {
builder()
}
let gcd = getSum {
8
12
5
}
Property Wrappers
With old versions of Swift, using computed properties required some boilerplate code:
var settings = ["swift": true, "latestVersion": true]
struct Settings {
var isSwift: Bool {
get {
return settings["swift"] ?? false
}
set {
settings["swift"] = newValue
}
}
var isLatestVersion: Bool {
get {
return settings["latestVersion"] ?? false
}
set {
settings["latestVersion"] = newValue
}
}
}
var newSettings = Settings()
newSettings.isSwift
newSettings.isLatestVersion
newSettings.isSwift = false
newSettings.isLatestVersion = false
Now this gets simplified with property wrappers:
// 1
@propertyWrapper
struct SettingsWrapper {
let key: String
let defaultValue: Bool
// 2
var wrappedValue: Bool {
get {
settings[key] ?? defaultValue
}
set {
settings[key] = newValue
}
}
}
// 3
struct Settings {
@SettingsWrapper(key: "swift", defaultValue: false) var isSwift: Bool
@SettingsWrapper(key: "latestVersion", defaultValue: false)
var isLatestVersion: Bool
}
Synthesizing Default Values for Initializers in Structures
Before, Swift was not setting initial values for properties in structures by default, so you had to do it in the initializer:
struct Author {
let name: String
var tutorialCount: Int
init(name: String, tutorialCount: Int = 0) {
self.name = name
self.tutorialCount = tutorialCount
}
}
let author = Author(name: "George")
Now you can do it directly, so in some case it's possible to skip the custom initializer:
struct Author {
let name: String
var tutorialCount = 0
}
let author = Author(name: "George")
Self for Static Members
Now it's possible to use Self to reference the current class in static members:
struct Editor {
static func reviewGuidelines() {
print("Review editing guidelines.")
}
func edit() {
Self.reviewGuidelines()
print("Ready for editing!")
}
}
Creating Uninitialized Arrays
It's possible to create uninitialized arrays:
// 1
let randomSwitches = Array<String>(unsafeUninitializedCapacity: 5) {
buffer, count in
// 2
for i in 0..<5 {
buffer[i] = Bool.random() ? "on" : "off"
}
// 3
count = 5
}
Static and Class Subscripts
Now it's possible to create static subscripts:
// 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
Dynamic Member Lookup for Keypaths
It's possible to use dynamic member lookup for keypaths:
// 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
Keypaths for Tuples
It's possible to use keypaths for tuples:
// 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]
Equatable and Hashable Conformance for Weak and Unowned Properties
Now Swift deducts Equatable and Hashable conformance for classes with weak and unowned properties.
Syntactic sugar additions
Calling Types as Functions
Marking a method with callAsFunction it's possible to mark a method of a type to be called as function:
struct Quadratic {
var a: Double
var b: Double
var c: Double
func callAsFunction(_ x: Double) -> Double {
a * pow(x, 2) + b * x + c
}
}
let f = Quadratic(a: 4, b: 4, c: 3)
Note that you can have multiple callAsFunction in a class, with overloading.
Key Path Expressions as Functions
It's possible to rewrite:
orders.map { $0.email }
with:
orders.map(\.email)
Subscripts With Default Arguments
It's possible to provide default arguments to subscripts:
struct Multiplier {
subscript(x: Int, y: Int = 1) -> Int {
x * y
}
}
let multiplier = Multiplier()
multiplier[2, 3]
multiplier[4]