I have encountered numerous situations where a coder have used the guard keyword. And then later, in a seemingly almost identical situation the same coder in the same code does not use the guard keyword. I am aware that this may be a stupid question, so please don't bash it. When should I use the guard keyword and where shouldn't I?
Here is an example (there are many more). This is part of a script that is requesting data form an API.
//Here I am using guard
guard let json = json else {
//Now I am not using guard
if let error = error {
completion(.Failure(error))
} else {
//Error handling
}
return
}
Why not use the:
if let var1 = var1 {
//Keep on going
} else {
//Don't crash
}
syntax all the time instead of the guard syntax? At first glance it even seems to have more functionality, but I am certain that does not have to be the case.
One great benefit of the guard statement is that you know that if the condition is not satisfied then the execution flow gets stopped.
This is important for several reasons
Unwrapping
You can define unwrapped values which don't need a new scope { ... } to be available
func next(num:Int?) -> Int? {
guard let num = num else { return nil }
return num + 1
}
Readability
When you read the code you know that if the guard condition is not satisfied then the following lines won't be executed.
Semantics
You know a guard statement is there to check conditions required for the following block of code.
But I can replace every guard with an if
Sure. We could also replace every while and for with a goto in some languages. And we could always replace recursion with iteration (and viceversa).
But this doesn't necessarily means it is always a good idea.
Despite we can implement some behaviours with more then one programming "tool", we should still use the one that better fits that specific scenario.
Related
What is the difference between:
guard let json_data = Data(contentsOf: path) else {return nil}
and
let json_data = try? Data(contentsOf: path)
I dont want to use optional while loading the data into the variable. I want other ways to try it.
Thanks in advance.
The options are:
Your first example, unwrapping it with guard, is missing a try?:
func foo() -> Bar? {
guard let jsonData = try? Data(contentsOf: path) else { return nil }
// if you get here, `jsonData` is not `Optional`
…
}
This will safely unwrap your optional and let you do whatever you want if the unwrapping failed. (In your example, you are returning nil.)
Your second example, yields an optional, which you presumably need to unwrap with an if statement.
func foo() -> Bar? {
let jsonData = try? Data(contentsOf: path)
// jsonData is `Optional` in this example
if let jsonData {
…
} else {
return nil
}
}
We would generally favor the first option over this, where the “early exit” of the guard makes it a little easier to read the code, but there are cases where you might use this pattern.
An option that hasn’t been considered is to actually throw the error (using try instead of try?):
func foo() throws -> Bar {
let jsonData = try Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this only passes the buck of handling the error to the caller (i.e., a do-catch block). But it does have a few virtues over the prior two examples, namely that (a) the useful information of the error object is not just discarded, thereby making it easier to diagnose problems during the development process; and (b) you don’t have to return an optional.
Yet another option (to be used only if you know that this will always succeed, e.g., you are reading a well-known file from your bundle that you know must always succeed) is try!, a “force-try”:
func foo() -> Bar {
let jsonData = try! Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this will crash if the Data(contentsOf:) can ever fail, so only use this in scenarios where you know that this is impossible.
Personally, I would generally favor option 3 (where I capture what went wrong) in cases where the Data(contentsOf:) might ever plausibly fail at runtime, and I might consider option 4 (where it crashes with a meaningful error message) when I know it is impossible for it to ever fail at runtime. That having been said, more than once I found myself using option 4 and I later regretted not using option 3, simply because there was some weird edge-case that I neglected to consider.
In short, nowadays I tend to defensively catch errors, log the full error in the console and show a nice localized message in the UI (i.e., option 3). I almost never use try?, because if something can fail, it’s rarely a good idea to discard the useful diagnostic information.
It will be better to be optional once you are loading the data to avoid application crash in and problems in case there was no data there. This is consider as a safe feature.
We are working in a Swift project. A function was there like,
fun getSleepAmmount() -> Int {
// calculate sleep time
// return value when valid
// else
return -1
}
My team member prefers the above function where caller needs to check with -1 which is not I am comfortable with. My suggestion is to redesign with nil return (although callers still need to check nullability) like,
fun getSleepAmmount() -> Int? {
// calculate sleep time
// return value when valid
// else
return nil
}
But my colleagues do not want to redesign. Which version of the functions is cleaner and why?
Obviously, nil is much cleaner. Because of -1 means nothing. This is just a magic word. It is difficult to support, refactor and handle this case.
Returning nil is a better solution instead of using any garbage or default value.
Returning any default value may in future clash with the actual result.
Also, there might be other developers dealing with the same code. So, using nil will have a better explanation than using -1.
Second is the better as youll do
if let v = getSleepAmmount() {}
But with First
let v = getSleepAmmount()
if v > 0 {}
Returning nil means this isn't a valid return while -1 may mean another thing that will be miss-understood by a new developer that checks the code
If there is code in the caller that should only run if there is a valid sleep amount, then optionals is the better and clearer way to go. This is exactly what guard let and if let are designed for:
guard let sleepAmount = getSleepAmount() { else return }
// do something with sleepAmount
An even better way would be to throw an error inside the function:
func getSleepAmmount() throws -> Int {
// calculate sleep time
// return when valid
// else
throw InvalidSleepAmount
}
Then
do {
let sleepAmount = try getSleepAmount()
// do something with it
} catch InvalidSleepAmount {
// error processing
}
(If you want, your function could throw different errors so the caller gets to know why the sleep amount is invalid, SleepTooShort, SleepTooLong, NoSleep etc)
I am getting these errors often when a function I call uses something like:
optionalVar!
"Unexpectedly found nil while unwrapping an Optional"
I am unsure how to deal with functions that fail sometimes if I don't always have control over the inner code?
Is there a way to protect around such crashes? In most languages I could put try catches around most things.
When I do something like:
if let result = Blah.someExternalFunction(html: "some bad html") { }
This can still fail inside "someExternalFunction", even after trying to add try? in front of it.
Thanks
What you're looking for is if let
If you have an optional value, you can simply do something like this to "try" it:
if let val = optionalVar{
//val is already unwrapped
}
else{//it was nil}
Another option is to use a guard statement. It works similarly.
guard let val = optionalVar
else{
//the value is nil, so you need to exit the current function
return
}
//'val' is now unwrapped for any code below the guard-else statement
I know there are some similar questions around, but I couldn't find one specific to my issue.
I have a request where I want to check for the presence of the error key. it is not present everything is fine, if not I should handle the error. Currently, I have it implemented as follows:
if let error = json["error"] {
// handle error
}
else {
// handle success
}
I would like to use a guard statement here to have the success case unindented. The only way I came up with is
guard json["error"] == nil else {
let error = json["error"]!
// handle error
}
// handle success
but that seems wrong to me with the !. Are there any other approaches to this?
In your guard code you would have to have a return statement in the else block. Like this...
guard json["error"] == nil else {
let error = json["error"]!
// handle error
return
}
// handle success
But you are correct. Having to force unwrap the error is not ideal.
So in this case. I think guard is the wrong solution. Instead use if but return from the conditional block. This removes the need for using an else block.
if let error = json["error"] {
print(error)
// handle error
return
}
// handle success...
// no need for else block. Just return from the if in the error case.
The difference between guard let and if let is where the unwrapped optional is scoped.
With guard it is scoped outside the block with if it is scoped inside the block.
An idea for your issue was proposed on the Swift Evolution mailing list:
"guard not let" optional binding
https://forums.swift.org/t/idea-guard-not-let-optional-binding/2614
[it is] fairly common that you want to check that an optional is nil, and still bail if it isn’t (maybe using the value that you now know exists), e.g:
guard cachedValue == nil else { return cachedValue! }
cachedValue = //… expensive calculation
It seems a little bit “unfair” that we have this lovely clean let syntax when checking for Optional.Some, but we to have to do this ugly manual check against nil and explicit unwrap when checking for Optional.None. There is literally no other way to satisfy the guard statement; our optional bindings only go one-way can’t be evaluated.
Unfortunately that construct does not currently exist in Swift.
The alternatives are the slightly awkward/duplicated guard syntax, possibly with a force-unwrap:
guard json["error"] == nil else {
return json["error"]!
}
or using if-let (which does not enforce scope-exit like guard):
if let error = json["error"] {
return error
}
This question already has answers here:
Swift's guard keyword
(13 answers)
Closed 7 years ago.
I am confused about when to use guard and when to use if...else.
Is guard is replacement or alternative for If statement ?
Main thing want to know what are the functional benefits of guard statement for Swift language?
Any help to clear this situation will be appreciated.
Using guard might not seem much different to using if, but with guard your intention is clearer: execution should not continue if your conditions are not met. Plus it has the advantage of being shorter and more readable, so guard is a real improvement, and I'm sure it will be adopted quickly.
There is one bonus to using guard that might make it even more useful to you: if you use it to unwrap any optionals, those unwrapped values stay around for you to use in the rest of your code block. For example:
guard let unwrappedName = userName else {
return
}
print("Your username is \(unwrappedName)")
This is in comparison to a straight if statement, where the unwrapped value would be available only inside the if block, like this:
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)")
https://www.hackingwithswift.com/swift2