Swift's guard keyword

Reading this article I noticed great benefits using Guard

Here you can compare the use of guard with an example:

This is the part without guard:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
  1. Here you’re putting your desired code within all the conditions

    You might not immediately see a problem with this, but you could imagine how confusing it could become if it was nested with numerous conditions that all needed to be met before running your statements

The way to clean this up is to do each of your checks first, and exit if any aren’t met. This allows easy understanding of what conditions will make this function exit.

But now we can use guard and we can see that is possible to resolve some issues:

func fooGuard(x: Int?) {
    guard let x = x where x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
    x.description
}
  1. Checking for the condition you do want, not the one you don’t. This again is similar to an assert. If the condition is not met, guard‘s else statement is run, which breaks out of the function.
  2. If the condition passes, the optional variable here is automatically unwrapped for you within the scope that the guard statement was called – in this case, the fooGuard(_:) function.
  3. You are checking for bad cases early, making your function more readable and easier to maintain

This same pattern holds true for non-optional values as well:

func fooNonOptionalGood(x: Int) {
    guard x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

func fooNonOptionalBad(x: Int) {
    if x <= 0 {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

If you still have any questions you can read the entire article: Swift guard statement.

Wrapping Up

And finally, reading and testing I found that if you use guard to unwrap any optionals,

those unwrapped values stay around for you to use in the rest of your code block

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")

Here the unwrapped value would be available only inside the if block

if let unwrappedName = userName {
    print("Your username is \(unwrappedName)")
} else {
    return
}

// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")

Unlike if, guard creates the variable that can be accessed from outside its block. It is useful to unwrap a lot of Optionals.


There are really two big benefits to guard. One is avoiding the pyramid of doom, as others have mentioned – lots of annoying if let statements nested inside each other moving further and further to the right.

The other benefit is often the logic you want to implement is more "if not let” than "if let { } else".

Here’s an example: suppose you want to implement accumulate – a cross between map and reduce where it gives you back an array of running reduces. Here it is with guard:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
        // if there are no elements, I just want to bail out and
        // return an empty array
        guard var running = self.first else { return [] }

        // running will now be an unwrapped non-optional
        var result = [running]

        // dropFirst is safe because the collection
        // must have at least one element at this point
        for x in dropFirst(self) {
            running = combine(running, x)
            result.append(running)
        }
        return result
    }

}


let a = [1,2,3].accumulate(+)  // [1,3,6]
let b = [Int]().accumulate(+)  // []

How would you write it without guard, but still using first that returns an optional? Something like this:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {

        if var running = self.first  {
            var result = [running]

            for x in dropFirst(self) {
                running = combine(running, x)
                result.append(running)
            }
            return result
        }
        else {
            return []
        }
    }

}

The extra nesting is annoying, but also, it’s not as logical to have the if and the else so far apart. It’s much more readable to have the early exit for the empty case, and then continue with the rest of the function as if that wasn’t a possibility.