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")
}