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ă.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

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:

  1. Mark File as @dynamicMemberLookup to enable dot syntax for custom subscripts.
  2. Create a static subscript that returns the default or custom path for File.
  3. Define the class version of the previous subscript using dynamic member lookup.
  4. Call both subscripts with the corresponding syntax.
Note: Want to learn more about subscripts in Swift? Check out the subscripts tutorial: Custom Subscripts in Swift.

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:

  1. Declare x and y for Point.
  2. Annotate Circle with @dynamicMemberLookup to enable dot syntax for its subscripts.
  3. Create a generic subscript which uses keypaths to access center properties from Circle.
  4. Call center properties on circle using dynamic member lookup instead of keypaths.
Note: Need more details about how dynamic member lookup works in Swift? Check out the dynamic features tutorial: Dynamic Features in Swift.

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:

  1. Declare brand, year and details for Instrument.
  2. Use keypaths to get type and pitch from details in instrument.

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:

  1. Define different styles for TutorialStyle.
  2. Swift fires a warning since it’s not clear to the compiler what .none means in this case: Optional.none or TutorialStyle.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:

  1. Declare all possible states for TutorialStatus.
  2. 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.

Note: Want to learn more about pattern matching in Swift? Check out the pattern matching tutorial: Pattern Matching in Swift.

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.