Guard Statement Explained in Swift

Written by LearnAppMaking on March 4 2021 in App Development, Swift

Guard Statement Explained in Swift

The guard statement in Swift helps you return your functions early, if a condition isn’t satisfied. In this tutorial we’ll discuss what guard is, and how you can use it in practical iOS development.

Here’s what we’ll get into:

  • How guard works and when it’s smart to use
  • How to unwrap optionals with guard let
  • Why it’s important to “early return” a function
  • Using guard in practical iOS development

Ready? Let’s get started!

  1. How To Use “guard” in Swift
  2. Early Returns – Why?
  3. Unwrap Optionals with “guard let”
  4. Using “guard” in Practical iOS Development
  5. Further Reading

How To Use “guard” in Swift

You use the guard statement in Swift to (early) return a function when a condition isn’t satisfied. It’s similar to an if statement.

Let’s take a look at an example:

func sqrt(number: Int) -> Int?
{
    guard number >= 0 else {
        return nil
    }

    // Calculate and return square root of `number`
    return ···
}

In the above code, a hypothetical function sqrt(number:) calculates the square root of a given number and returns it. For example, the square root of 144 is 12, because 122 = 144.

You can’t calculate the square root of a negative number, so the sqrt(number:) function checks that number is greater than or equal to 0 by using the guard statement.

Here’s that guard statement once more:

guard number >= 0 else {
    return nil
}

The syntax for guard in Swift is guard condition else { ··· }.

It’s easiest to compare guard to a conditional, i.e. an if statement, that evaluates a logical expression.

In the above code, we’re checking if number >= 0. If it’s not, i.e. number is negative, the else clause of the guard block is invoked. This will return nil to the caller of the function, and the function exits.

It’s easiest to read the above code as: “Guard that number is greater than or equal to zero, or else_, return nil.”_ See how that reads quite naturally?

“Guard that … is true, else, do this.”

If you look closely, you’ll notice that the guard statement is the opposite of an if statement. This doesn’t do guard justice, but it’s important to point out. Here, take a look at this:

if number < 0 {
    return nil
}

Here’s another one:

if number >= 0 {
    // Do something...
} else {
    return nil
}

See how these guard and the if statements have the same effect? The else clause of the guard statement is invoked when number >= 0 evaluates to false.

The guard statement can only be used to “transfer program control out of scope”. This is a fancy way of saying that the body of the guard block, i.e. what’s inside the squiggly brackets, needs to exit the function (or loop).

You can do so with the following statements:

  • return, to exit a function or closure
  • break, to exit a loop like for
  • continue, to continue to the next loop iteration
  • throw, to exit a function and throw an error value

It’s worth noting here that, next to using guard in functions and closures, you can also use it in loops, like with for in.

Here’s an example:

for i in 1...10 
{
    guard i.isMultiple(of: 2) else {
        continue
    }

    print(i)
}

In the above code we’re using the guard statement to check that an integer i is even, i.e. a multiple of 2. If it’s not, the continue statement causes the loop to continue to the next iteration.

Thanks to that bit of wizardry we can “guard” or “make sure” that a condition is met. And if it’s not, we can respond with exiting the scope.

†: In mathematics, no negative number can have a real square root.

Early Returns – Why?

Is inverting if statements all there is to the guard statement? If it would be, we just might as well use if statements!

Here’s what the official Swift documentation says about guard:

A guard statement is used to transfer program control out of a scope if one or more conditions aren’t met.

What it means is that you use guard to exit a function (“transfer control out of scope”) when a condition isn’t satisfied; when it’s not true. For example, when the input for sqrt(number:) is negative and number >= 0 evaluates to false.

When you already know that it doesn’t make sense to continue executing a function, you might as well exit the function early. This principle is called early return (or early escape or exit).

It’s called “early” because you typically exit at the top of the function, when the computationally intense stuff hasn’t happened yet. If you’re not going to like the movie, why watch it all the way to the end?

Early returns are useful for a few reasons:

  • It’s clear: When a function returns early, and describes those early return conditions at the top of the function, you can quickly, clearly and comprehensively read the conditions for which a function won’t continue.
  • It’s efficient: Why continue executing if the function isn’t going to complete anyway? It’s an efficient use of resources to escape the function as early as possible.

But… you could say the same about using if for an early return, right? What makes guard superior syntax for early returns?

  • It’s natural: The “Guard that … is true, or else…” syntax reads naturally in English, which makes it easier to comprehend what the code does.
  • It’s strict: You can only use guard to escape the scope (i.e., transfer program control out of scope) with return, break, etc. Making this explicit makes the control flow of your code clearer.
  • Motivates early returns: The explicit guard statement motivates developers to return a function early. Design choices that make coding safer and more productive can be seen throughout the Swift language.

The guard statement is technically only syntactic sugar, i.e. it merely sugarcoats already existing control flow mechanisms with more expressive syntax. Syntactic sugar is a good thing – if it wouldn’t be, we would all still use punch cards!

Unwrap Optionals with “guard let”

The guard statement has another use case. You can combine optional binding, i.e. if let, with guard.

Here’s an example:

func login()
{
    guard let username = usernameField?.text else {
        throw Error.invalidUsername
    }

    // Authenticate the user with `username`
    API.shared.login(username, ···)
}

In the above example we’re using guard let to do 3 things:

  1. Check that the expression usernameField?.text isn’t nil, i.e. unwrapping the optional
  2. If the expression isn’t nil, i.e. it has a value, we assign that value to the constant username
  3. If the expression is nil, the else clause of guard is invoked and an error is thrown, which exits the function

Let’s disect that statement for a bit. First, imagine your app attempts to authenticate a user with a username and a password. The view controller has a text field property called usernameField of type UITextField? (an optional).

Both usernameField and its text property are optionals, so we’ll need to unwrap the optional to get to the value. A great approach is to use if let, like this:

if let username = usernameField?.text {
    // Do something with `username`
}

In the above code, we can safely use the username constant, inside the brackets, to authenticate the user. Thanks to if let we are 100% certain that the conditional body is only executed if usernameField?.text has a value.

Now, consider that we want to exit the function if usernameField?.text does not have a value – so when it’s nil. How can we do that?

Here’s an idea:

if let username = usernameField?.text {
    // Do something with `username`...
} else {
    throw Error.invalidUsername
}

In the above code we’re throwing an error if usernameField?.text is nil, so we can respond to it having a value or it being nil.

This code has a few problems, though:

  • Late Return: In the spirit of early returns, the throwing code is now pushed lower into the function. The positive/valid code is at the top, and the negative/exit code is at the bottom. Imagine you have multiple conditions that check for validity. That can quickly turn into a pyramid of doom; a ton of nested conditionals, each one a level of indentation deeper.
  • Verbose: We really just want to get the unwrapping out of the way as quickly as possible. The only thing we need is that username constant, and throwing an error if usernameField?.text is nil. We don’t need or want two conditional clauses for that!
  • Implicit: We’re not forced to throw or return in the else block; we’re not forced to transfer control out of scope. As a result, a careless developer might do poor man’s debugging with print() and not handle the error altogether. That’ll surely come back to bite you in the rear, later!

How do you solve this? Here’s how:

func login()
{
    guard let username = usernameField?.text else {
        throw Error.invalidUsername
    }

    // Authenticate the user with `username`
    API.shared.login(username, ···)
}

This guard let statement combines if let and guard. The usernameField?.text expression is tested for nil. If it’s nil, the error is thrown and the function exits. If it’s not nil, the value is assigned to username, and the function continues.

What’s counter-intuitive – and super cool – about guard let is that the username constant is available within the scope of the function, and not just the conditional body. You can use username “outside” of the squiggly brackets of the guard let ··· else { ··· } block. Neat!

Using “guard” in Practical iOS Development

If you’re already familiar with software development, you’re hopefully also familiar with the concept of early returns. It’s a good practice to exit a function or scope as soon as possible, if it becomes apparent that continuing execution makes no sense. Go home if movie sucks.

In practical iOS development, you’ll often see the guard statement as a one-liner. Here’s an example:

func parseAge(_ age: String)
{
    guard let age = Int(age) else { return }

    // Do something now that `age` is of type `Int`
}

In the above code, the failable initializer Int() is used to convert the string age to a constant with the same name of type Int. The guard statement is written on one line, which doesn’t affect its functionality (whitespace never does) and makes it much more pleasant to write.

Author’s Note: I recall a discussion on the Swift message boards about omitting the squiggly brackets, or even making else { return } implicit, but I can’t find it anymore… If doing so is useful, you can surely read about it on Swift Evolution. I wonder if we’ll see implicit guard let age = Int(age) in a future version of Swift.

Moving on. You’re likely to come across something like the following, in your iOS development work:

guard let username = usernameField?.text,
      let password = passwordField?.text, 
      !username.isEmpty, !password.isEmpty else {
    throw ···
}

See what’s happening there? The guard block is used to validate four conditions:

  1. If usernameField?.text is nil (if not, unwrap and assign to username)
  2. If passwordField?.text is nil (if not, unwrap and assign to password)
  3. If username is empty (i.e., string has zero length)
  4. If password is empty (i.e., string has zero length)

When usernameField?.text is nil, or passwordField?.text is nil, or either username or password is empty, the else clause is invoked and an error is thrown.

Just to be clear: it’s perfectly fine to check for these scenarios. It doesn’t make sense to attempt to authenticate the user if the password is empty or if the usernameField outlet property somehow is nil.

What’s annoying about this guard block, is that it can become spaghettiful (unclear) quickly. Just as with if let, it’s tempting to create a “pyramid of if let”, combining lots of conditionals and optional unwrapping, separated by commas.

Depending on your programming style, you can split up the above blocks in a few ways. First, it’s a good idea to separate the unwrapping with if let and the “simple” checks for isEmpty. Like this:

guard let username = usernameField?.text,
      let password = passwordField?.text {
    throw ...
}

if username.isEmpty || password.isEmpty {
    throw ...
}

In the above code, we’re even forgoing the guard statement in favor of a more descriptive if statement. It reads more naturally to say: “If username or password is empty”, compared to: “Guard that username not is empty, and password not is empty.” This is up for debate though. If you don’t agree, at least use it as an example to sharpen your thinking.

Quick Note: Using comma’s in if statements is not exactly the same as “AND” &&, but it’s similar. Comma-separated clauses are executed in order, and not continued if a preceding clause evaluates to false. Keep in mind that the order of precedence of , and && is not the same. See SE-099.

Further Reading

The guard and guard let statements make your code easier to read, safer, and less error-prone. You can code more productively, knowing that guard statements check a condition and exit if it’s invalid.

If you look closer, you see the tension between designing a programming language that’s convenient for the developer without making it too strict and opinionated. That’s the role of syntactic sugar – making you more productive, and your code more flexible, without getting too much in the way.

In any case, here’s what you learned:

  • How the guard statement works and when it’s best to use (for early returns)
  • Why early returns matter, because it’s efficient and increases clarity
  • How to unwrap optionals with guard let, and why that’s sometimes more convenient than if let
  • Using guard in practical iOS development – should you build a pyramid or not?

Want to learn more? Check out these resources:

LearnAppMaking

LearnAppMaking

At LearnAppMaking.com, app developers learn how to build and launch awesome iOS apps.