Guard statement in Swift iOS 08.01.2019

When your code needs to decide whether to exit early, Swift provides a special syntax — the guard construct. In effect, a guard construct is an if construct where you exit early if the condition fails.

guard condition else {
    statements
    exit
}

A guard construct consists solely of a condition and an else block. The else block must jump out of the current scope, by any of the means that Swift provides, such as return, break, continue, throw, or fatalError — anything that guarantees to the compiler that, in case of failure of the condition, execution absolutely will not proceed within the block that contains the guard construct.

An elegant consequence of this architecture is that, because the guard construct guarantees an exit on failure of the condition, the compiler knows that the condition has succeeded after the guard construct if we do not exit. Thus, a conditional binding in the condition is in scope after the guard construct, without introducing a further nested scope. For example:

guard let s = optionalString else {return}
// s is now a String (not an Optional)

It’s not uncommon to have a series of guard constructs, one after another. This may seem a rather clunky and imperative mode of expression. It’s a nice alternative to a single elaborate if construct, or to the "pyramid of doom". It looks like exactly what it is, a sequence of gates through which the code must pass in order to proceed further.

@objc func tapField(_ g: Any) {
    // g must be a gesture recognizer
    guard let g = g as? UIGestureRecognizer else {return}
    // and that gesture recognizer must have a view
    guard g.view != nil else {return}
    // okay, now we can proceed...
}

It’s often possible to combine multiple guard statement conditions into a single condition list:

@objc func tapField(_ g: Any) {
    // g must be a gesture recognizer
    // and that gesture recognizer must have a view
    guard let g = g as? UIGestureRecognizer, g.view != nil
    else {return}
    // okay, now we can proceed...
}

A guard construct will also come in handy in conjunction with try?. Let’s presume we can’t proceed unless String(contentsOfFile:) succeeds. Then we can call it like this:

let f = // path to some file, maybe
guard let s = try? String(contentsOfFile: f) else {return}
// s is now a String (not an Optional)

There is also a guard case construct, forming the logical inverse of if case. To illustrate, we’ll use MyError enum:

enum MyError : Equatable {
    case number(Int)
    case message(String)
    case fatal
}

let num = 4
let err : MyError = .number(num)

guard case let .number(num) = err else {return}
// n is now the extracted number

guard case helps to solve an interesting problem. Suppose we have a function whose returned value we want to check in a guard statement:

guard howMany() > 10 else {return}

All well and good, but suppose also that in the next line we want to use the value returned from that function. We don’t want to call the function again; it might be time-consuming and it might have side effects. We want to capture the result of calling the function and pass that captured result on into the subsequent code. But we can’t do that with guard let, because that requires an Optional, and our function howMany doesn’t return an Optional.

What should we do? guard case to the rescue:

guard case let output = howMany(), output > 10 else {return}
// now output is in scope

Note that a guard construct’s conditional binding can’t use, on the left side of the equal sign, a name already declared in the same scope. This is illegal:

let s = // ... some Optional
guard let s = s else {return} // compile error

The reason is that guard let, unlike if let and while let, doesn’t declare the bound variable for a nested scope; it declares it for this scope. Thus, we can’t declare s here because s has already been declared in the same scope.