Using the Swift if let with logical AND operator &&

SwiftExpression

Swift Problem Overview


We know that we can use an if let statement as a shorthand to check for an optional nil then unwrap.

However, I want to combine that with another expression using the logical AND operator &&.

So, for example, here I do optional chaining to unwrap and optionally downcast my rootViewController to tabBarController. But rather than have nested if statements, I'd like to combine them.

if let tabBarController = window!.rootViewController as? UITabBarController {
    if tabBarController.viewControllers.count > 0 {
        println("do stuff")
     }
 }

Combined giving:

if let tabBarController = window!.rootViewController as? UITabBarController &&
    tabBarController.viewControllers.count > 0 {
        println("do stuff")
     }
}

The above gives the compilation error Use of unresolved identifier 'tabBarController'

Simplifying:

if let tabBarController = window!.rootViewController as? UITabBarController && true {
   println("do stuff")
}

This gives a compilation error Bound value in a conditional binding must be of Optional type. Having attempted various syntactic variations, each gives a different compiler error. I've yet to find the winning combination of order and parentheses.

So, the question is, is it possible and if so what is correct syntax?

Note that I want to do this with an if statement not a switch statement or a ternary ? operator.

Swift Solutions


Solution 1 - Swift

As of Swift 1.2, this is now possible. The Swift 1.2 and Xcode 6.3 beta release notes state:

> More powerful optional unwrapping with if let — The if let construct > can now unwrap multiple optionals at once, as well as include > intervening boolean conditions. This lets you express conditional > control flow without unnecessary nesting.

With the statement above, the syntax would then be:

if let tabBarController = window!.rootViewController as? UITabBarController where tabBarController.viewControllers.count > 0 {
        println("do stuff")
}

This uses the where clause.

Another example, this time casting AnyObject to Int, unwrapping the optional, and checking that the unwrapped optional meets the condition:

if let w = width as? Int where w < 500
{
    println("success!")
}

For those now using Swift 3, "where" has been replaced by a comma. The equivalent would therefore be:

if let w = width as? Int, w < 500
{
    println("success!")
}

Solution 2 - Swift

In Swift 3 Max MacLeod's example would look like this:

if let tabBarController = window!.rootViewController as? UITabBarController, tabBarController.viewControllers.count > 0 {
    println("do stuff")
}

The where was replaced by ,

Solution 3 - Swift

Max's answer is correct and one way of doing this. Notice though that when written this way:

if let a = someOptional where someBool { }

The someOptional expression will be resolved first. If it fails then the someBool expression will not be evaluated (short-circuit evaluation, as you'd expect).

If you want to write this the other way around it can be done like so:

if someBool, let a = someOptional { }

In this case someBool is evaluated first, and only if it evaluates to true is the someOptional expression evaluated.

Solution 4 - Swift

Swift 4, I will use,

let i = navigationController?.viewControllers.index(of: self)
if let index = i, index > 0, let parent = navigationController?.viewControllers[index-1] {
    // access parent
}

Solution 5 - Swift

It is not possible.

From Swift grammar

> GRAMMAR OF AN IF STATEMENT > > if-statement → if ­if-condition­ code-block­ else-clause­opt­ > > if-condition → expression­ | declaration­ > > else-clause → else­ code-block­ | else­ if-statement­

>The value of any condition in an if statement must have a type that conforms to the BooleanType protocol. The condition can also be an optional binding declaration, as discussed in Optional Binding

if-condition must be expression­ or declaration­. You can't have both expression and declaration.

let foo = bar is a declaration, it doesn't evaluate to a value that conforms to BooleanType. It declares a constant/variable foo.

Your original solution is good enough, it is much more readable then combining the conditions.

Solution 6 - Swift

if let tabBarController = window!.rootViewController as? UITabBarController ,
               tabBarController.viewControllers?.count ?? 0 > 0 {
                    println("do stuff")   
              }
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMax MacLeodView Question on Stackoverflow
Solution 1 - SwiftMax MacLeodView Answer on Stackoverflow
Solution 2 - Swiftph1lb4View Answer on Stackoverflow
Solution 3 - SwiftparView Answer on Stackoverflow
Solution 4 - SwiftSazzad Hissain KhanView Answer on Stackoverflow
Solution 5 - SwiftBryan ChenView Answer on Stackoverflow
Solution 6 - SwiftMuhammad NaveedView Answer on Stackoverflow