108

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?

Moon Cat
  • 2,029
  • 3
  • 20
  • 25
  • Since Booleans conform to the Equatable protocol, then you can compare an optional to a non-optional. See [here](https://stackoverflow.com/questions/47059252/why-swift-compiler-behaves-differently-with-equality-operator-with-without-equat/47059497#47059497) – mfaani Nov 09 '17 at 21:43

6 Answers6

222

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

karim
  • 15,408
  • 7
  • 58
  • 96
Antonio
  • 71,651
  • 11
  • 148
  • 165
  • 4
    The third option is the preferred solution because it is the best way to express the intention of the code. Using `if let` would also work. – Sulthan Aug 27 '14 at 09:30
  • @Sulthan I agree that the 3rd is easier to read. Probably `let` is not as good, because it just tells you whether the optional has a value or not - you should add a nested if to check whether the unwrapped is true or not – Antonio Aug 27 '14 at 09:33
  • 37
    A variant of the third option, using the *"nil coalescing operator ??":* `if boolean ?? false { ... }` . – Martin R Aug 27 '14 at 09:42
  • 6
    But if I want to negate it it starts to look ridiculous: `if !(boolean ?? true) { ... }` :( – Andreas May 27 '15 at 18:55
  • 4
    Forced unwrap an utmost bad idea. It should always be avoided. – Matthieu Riegler Jun 10 '16 at 08:54
  • Nothing wrong with forced unwrap if it is used correctly. The third solution is safe and by far the simplest way to make 1 liners for booleans. – Warpzit Apr 18 '17 at 09:49
  • 1
    For completeness, another option: `if case .some(true) = boolean { ... }` – pgmarchenko Mar 18 '18 at 08:53
  • 6
    What's wrong with the first option? To me it seems like the best way. – Vahid Amiri Jul 01 '18 at 20:22
  • @VahidAmiri First is just ok to use anyway, not need to think about others if it gets complicated like this.... – Renetik Jan 19 '19 at 22:59
49

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.).

Mobile Dan
  • 6,444
  • 1
  • 44
  • 44
  • I am having difficulty using Boolean expressions with an optional for while loops. The nil coalescing operator works, but it is messy and error prone. Is there a way to use `if let`? – jbaraga Sep 24 '16 at 16:16
  • @jbaraga, please post an example of the while loop you are wondering about. – Mobile Dan Sep 24 '16 at 20:48
  • In using an array as a stack, I want to pop off values until a condition is met, or the stack is empty. For example, `while array.last < threshold { array.removeLast() }` – jbaraga Sep 24 '16 at 21:47
  • You can accomplish that stack processing with `if, let, where` using this: `while let last = array.last where last < threshold { array.removeLast() }` in Swift 2 or `while let last = array.last, last < threshold { array.removeLast() }` in Swift 3. – Mobile Dan Sep 26 '16 at 05:06
  • That is better, thanks. I was not aware of `while let`. – jbaraga Sep 28 '16 at 02:38
  • I don't get it. What's wrong with this? `var optionalBool :Bool? = false; if (optionalBool == false){ print("is false"); }` doesn't require any unwrapping... – mfaani Nov 09 '17 at 20:42
  • Since Booleans conform to the `Equatable` protocol, then, you can compare an optional to a non-optional. See [here](https://stackoverflow.com/a/47059497/5175709) – mfaani Nov 09 '17 at 21:11
  • @Honey, Great question. It's code style. `if optionalBool == false { ...` works but it is inconsistent with the convention for evaluating a non-optional boolean which is like this `if nonOptionalBool { ...`. I prefer the approach that aligns with the standard Swift coding conventions but there are many approaches that work in this case. – Mobile Dan Nov 09 '17 at 21:32
9
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")
}
Blazej SLEBODA
  • 8,936
  • 7
  • 53
  • 93
1

I found another solution, overloading the Boolean operators. For example:

public func < <T: Comparable> (left: T?, right: T) -> Bool {
    if let left = left {
        return left < right
    }
    return false
}

This may not be totally in the "spirit" of the language changes, but it allows for safe unwrapping of optionals, and it is usable for conditionals anywhere, including while loops.

jbaraga
  • 656
  • 5
  • 16
  • 1
    Sorry, looking back at the original post, it does not answer that specific question, but rather the question I raised in my earlier comment. – jbaraga Sep 24 '16 at 21:56
  • I'd be very careful about using this overload, because there may be cases where you don't want nil to be treated as "greater than" a non-nil value (you may want the opposite result in certain contexts, or possibly alternative handling entirely). Using normal unwrapping instead forces you to explicitly address how you want to handle nils in every case, so you're less likely to run into unexpected results. – John Montgomery May 30 '18 at 21:11
1

The answer I found most easy to read is to define a function. Not very complicated but does the work.

func isTrue(_ bool: Bool?) -> Bool {
    guard let b = bool else {
        return false
    }
    return b
}

usage:

let b: Bool? = true
if isTrue(b) {
    // b exists and is true
} else {
    // b does either not exist or is false
}
LuLuGaGa
  • 13,089
  • 6
  • 49
  • 57
Charel ctk
  • 13
  • 2
1

As Antonio said

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.

I spent a few hours trying to understand a line of code I stumbled upon, but this thread put me on the right track.

This quote is from august 2014, and since then Apple introduced Never following proposal SE-0102 and latter made it conform to Equatable, Hashable, Error and Comparable

It is now possible to check if a boolean is nil using Never? :


var boolean: Bool? = false
boolean is Never? // false
boolean = true
boolean is Never? // false
boolean = nil
boolean is Never? // true

You can actually use any other uninhabitable types :

public enum NeverEver { }
var boolean: Bool? = false
boolean is NeverEver? // false
boolean = true
boolean is NeverEver? // false
boolean = nil
boolean is NeverEver? // true

That being said, it's also possible to use a property wrapper now :

@propertyWrapper struct OptionalBool {
    public var wrappedValue: Bool?
    public var projectedValue: Bool { wrappedValue ?? false }
    public init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate {
            return "predicate is true"
        }
        return "predicate is false"
    }
}

var object = Struct()
object.description // "predicate is false"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"

or even:

@propertyWrapper struct OptionalBool {
    var wrappedValue: Bool?
    var projectedValue: OptionalBool { self }
    var isNil: Bool { wrappedValue is Never? }
    var value: Bool { wrappedValue ?? false }
    
    init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate.value {
            return "predicate is true"
        }
        if !$predicate.isNil {
            return "predicate is false"
        }
        return "predicate is nil"
    }
}

var object = Struct()
object.description // "predicate is nil"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"

AnderCover
  • 2,488
  • 3
  • 23
  • 42