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.

Never miss a post!

iOS Size Classes Cheat Sheet

Subscribe and get my free iOS Size Classes Cheat Sheet

Success! Now check your email to confirm your subscription and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
No time to watch WWDC videos?

Sign up to get my iOS posts direct to your inbox and I will send you a free PDF of my iOS Size Classes Cheat Sheet.

OK! Check your inbox (or spam folder) for an email to confirm your details and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
Archives Categories