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.