What's new in Swift 5, 5.1 and 5.2

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]