Swift 3 and Comparing Optionals

One of the more challenging aspects of learning and using Swift is keeping up with the changes. I wrote a post about a year and a half ago on sorting an array of dictionaries. I updated that post for the latest Swift syntax last year but I recently realised it was out of date again when Swift 3 removed the ability to directly compare two optional values.

Comparing Optionals

My problem came from some Swift 2 code that was sorting an array of dictionaries. A function (or closure) called during the sort compares two of the dictionaries and returns a boolean result:

func personSort(p1:[String:String], p2:[String:String]) -> Bool {
  if (p1["surname"] == p2["surname"]) {
    return p1["given"] < p2["given"]
  } else {
    return p1["surname"] < p2["surname"]
  }
}

The result of the dictionary lookup (e.g. p1["surname"]) is an optional that returns nil when the key is not present in the dictionary. Swift 2 allowed comparison of optional values but using Xcode 8 and Swift 3 gives an error that the value of the optional type is not unwrapped:

The Xcode Fix-it hint to force unwrap the optionals by adding ! is not great:

return p1["given"]! < p2["given"]!

Assuming that a person will always have a given name and surname is likely to be a mistake that would lead to a crash here. The best approach I could come up with was to use guards to unwrap each of the optionals, returning false if either key was missing:

func personSort(p1:[String:String], p2:[String:String]) -> Bool {
  guard let s1 = p1["surname"], let s2 = p2["surname"] else {
    return false
  }

  if s1 == s2 {
    guard let g1 = p1["given"], let g2 = p2["given"] else {
      return false
    }
    return g1 < g2
  }
  return s1 < s2
}

Note that you can still use == and != operators on optionals.

Swift 3 Removes Optional Comparison Operators

Why did Swift 3 make the change? The best I can summarise after reading Swift evolution proposal SE-0121 Remove Optional Comparison Operators is that at the moment there is no generic way to define what the expected result should be when comparing nil to a non-nil value. For example, what should the value of result be in this case:

let name1: String? = "Simpson"
let name2: String? = nil
let result = name1 < name2 // true or false?

So for now at least you need to rewrite any optional comparisons when moving to Swift 3.