10

I am looking for a way to evaluate a Swift Bool concisely in a single if statement, when the Bool is the property of an optional object:

var objectWithBool: ClassWithBool?
// ...

if let obj = objectWithBool {
    if obj.bool {
        // bool == true
    } else {
        // bool == false
    }
} else {
    // objectWithBool == nil
}

Is there are way to combine these if statements? In Objective-C this could easily be done, as a nil object can be evaluated in the same expression:

if (objectWithBool.bool) {
    // bool == true
} else {
    // bool == false || objectWithBool == nil
}
Stuart
  • 36,683
  • 19
  • 101
  • 139

3 Answers3

17

Ah, found it:

if objectWithBool?.bool == true {
    // objectWithBool != nil && bool == true
} else {
    // objectWithBool == nil || bool == false
}

The optional chaining expression objectWithBool?.bool returns an optional Bool. Since it is optional, that expression alone in the if statement would be evaluated to true/false based on whether the optional contains a value or not.

By using the == operator the if statement checks the optional's value, which in this case can be true, false, or nil.

Stuart
  • 36,683
  • 19
  • 101
  • 139
  • I would not say that it *"forces the optional Bool to be evaluated"*. `objectWithBool?.bool` can be `true`, `false` or `nil` (`== Optional.None`), and the comparison `== true` yields `true` only in the first case. – Martin R Nov 13 '14 at 20:23
  • 1
    @MartinR True, I was a bit hasty there. I've expanded the explanation a bit - hopefully it makes more sense now. – Stuart Nov 13 '14 at 21:53
4

Another possible solution is:

if objectWithBool?.bool ?? false {
    println("objectWithBool != nil && objectWithBool.bool == true")
} else {
    println("objectWithBool == nil || objectWithBool.bool == false")
}

The "nil coalescing operator" a ?? b is a shorthand for

a != nil ? a! : b
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks @MartinR. I like the nil coalescing operator, it can produce some really neat code. I'd argue that here it's slightly less clear than checking for equality with `true`, since at a glance the presence of `false` might look like the logic is the other way around. Good idea though! – Stuart Nov 13 '14 at 15:11
  • @Stuart: That depends on how you look at it. Coming from (plain) C, I prefer `if boolVar {}` to `if boolVar == true {}` and then `?? false` acts as a default value. And it works similarly for other types, for example `if (myArray?.count ?? 0) > 0`. – So it is just a matter of personal preference :) – Martin R Nov 13 '14 at 20:20
  • You're right, and I also prefer `if boolVar {}` where possible. I suppose I mean it could be confusing in the naïve sense that at a glance one might mistakenly think the expression is checking for _equality_ with `false`. I'll probably change my mind when I get more accustomed to reading the `??` operator :) – Stuart Nov 13 '14 at 21:30
0

You could also do :

if let obj = objectWithBool where obj {
    // objectWithBool != nil && obj == true
} else {
   // objectWithBool == nil || obj == false
}
Nyakiba
  • 862
  • 8
  • 18
  • This is useful if you need to use the unwrapped `Bool` inside the `if` block, otherwise you should discard the variable with `if let _ = objectWithBool ...`, or use one of the other answers. Also, note that in Swift 3 the syntax has changed to `if let obj = objectWithBool, obj { ... } else { ... }` – Stuart Oct 26 '16 at 15:48