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