Guard statements in Swift explained with code examples

Guard statements in Swift allow us to implement checks into our code that prevents the current scope from continuing. When writing code, we often have certain required conditions before continuing a method. An example can be unwrapping an optional input field before submitting a form.

Required conditions can be either a boolean value that needs to be true or unwrapping an optional that can not be nil. We can use guard statements for such, but there’s also the option to use an if let statement. The question is: when to use which? Let’s dive in.

What is a guard statement?

You can see a guard statement as a security guard that performs conditional checks to ensure your code is allowed to continue. A guard statement looks as follows:

guard <condition> else {
    return
}

The condition of a guard statement must be of type boolean. The condition can also be an unwrap of an optional. An example of a form submission method could look as follows:

struct FormSubmitter {
    var optionalInputName: String?
    var isFormRequestRunning: Bool

    func submit() {
        guard let inputName = optionalInputName, isFormRequestRunning == false else {
            return
        }
        // Continue form submission
    }
}

You can see that the submission only continues once an input name exists and there’s no running form request. The return keyword within the closure of the guard statement is required and enforced by the compiler, ensuring that we require the conditions. You can use the following keywords to exit the scope once there are unmet conditions:

  • return: exit the scope without continueing or throwing an error
  • break: used to exit a switch-case for enums
  • continue: used to continue a for-loop
  • throw: used to throw an error to indicate a failure

Splitting guard statements

The above guard statement works excellent, but you could argue that it’s better to split the statement up in two. Doing so allows us to throw a specific error per condition and will enable us to debug any issues better. Such implementation could look as follows:

struct FormSubmitter {
    enum FormSubmissionError: Error {
        case missingInputName
        case formRequestRunning
    }
    var optionalInputName: String?
    var isFormRequestRunning: Bool

    func submit() throws {
        guard let inputName = optionalInputName else {
            throw FormSubmissionError.missingInputName
        }
        guard isFormRequestRunning == false else {
            throw FormSubmissionError.formRequestRunning
        }
        // Continue form submission
    }
}

The above implementation works better since it throws a specific error for each unmet condition. Implementors of the form submission method can explicitly handle each error.

Throwing an error is seen as an early exit as well and takes away the requirement to use the return keyword.

When to use guard over if let statements

Guard vs. if let: when to use which? In my experience, this is one of the most opinionated parts of Swift. I’ve had several discussions with my team at WeTransfer during pull requests reviews arguing to use a guard statement or not.

You could write the submit method example as follows:

func submit() throws {
    if let inputName = optionalInputName, isFormRequestRunning == false {
        // Continue form submission
    }
}

This works fine and makes sure conditions are met before continuing the submission. However, the compiler will not force us to return the scope once the conditions are false. Since a return is not enforced, we can potentially run into unforeseen bugs. A simple example could be a print statement that makes us think that the submission succeeded:

func submit() throws {
    if let inputName = optionalInputName, isFormRequestRunning == false {
        // Continue form submission
    }
    print("Submission succeeded")
}

In the above example, the print statement always executes. We can solve this by either using an if else implementation:

func submit() throws {
    if let inputName = optionalInputName, isFormRequestRunning == false {
        // Continue form submission
    } else {
        return
    }
    print("Submission succeeded")
}

Or by moving the print statement into the body of the conditional check:

func submit() throws {
    if let inputName = optionalInputName, isFormRequestRunning == false {
        // Continue form submission
        print("Submission succeeded")
    }
}

However, in my opinion, it’s much nicer to use the guard statements instead:

func submit() throws {
    guard let inputName = optionalInputName else {
        throw FormSubmissionError.missingInputName
    }
    guard isFormRequestRunning == false else {
        throw FormSubmissionError.formRequestRunning
    }
    // Continue form submission

    print("Submission succeeded")
}

So, should I never use if let statements?

I hear you thinking:

“Should I always use a guard statement over an if let?”

Definitely not! There are examples in which you want your code to continue, even if there is an unmet condition. In this case, we can’t use a guard statement since that will force us to exit the current scope.

For example, we might want to cancel the running request and continue the new submission right after:

func submitForm() throws {
    guard let inputName = optionalInputName else {
        throw FormSubmissionError.missingInputName
    }

    if isFormRequestRunning {
        cancelRunningRequest()
    }

    // Continue form submission

    print("Submission succeeded")
}

I’m looking at guards as a way to describe the requirements for a method. Guard statements allow me to prevent bugs and ensure conditions are set before executing the actual body of a function.

Benefit of using a guards

As described earlier, guard statements have several benefits that help us write better code. To round up, a quick overview:

  • Enforced exit by the compiler: we have to throw an error or use the return keyword. This requirement prevents us from writing bugs in which code executes while conditions aren’t met.
  • Clear description of method requirements. A guard explicitly states that these conditions should be met before the body of the function is getting executed.
  • Improved readability since a guard explains you right away that there are required conditions.

Conclusion

Using a guard statement is a great way to ensure all conditions are true before executing the inner body of a method. Utilizing a guard, we indicate the requirements for a particular function. There are cases in which splitting up statements makes more sense to give detailed feedback to implementors. You can use an if let statement if the method is allowed to continue when there are unmet conditions.

If you like to learn more tips on Swift, check out the swift category page. Feel free to contact me or tweet me on Twitter if you have any additional suggestions or feedback.

Thanks!

 

Featured SwiftLee Jobs

Loading RSS Feed

Browse more Swift related Jobs, or add your own on SwiftLee Jobs