Most elegant way to compare two optionals in Swift

Viewed 12772

I have two AnyObject? variables that I would like to compare for reference equality:

var oldValue: AnyObject?
var newValue: AnyObject?
...
if oldValue != newValue {
    changed = true
}

This doesn't work though, as I apparently cannot compare two optionals directly. I want the behavior as if I were comparing ids in Objective-C, that is:

  • true if both are nil
  • true if both have a value and the values are also equal
  • false otherwise

Is there an elegant way to write this in Swift (ideally without having to write a custom extension)?

This is the best I've come up with:

if !(oldValue != nil && newValue != nil && oldValue == newValue)

Not very pretty. :(

5 Answers

I liked @Keith's solution. But I think it is not written in Swift 4, as I can not compile it with Swift 4 compiler.

So I have converted his code to Swift 4 version here.

Remember, if you're using higher version of Swift language than Swift 4.1, then this answer is of no need as it provides this feature by default. You can refer here for more details.

Swift 4 version of @Keith's code:

infix operator ==? : ComparisonPrecedence

func ==? <T: Comparable>(lhs: T?, rhs: T?) -> Bool {
    if let lhs = lhs, let rhs = rhs {
        return lhs == rhs
    } else {
        return lhs == nil && rhs == nil
    }
}

func ==? <T: AnyObject>(lhs: T?, rhs: T?) -> Bool {
    if let lhs = lhs, let rhs = rhs {
        return lhs === rhs
    } else {
        return lhs == nil && rhs == nil
    }
}

You can overload == operator for some Comparable type

public func ==<T: SomeType>(lhs: T?, rhs: T?) -> Bool {
    switch (lhs,rhs) {
    case (.some(let lhs), .some(let rhs)):
        return lhs == rhs
    case (.none, .none):
        return true
    default:
        return false
    }
}

Or use === comparison for AnyObject, though, personally I would prefer not to use AnyObject in the first place.

Related