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:
guard
works and when it’s smart to useguard let
guard
in practical iOS developmentReady? Let’s get started!
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 closurebreak
, to exit a loop like for
continue
, to continue to the next loop iterationthrow
, to exit a function and throw an error valueIt’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.
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:
But… you could say the same about using if
for an early return, right? What makes guard
superior syntax for early returns?
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.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!
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:
usernameField?.text
isn’t nil
, i.e. unwrapping the optionalnil
, i.e. it has a value, we assign that value to the constant username
nil
, the else
clause of guard
is invoked and an error is thrown, which exits the functionLet’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:
username
constant, and throwing an error if usernameField?.text
is nil
. We don’t need or want two conditional clauses for that!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!
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:
usernameField?.text
is nil
(if not, unwrap and assign to username
)passwordField?.text
is nil
(if not, unwrap and assign to password
)username
is empty (i.e., string has zero length)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.
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:
guard
statement works and when it’s best to use (for early returns)guard let
, and why that’s sometimes more convenient than if let
guard
in practical iOS development – should you build a pyramid or not?Want to learn more? Check out these resources: