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.