Swift Optional Pattern Matching

A quick tip on using the Swift Optional pattern when you want to match on optional types in a switch of case-let statement.

Optional Pattern

Swift implements optional types as an enum with two cases, .none and .some(Wrapped). It’s often not necessary but we can switch on an optional type:

let capital: String? = "London"

switch capital {
case .none:
  print("value is nil")
case .some(let capital):
  print("value is \(capital)")
}

// value is London

The Optional pattern is an identifier followed by a question mark. We can use it to replace enumeration case patterns such as .some(let capital) with let capital?:

switch capital {
case .none:
    print("value is nil")
case let capital?:
    print("value is \(capital)")
}

// value is London

The optional pattern can be useful when using the always tricky if-case-let syntax with collections of optionals:

let capitals = ["Paris", "Rome", nil, "Madrid"]
for case let capital? in capitals {
    print(capital)
}
// Paris
// Rome
// Madrid

Or a recent example where I needed the order of two optional strings. One way to do that is to switch on the tuple of optional strings:

func compare(_ lhs: String?, _ rhs: String?) -> ComparisonResult {
    switch (lhs, rhs) {
    case (.none, .none): return .orderedSame
    case (.some, .none): return .orderedDescending
    case (.none, .some): return .orderedAscending
    case let (lhs?, rhs?):
        if lhs == rhs { return .orderedSame }
        return lhs < rhs ? .orderedAscending : .orderedDescending
    }
}

The Optional pattern used when both strings have a value is, in my opinion, a more readable choice:

// case (.some(let lhs), .some(let rhs)):
case let (lhs?, rhs?):

Note in this case that I decided I wanted the nil values to come first:

compare(nil, "Paris") // .orderedAscending
compare("Paris", nil) // .orderedDescending