Paul Hudson September 23rd 2019 @twostraws
Swift's guard
keyword lets us check an optional exists and exit the current scope if it doesn't, which makes it perfect for early returns in methods.
When a method runs, you want to be sure that it has all the data it needs to work properly, and your code should only execute when that's the case. Coders solved that in common two ways: pyramids of doom and early returns. The former looks like this:
if firstName != "" {
if lastName != "" {
if address != "" {
// do great code
}
}
}
It's called a pyramid of doom because of the natural shape of the code as it piles up with more and more indenting.
The latter looks like this:
if firstName == "" { return }
if lastName == "" { return }
if address == "" { return }
// do great code
This technique is called "early return" because you return from the method as early as possible, meaning that if you remain in the method it means everything is safe.
Neither of these two solutions are attractive. The first solution is messy, and only gets worse as you check more things. By the time you get to the meat of your method, you might be ten levels deep in checks, which is nasty!
The second solution isn't very descriptive, in that it doesn't make clear why we are we returning. It also checks against the opposite of what we care about: we're looking for a valid first name, so checking for an empty string is reversed.
In Swift, early returns have another problem, which is in the way they handle optionals. As you might imagine, one of the most important checks in Swift lies in safely unwrapping optionals, but if you use a straight if/let
in your early return, the unwrapped optional will leave scope almost immediately, and you can't use it. You either re-unwrap it, or you force unwrap now that you know it exists. Both are grim.
There's a Swift keyword called guard
, and enables improved early returns in three ways:
guard
what you want to be the case rather than the reverse. guard
is used specifically for trapping invalid parameters being passed to a method, so everyone will understand what it does when they see it.guard
remain in scope after the guard
finishes, so you can use them. This means you can check an optional variable is valid by unwrapping it, then use it straight away.Any conditions you would have checked using if
before, you can now check using guard
. Here are some examples:
guard name.characters.count > 0 else {
throw InputError.NameIsEmpty
}
guard age > 18 else {
return false
}
guard #available(iOS 9, *) else {
return
}
func printName() {
guard let unwrappedName = name else {
print("You need to provide a name.")
return
}
print(unwrappedName)
}
That last example shows how unwrapped optionals remain in scope – a key advantage of guard
compared to regular early returns.
Link copied to your pasteboard.