Swift: guard let vs if let
if let
and guard let
serve similar, but distinct purposes.
The "else" case of guard
must exit the current scope. Generally that means it must call return
or abort the program. guard
is used to provide early return without requiring nesting of the rest of the function.
if let
nests its scope, and does not require anything special of it. It can return
or not.
In general, if the if-let
block was going to be the rest of the function, or its else
clause would have a return
or abort in it, then you should be using guard
instead. This often means (at least in my experience), when in doubt, guard
is usually the better answer. But there are plenty of situations where if let
still is appropriate.
Are guard let and if let different statements than guard and if?
Optional binding in if
statement works like that: it checks if given value has value and if it has it goes with this value to if
block
if let constant = optional {
constant
}
With optional binding in guard
statement it checks if value exists and if does, it continue in current scope with variable / constant assigned in this scope
guard let constant = optional else { ... }
constant
So, for your initializer you need to assign constant, so you need to use let
keyword and since your initalizer is optional, you need to return nil
if initalization fails
guard let aString = optionalString() else { return nil }
self.aString = aString
Guard vs If Not
There is a difference if you need to declare a variable in the guard statement, i.e.
guard let foo = bar else { return }
In this case, you can continue to use foo as a non-optional in the rest of the method. You can't do this with a simple if statement.
If you're wondering why that's handy:
if let because = nobody {
if let likes = pyramids {
if let of = doom {
// guard can help you avoid this!
}
}
}
Return statement in IF LET vs GUARD LET
First of all, None of them is properly implemented.
- In
peripheral.services!
ifservices
will be nil, it will crash. - Return statement - There is no necessity of return in both, you can just remove it from
if-let
and everything will work fine - The benefit of
guard
overif-let
is it reducesif-else
brackets resulting more readable code - You use
guard
if you care about value of variable outside the block scope, think aboutif-let
you can only use unwrapped variable inside block only. - Use
if-let
for small number of variables unwrapping and value of unwrapped variable not required outside block scope - Use
guard-else
for using unwrapped variables outside block scope - If you are going to use
error
object outside the block scope(main use of guard block) then go withguard-else
otherwise go withif-let
.
In Swift, how do I do 'if let' instead of this?
There are several things you can do with an optional value:
var optionalText:String?
...
var text4:String? = optionalText // assign it to another optional variable
var text5 = optionalText // the same, the compiler will infer that text5 is also an optional string
optionalText?.append(" hello") // will only call `append(_:)` on `text` if it's not nil, otherwise this will be ignored
let text6:String = optionalText ?? "It was nil!" // use the coalescing operator
let text7:String = optionalText! // force-unwrap it into a String; if it's nil, your app will crash
// you CANNOT, however, treat it as a non-optional:
let newText = "Hello " + optionalText // this won't compile
// you can unwrap it:
if let text8 = optionalText {
// this code block will ONLY execute if textField.text was not nil
let newString = "Hello "+ text8 // this constant will only work in this block
// in that case, text8 will be of type String (non-optional)
// so it shouldn't be treated as an optional
}
let newString = "Hello "+ text8 // this won't compile
// unwrap it with `guard`:
guard let text8 = optionalText else {
// this code block will ONLY execute if textField.text WAS nil
// and it must return
return
}
let newString = "Hello "+ text8 // this is okay, text8 lives on in case of `guard`
These are the differences between guard let
and if let
:
if let nonOptional = optional {}
will assign a value to a non-optional constant by unwrapping an optional value and execute the code block, but only if the optional isn't nil. The non-optional constant will only live inside theif let { }
block. Use this if you want to handle both cases (nil
or otherwise) and then move on with your code.guard let nonOptional = optional else { }
will do the same assignment if possible, but code flow will be different afterwards: it will execute theelse
block in case the optional value isnil
, and thatelse
block will have to quit the scope (return
,continue
, orbreak
); it must not fall through, i.e. execution must not continue right after the block (the compiler will make sure of that). However, yournonOptional
constant will live on after this statement. Use this if your code largely depends on the optional value not being a nil: quit early if the condition fails, and otherwise hold on to the non-optional value and use if for the rest of your enclosing scope.
Btw., you can also use var
instead of let
if it makes sense, in both cases.
The main purpose of guard
is to avoid the "pyramid of doom" type of nested checks:
// pyramid of doom
func doomed() {
if condition1 {
if condition2 {
if condition3 {
// success scenario
print("all good")
// ...
} else {
print("failed condition 3")
return
}
} else {
print("failed condition 2")
return
}
} else {
print("failed condition 1")
return
}
}
// avoiding pyramid of doom
func nonDoomed() {
guard condition1 else {
print("failed condition 1")
return
}
guard condition2 else {
print("failed condition 2")
return
}
guard condition3 else {
print("failed condition 3")
return
}
// success scenario
print("all good")
// ...
}
Without guard
, your success scenario is hidden in the middle of nested if
statements related to error conditions, making your code difficult to read or edit.
With guard
, each error condition is handled separately, and after you get them out of the way, you can go on with the success scenario. With guard let
, you can also ensure that all the necessary constants and variables are available for the rest of the scope.
What you seem to need is one of two things:
Optionally unwrap and use your optional:
if let realImage = animal?.img {
let animalImage = UIImage(data: animal!.img!) as UIImage?
saveImageView.image = animalImage
}
// otherwise, saveImageView.image will not be assigned a new value
Simply pass an optional value
saveImageView.image = animal?.img
This should work because the left-hand side and the right-hand side are both UIImage?
optionals.
Swift - Guard-let vs if-let
Running your guard example myself in a playground is successful for me. I would recommend breaking up your guard into multiple logical sections. For example to help you track down your error you could change it to:
//Note: You should not force unwrap data here
guard let parsedData = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) else{
print("Serialization error"); return
}
guard
let parsedDict = parsedData as? [String:Any],
let stop = parsedDict["Stop"] as? [String:Any] else {
print("Error casting to dictionary"); return
}
guard
let name = stop["Name"] as? String,
let latitude = stop["Latitude"] as? String,
let longitude = stop["Longitude"] as? String else {
print("Error casting dictionary values"); return
}
//Everything is ok here
If Let vs Guard Let for UIImage in Swift 1.2
An if let
optional binding statement creates a new variable that only exists inside the braces that follow:
if let foo = optionalFoo {
//foo is only defined here
}
//foo has gone out of scope.
guard let creates a new variable that exists from the code after through the end of the current scope:
func bar(optionalFoo: FooThing) {
guard let foo = optionalFoo else {return}
//foo exists from here until the end of the
//current scope (the end of the func)
}
Since Swift 1 doesn't have a guard statement, you need to move the code that's after the guard let
to inside the braces of the if let
:
It looks like @ReinierMelian beat me to it with his answer. See his answer for the correct form of your code in Swift 1.x.
Note that Swift 1.0 is badly out of date, and you are doing yourself a disservice working with it. You should upgrade to Xcode 8.3 and Swift 3.1. The change from Swift 1 to Swift 2 was significant, and from Swift 2 to Swift 3 was big. Swift 1 code won't compile in Xcode 8, and you may not be able to run it through an automatic conversion. (Xcode 8 will offer to run Swift 2.x through an automatic conversion to Swift 3, but I'm not sure if it will convert Swift 1 to Swift 3.
Swift: Benefit of using a guard-statement?
I don't think that is a very good example of using guard, it is more common to use it with variables that might be nil (aka optional) or functions that might return nil. I suggest you read about the guard statement in the Swift Programming Language book (just scroll down a bit to "Early Exit")
We could make a better example from your code that is lacking some validation
func getEmail(email: String?) -> String? {
guard let input = email, !input.isEmpty else {
return nil
}
return input + "@somewhere.com"
}
Here we use guard to check that the parameter email
is not nil by assigning it to a local variable input
. If it is nil the function will return nil and otherwise it will check if it is empty and then it will also return.
If it is ok the function will continue and create and return an email address. Note that the function is declared to return an optional string since I think it is much clearer if a function like this returns nil rather than an empty string if it fails.
Related Topics
How to Check If a String Contains Another String in Swift
String Value to Unsafepointer≪Uint8≫ Function Parameter Behavior
How to Hash Nsstring With Sha1 in Swift
How to Convert a View (Not Uiview) to an Image
How to Detect a Tap Gesture Location in Swiftui
Swiftui View and @Fetchrequest Predicate With Variable That Can Change
Why Do I Need Underscores in Swift
Swift Structs to Nsdata and Back
How to Make a Swiftui List Scroll Automatically
Convert String to Date in Swift
What Are the New "For", "At", "In" Keywords in Swift3 Function Declarations
Why Are Emoji Characters Like 👩👩👧👦 Treated So Strangely in Swift Strings
Swiftui Iterating Through Dictionary With Foreach