Conditional optional checks

I have two optional properties and would like to know how to elegantly unwrap them to perform a conditional check.

There is the bang operator solution, which I would prefer to avoid:

    let optionalOne: Int?
    let optionalTwo: Int?

    if oneCheck != nil && oneCheck > 0 || twoCheck != nil && twoCheck > 0 {}

And the lengthy if let check:

    let optionalOne: Int?
    let optionalTwo: Int?


    if let oneCheck = optionalOne {
        if let twoCheck = optionalTwo {
            if oneCheck > 0 || twoCheck > 0 {
            }
        }
    }

Is there a more elegant Swift like solution than the two options above please ?

You can combine multiple if clauses into a single level!

if let oneCheck = optionalOne,
   let twoCheck = optionalTwo,
   oneCheck > 0 || twoCheck > 0 {
    // …
}

ETA: though it’s worth noting, your two examples aren’t actually equivalent—the version I’ve posted here only satisfies the condition if both optional shave a value, at least one of which is greater than 0, but your first example will satisfy the condition if at least one of the optional has a value and is greater than 0, which is subtly different behavior.

3 Likes

You could use the nil coalescing operator like so:

if (optionalOne ?? -1) > 0
|| (optionalTwo ?? -1) > 0 {}
3 Likes

I've never seen this before, interesting.

BTW, I do not follow compiler logic here:

var v: Int? = nil

func test() {
    v == 0  // can do this
    v != 0  // and this
    v > 0   // but not that?
    // Error: Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
}

The first two use optional promotion, and are comparing two optionals for equality.

The third line cannot do the same because there is no “>” operator for Optional.

3 Likes

I can do this myself:

extension Optional where Wrapped: Comparable {
    static func > (a: Self, b: Self) -> Bool {
        switch (a, b) {
        case (.some(let x), .some(let y)):
            return x > y
        default:
            return false
        }
    }
}

and I guess something like this is already done for == and !=.

1 Like

…right.

You said you didn’t understand the “compiler logic”, so I explained it.

The compiler rejects the code because no such operator exists.

If you are wondering why there is no such operator, that’s not a question about the compiler.

1 Like

The reason there is no such operator is that it was removed from Swift in version 3. At the time, conditional conformances were not possible, but the principal reason for the removal of these operators stands even today:

If nil can be compared with non-nil, then it has to be either less than any non-nil value or greater than every non-nil value (or else it breaks the semantic guarantees of Comparable like NaN, which we very much wouldn’t want to do)—either choice can cause non-intuitive behavior in generic contexts when users do not anticipate optional promotion.

4 Likes