Swift's guard keyword
Swift2Guard StatementSwift2 Problem Overview
Swift 2 introduced the guard
keyword, which could be used to ensure that various data is configured ready to go. An example I saw on this website demonstrates an submitTapped function:
func submitTapped() {
guard username.text.characters.count > 0 else {
return
}
print("All good")
}
I am wondering if using guard
is any different than doing it the old fashioned way, using an if
condition. Does it give benefits, which you could not get by using a simple check?
Swift2 Solutions
Solution 1 - Swift2
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
}
- 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)")
Solution 2 - Swift2
Unlike if
, guard
creates the variable that can be accessed from outside its block. It is useful to unwrap a lot of Optional
s.
Solution 3 - Swift2
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.
Solution 4 - Swift2
When a condition is met using guard
it exposes variables declared within the guard
block to the rest of the code-block, bringing them into its scope. Which, as previously stated, will certainly come in handy with nested if let
statements.
Note that guard requires a return or a throw in its else statement.
Parsing JSON with Guard
Below is an example of how one might parse a JSON object using guard rather than if-let. This is an excerpt from a blog entry that includes a playground file which you can find here:
How to use Guard in Swift 2 to parse JSON
func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {
guard let firstname = data["First"] as? String else {
return Developer() // we could return a nil Developer()
}
guard let lastname = data["Last"] as? String else {
throw ParseError.BadName // or we could throw a custom exception and handle the error
}
guard let website = data["WebSite"] as? String else {
throw ParseError.BadName
}
guard let iosDev = data["iosDeveloper"] as? Bool else {
throw ParseError.BadName
}
return Developer(first: firstname, last: lastname, site: website, ios: iosDev)
}
download playground: guard playground
More info:
Here's an excerpt from the The Swift Programming Language Guide:
> If the guard statement’s condition is met, code execution continues > after the guard statement’s closing brace. Any variables or constants > that were assigned values using an optional binding as part of the > condition are available for the rest of the code block that the guard > statement appears in. > > If that condition is not met, the code inside the else branch is > executed. That branch must transfer control to exit the code block > that that guard statement appears in. It can do this with a control > transfer statement such as return, break, or continue, or it can call > a function or method that doesn’t return, such as fatalError().
Solution 5 - Swift2
One benefit is elimination a lot of nested if let
statements. See the WWDC "What's New in Swift" video around 15:30, the section titled "Pyramid of Doom".
Solution 6 - Swift2
When to use guards
If you’ve got a view controller with a few UITextField elements or some other type of user input, you’ll immediately notice that you must unwrap the textField.text optional to get to the text inside (if any!). isEmpty won’t do you any good here, without any input the text field will simply return nil.
So you have a few of these which you unwrap and eventually pass to a function that posts them to a server endpoint. We don’t want the server code to have to deal with nil values or mistakenly send invalid values to the server so we’ll unwrap those input values with guard first.
func submit() {
guard let name = nameField.text else {
show("No name to submit")
return
}
guard let address = addressField.text else {
show("No address to submit")
return
}
guard let phone = phoneField.text else {
show("No phone to submit")
return
}
sendToServer(name, address: address, phone: phone)
}
func sendToServer(name: String, address: String, phone: String) {
...
}
You’ll notice that our server communication function takes non-optional String values as parameters, hence the guard unwrapping beforehand. The unwrapping is a little unintuitive because we’re used to unwrapping with if let which unwraps values for use inside a block. Here the guard statement has an associated block but it’s actually an else block - i.e. the thing you do if the unwrapping fails - the values are unwrapped straight into the same context as the statement itself.
// separation of concerns
Without using guard, we’d end up with a big pile of code that resembles a pyramid of doom. This doesn’t scale well for adding new fields to our form or make for very readable code. Indentation can be difficult to follow, particularly with so many else statements at each fork. Without guard
func nonguardSubmit() {
if let name = nameField.text {
if let address = addressField.text {
if let phone = phoneField.text {
sendToServer(name, address: address, phone: phone)
} else {
show("no phone to submit")
}
} else {
show("no address to submit")
}
} else {
show("no name to submit")
}
}
Yes, we could even combine all these if let statements into a single statement separated with commas but we would loose the ability to figure out which statement failed and present a message to the user.
Solution 7 - Swift2
With using guard our intension is clear. we do not want to execute rest of the code if that particular condition is not satisfied. here we are able to extending chain too, please have a look at below code:
guard let value1 = number1, let value2 = number2 else { return }
// do stuff here
Solution 8 - Swift2
Guard statement going to do . it is couple of different
- it is allow me to reduce nested if statement
- it is increase my scope which my variable accessible
if Statement
func doTatal(num1 : Int?, num2: Int?) {
// nested if statement
if let fistNum = num1 where num1 > 0 {
if let lastNum = num2 where num2 < 50 {
let total = fistNum + lastNum
}
}
// don't allow me to access out of the scope
//total = fistNum + lastNum
}
Guard statement
func doTatal(num1 : Int?, num2: Int?) {
//reduce nested if statement and check positive way not negative way
guard let fistNum = num1 where num1 > 0 else{
return
}
guard let lastNum = num2 where num2 < 50 else {
return
}
// increase my scope which my variable accessible
let total = fistNum + lastNum
}
Solution 9 - Swift2
> From Apple documentation:
Guard Statement
A guard statement is used to transfer program control out of a scope if one or more conditions aren’t met.
Synatx:
guard condition else {
statements
}
Advantage:
1. By using guard
statement we can get rid of deeply nested conditionals whose sole purpose is validating a set of requirements.
2. It was designed specifically for exiting a method or function early.
if you use if let below is the code how it looks.
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error == nil {
if let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 {
if let data = data {
//Process Data Here.
print("Data: \(data)")
} else {
print("No data was returned by the request!")
}
} else {
print("Your request returned a status code other than 2XX!")
}
} else {
print("Error Info: \(error.debugDescription)")
}
}
task.resume()
Using guard you can transfer control out of a scope if one or more conditions aren't met.
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
/* GUARD: was there an error? */
guard (error == nil) else {
print("There was an error with your request: \(error)")
return
}
/* GUARD: Did we get a successful 2XX response? */
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
print("Your request returned a status code other than 2XX!")
return
}
/* GUARD: was there any data returned? */
guard let data = data else {
print("No data was returned by the request!")
return
}
//Process Data Here.
print("Data: \(data)")
}
task.resume()
Reference:
Solution 10 - Swift2
Like an if statement, guard executes statements based on a Boolean value of an expression. Unlike an if statement, guard statements only run if the conditions are not met. You can think of guard more like an Assert, but rather than crashing, you can gracefully exit.
Solution 11 - Swift2
It really really does make the flow of a sequence with several lookups and optionals much more concise and clear and reduces lots of if nesting. See Erica Sadun post on replacing Ifs. .... Could get carried away, an example below:
let filteredLinks = locationsLinkedToList.filter({$0.actionVerb == movementCommand})
guard let foundLink = filteredLinks.first else {return ("<Person> cannot go in that direction.", nil, nil)}
guard filteredLinks.count == 1 else {return ("<Person> cannot decide which route to take.", nil, nil)}
guard let nextLocation = foundLink.toLocation else {return ("<Person> cannot go in that direction.", nil, nil)}
See if that sticks.
Solution 12 - Swift2
Simply put, it provides a way to validate fields prior to execution. This is a good programming style as it enhances readability. In other languages, it may look like this:
func doSomething() {
if something == nil {
// return, break, throw error, etc.
}
...
}
But because Swift provides you with optionals, we can't check if it's nil and assign its value to a variable. In contrast, if let
checks that it's not nil and assigns a variable to hold the actual value. This is where guard
comes into play. It gives you a more concise way of exiting early using optionals.
Solution 13 - Swift2
Source: Guard in Swift
Let's see the example to understand it clearly
Example 1:
func validate() {
guard 3>2 else {
print ("False")
return
}
print ("True") //True
}
validate()
In the above example we see that 3 is greater than 2 and the statement inside the guard else clause are skipped and True is printed.
Example 2:
func validate() {
guard 1>2 else {
print ("False") //False
return
}
print ("True")
}
validate()
In the above example we see that 1 is smaller than 2 and the statement inside the guard else clause are executed and False is printed followed by return.
Example 3: gaurd let, unwrapping optionals through guard let
func getName(args myName: String?) {
guard let name = myName, !name.isEmpty else {
print ("Condition is false") // Condition is false return
}
print("Condition is met\(name)")
}
getName(args: "")
In the above example we are using guard let to unwrap the optionals. In the function getName we’ve defined a variable of type string myName which is optional. We then use guard let to check whether the variable myName is nil or not, if not assign to name and check again, name is not empty. If both the conditions qualified i.e. true the else block will be skipped and print “Conditions is met with name”.
Basically we are checking two things here separated by comma, first unwrapping and optional and checking whether that satisfies condition or not.
Here we are passing nothing to the function i.e. empty string and hence Condition is false is print.
func getName(args myName: String?) {
guard let name = myName, !name.isEmpty else {
print ("Condition is false")
return
}
print("Condition is met \(name)") // Condition is met Hello
} getName(args: "Hello")
Here we are passing “Hello” to the function and you can see the output is printed “Condition is met Hello”.