The Swift guard Statement - Andy Bargh (2023)

The Swift guard Statement - Andy Bargh (1)The Swift guard Statement - Andy Bargh (2)

The Swift guard Statement - Andy Bargh (3)

Newly introduced in Swift 2.0, the Swift guard statement brings the power of the nightclub doorman to the Swift language and sits somewhere between the bulk-standard if statement, and the more stringent and unforgiving assert statement.

Along with the guard statement comes the promise of cleaner, more explicit and a more elegant code and in this article I want to find out whether it lives up to these promises.

Table of Contents

The Example

In order to investigate, I’m going to use an (admittedly contrived) example.

Basically, it’s a function that accepts three optional Int values and performs a simple calculation on those values if, and only if, all the values are both non-nil and positive. In all other cases the function will return nil.

In reality, the exact nature of the function isn’t important. What is, is the structure it provides and in this article, I’ll use it to investigate the evolution of the Swift language that has lead to the introduction of the guard statement as well as looking at the guard statement itself in terms of both syntax and usage. So on with the history lesson.

Swift 1.0

if-let-else

When Swift was originally released, working with optionals wasn’t ideal.

We had to check each optional value individually to ensure that they contained a value and only once all the checks were complete could we actually use those values in a calculation.

Optional binding helped, but it had its own problems.

For example, using optional binding, our example function at that time would have looked something like this:

func fooSwiftWhenFirstReleased(a : Int?, b: Int?, c:Int?) -> Int? { if let a = a { if let b = b { if let c = c { if a > 0 && b > 0 && c > 0 { return a * b * c } } else { return nil } } else { return nil } } else { return nil }}

Code like this was common.

The pattern of ever increasing nesting that optional binding introduced, led to the nickname The Swift Pyramid of Doom and as you can see, it wasn’t pretty.

It was not uncommon to go hunting through multiple levels of nested if statement to decipher exactly what a function was doing and with all those braces and nested if statements everywhere identifying the primary path could be particularly tricky.

On top of this, because one or more of the optional bindings could fail, we had to return an optional from the function. This placed additional responsibilities onto the person using the function to check whether the return value was nil, further exacerbating the Pyramid of Doom problem.

Swift 1.2

Binding of Multiple Optionals on a Single Line

Swift 1.2 brought improvements though.

One of the major improvements was the ability to perform binding of multiple optionals on a single line.

In this case, Swift would evaluate each binding in turn and stop if any attempted binding was nil:

func fooMultiBinding(a : Int?, b: Int?, c:Int?) -> Int? { if let a = a, b = b, c = c { if a > 0 && b > 0 && c > 0 { return a * b * c } else { return nil } } else { return nil }}

As you can see, this new capability all but eliminated the Swift Pyramid of Doom but still left the main flow through the function buried within a couple of levels of nested if statement.

Swift 1.2 did however provide us with another card we could play.

The where Clause

In Swift 1.2 we were also able to combine the binding of multiple optionals with an additional boolean where clause within which we could place additional constraints on the values unwrapped in the binding.

With this where clause then, not only did the optional bindings have to complete successfully, but the boolean expression after the where had to evaluate to true for the firstbranch of the if statement to be executed:

func fooMultiBindingWithWhere(a : Int?, b: Int?, c:Int?) -> Int? { if let a = a, b = b, c = c where a > 0 && b > 0 && c > 0 { return a * b * c } else { return nil }}

Combining the optional binding with a where clause saved us a level of nesting but still left the primary path through our code buried within abranch of the if statement though.

Although Swift 1.2 was close then, we still weren’t quite there. With the newly introduced features ofSwift 2.0 though, we might just have a solution.

Swift 2.0

The Swift guard Statement

Swift 2.0, was released in June this year, and with it, came a new guard statement that has gone along way to solve the issues that remained inSwift 1.2.

The guard Statement Syntax

The general structure for the guard statement is as follows:

guard (conditionThatNeedsToBeTrue) else { // return, break, continue or throw or a call to a method // that doesn't return.}

The guard statement is similar to an if statement in that the statements within the associated block are execute based on the results of a boolean expression. That boolean expression might contain normal variables or constants or may contain the results of optionally bound values in similar fashion to an if-let statement. There are some subtle differences though.

Unlike the if or if-let statements, if the guard statements conditions are met, execution continues after the closing brace of the guard statement and if the conditions are not met, the guard statements mandatory else clause is executed.

It’s kind of the if and if-let statements in reverse. And the else clause is no ordinary else clause either.

In a guard statement, Swift places an additional requirement on the else clause that, if executed, it must exist the current scope using one of Swifts control transfer statements.

This can mean using either a return to leave a function, a continue or break to leave a loop, a throw to raise an exception or a call to a function that doesn’t return like fatalError() (which will also crash your app). Either way, the else clause must somehow exit the current scope.

You can think of a guard statement then, as more like an assert instead of an if.

Instead of immediately crashing your application as an assert would, the guard statement mandates that we exist the current scope but gives us a chance to do so in a more graceful manner.

The guard statement also works with optional binding too. Let’s re-write the example using the new guard statement then.

func fooUsingGuard(a : Int?, b: Int?, c:Int?) -> Int? { guard let a = a, b = b, c = c where a > 0 && b > 0 && c > 0 else { return nil } return a * b * c}

As you can see, the guard statement allows us to simplify the code a little further.

It does so by changing the way we think about and deal with condition checking.

The Bouncer Pattern

One of the things the guard statement does it so guide us towards checking values at the start of our function and exiting early if the values don’t meet any desired criteria.

This pattern of early check, early exit, is sometimes called the Bouncer Pattern and is based around the idea of checking values at the entry to the function (similar to a bouncer checking people as then enter a nightclub) and ejecting them if they don’t meet the desired criteria. Essentially it’s the code version of “If you’re names not down, you’re not coming in.”

Using the Bouncer Pattern helps keep the code that handles any broken conditions close to the condition check itself (rather than buried away in the else clause of an if statement somewhere) but also makes the code cleaner by leaving us to focus on the primary path through our code in the latter parts of the function body.

On top of this, the guard statement also has another major trick up its sleeve.

The guard Statement and Optional Binding

When combined with optional binding, as with the normal if-let syntax, one or more optional values are checked and automatically unwrapped. Now this is nothing new. You’ve seen optional binding before so you know how they work. But remember the point I mentioned earlier.

In the case of the guard statement, the true path is everything after the closing brace of the guard statement so when used with optional binding, instead of the bound optional values being available inside the following block of code as they would be when bound using the if-let syntax, they are instead available within rest of the function or block in which the guard statement was called (in this case the top-level scope of the function). This allows us to make use of the unwrapped values at any point after the end of the guard statement and allows us to do so without the need for any forced unwrapping.

The helps enhance the clarity of our code and keeps the primary flow through our function at, or at least close to, the top-level scope making it much easier to read and decipher. Never a bad thing.

Top Articles
Latest Posts
Article information

Author: Aracelis Kilback

Last Updated: 01/28/2023

Views: 6014

Rating: 4.3 / 5 (64 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Aracelis Kilback

Birthday: 1994-11-22

Address: Apt. 895 30151 Green Plain, Lake Mariela, RI 98141

Phone: +5992291857476

Job: Legal Officer

Hobby: LARPing, role-playing games, Slacklining, Reading, Inline skating, Brazilian jiu-jitsu, Dance

Introduction: My name is Aracelis Kilback, I am a nice, gentle, agreeable, joyous, attractive, combative, gifted person who loves writing and wants to share my knowledge and understanding with you.