Checking the value of an Optional Bool

When I want to check if an Optional Bool is true, doing this doesn't work:

var boolean : Bool? = false
if boolean{
}

It results in this error:

Optional type '@IvalueBool?' cannot be used as a boolean; test for '!= nil' instead

I don't want to check for nil; I want to check if the value returned is true.

Do I always have to do if boolean == true if I'm working with an Optional Bool?

Since Optionals don't conform to BooleanType anymore, shouldn't the compiler know that I want to check the value of the Bool?


Solution 1:

With optional booleans it's needed to make the check explicit:

if boolean == true {
    ...
}

Otherwise you can unwrap the optional:

if boolean! {
    ...
}

But that generates a runtime exception if boolean is nil - to prevent that:

if boolean != nil && boolean! {
    ...
}

Before beta 5 it was possible, but it has been changed as reported in the release notes:

Optionals no longer implicitly evaluate to true when they have a value and false when they do not, to avoid confusion when working with optional Bool values. Instead, make an explicit check against nil with the == or != operators to find out if an optional contains a value.

Addendum: as suggested by @MartinR, a more compact variation to the 3rd option is using the coalescing operator:

if boolean ?? false {
    // this code runs only if boolean == true
}

which means: if boolean is not nil, the expression evaluates to the boolean value (i.e. using the unwrapped boolean value), otherwise the expression evaluates to false

Solution 2:

Optional binding

Swift 3 & 4

var booleanValue : Bool? = false
if let booleanValue = booleanValue, booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

Swift 2.2

var booleanValue : Bool? = false
if let booleanValue = booleanValue where booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

The code let booleanValue = booleanValue returns false if booleanValue is nil and the if block does not execute. If booleanValue is not nil, this code defines a new variable named booleanValue of type Bool (instead of an optional, Bool?).

The Swift 3 & 4 code booleanValue (and Swift 2.2 code where booleanValue) evaluates the new booleanValue: Bool variable. If it is true, the if block executes with the newly defined booleanValue: Bool variable in scope (allowing the option to reference the bound value again within the if block).

Note: It's a Swift convention to name the bound constant/variable the same as the optional constant/variable such as let booleanValue = booleanValue. This technique is called variable shadowing. You could break from convention and use something like let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue. I point this out to help understand what's happening. I recommend using variable shadowing.

 

Other Approaches

Nil coalescing

Nil coalescing is clear for this specific case

var booleanValue : Bool? = false
if booleanValue ?? false {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

Checking for false is not as clear

var booleanValue : Bool? = false
if !(booleanValue ?? false) {
    // executes when booleanValue is false
    print("optional booleanValue: '\(booleanValue)'")
}

Note: if !booleanValue ?? false does not compile.

 

Force unwrapping optional (avoid)

Force unwrapping increases the chance that someone will make a change in the future that compiles but crashes at runtime. Therefore, I would avoid something like this:

var booleanValue : Bool? = false
if booleanValue != nil && booleanValue! {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

 

A General Approach

Though this stack overflow question asks specifically how to check if a Bool? is true within an if statement, it's helpful to identify a general approach whether checking for true, false or combining the unwrapped value with other expressions.

As the expression gets more complicated, I find the optional binding approach more flexible and easier to understand than other approaches. Note that optional binding works with any optional type (Int?, String?, etc.).

Solution 3:

var enabled: Bool? = true

if enabled == true {
    print("when is defined and true at the same moment")
}

if enabled == false {
    print("when is defined and false at the same moment")
}

if let enabled = enabled, enabled == true {
    print("when is defined and true at the same moment")
}

if let enabled = enabled, enabled == false {
    print("when is defined and false at the same moment")
}

if let enabled = enabled, enabled {
    print("when is defined and true at the same moment")
}

if let enabled = enabled, !enabled {
    print("when is defined and false at the same moment")
}

if enabled ?? false {
    print("when is defined and true at the same moment")
}

if enabled == .some(true) {
    print("when is defined and true at the same moment")
}

if enabled == (true) {
    print("when is defined and true at the same moment")
}

if case .some(true) = enabled {
    print("when is defined and true at the same moment")
}

if enabled == .some(false) {
    print("when is defined and false at the same moment")
}

if enabled == (false) {
    print("when is defined and false at the same moment")
}

if enabled == .none {
    print("when is not defined")
}

if enabled == nil {
    print("when is not defined")
}