Using the Swift if let with logical AND operator &&
SwiftExpressionSwift 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-clauseopt > > 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")
}
}