Checking the value of an Optional Bool

后端 未结 6 528
情书的邮戳
情书的邮戳 2021-01-30 10:13

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:

相关标签:
6条回答
  • 2021-01-30 10:31

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

    0 讨论(0)
  • 2021-01-30 10:32

    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

    0 讨论(0)
  • 2021-01-30 10:33
    var enabled: Bool? = true
    
    if let enabled = enabled, enabled == true {
        print("when is defined and true 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")
    }
    
    0 讨论(0)
  • 2021-01-30 10:34

    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.

    0 讨论(0)
  • 2021-01-30 10:35

    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
    }
    
    0 讨论(0)
  • 2021-01-30 10:47

    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"
    
    
    0 讨论(0)
提交回复
热议问题