Swift: Benefit of using a guard-statement? - ios

Just learning about the Swift guard keyword, I'm wondering what advantage it shall accomplish.
For example, let's say I have the following code:
func getEmail(email: String?) -> String {
guard email != "" else {
return ""
}
return email! + "#somewhere.com"
}
print(getEmail(email: "test123"))
I could write ...
if email == "" {
return ""
}
return email! + "#somewhere.com"
... as well.
So what's the advantage of having the guard-keyword?
Is it just having (a little bit) of syntactic sugar?

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.

Guard statement is early exit statement the main use of guard statement is to transfer program control out of a scope on certain conditions. These statements are similar with if statements which executes statements based on certain condition but unlike if, the guard statements only run when certain conditions are not met.
i strongly recommended to read this article Swift guard statement

If your code needs to decide whether to exit early, then you use the guard construct. A guard statement consists of a condition and an else block. The else block must leave the current scope, by any of the means that Swift provides, such as return, break, continue, throw, or fatalError. In case of failure of the condition, execution absolutely will not proceed within the block that contains the guard construct. Most of the time the construct is used to check Optional values, similar to if let statement. Unlike if let and/or while let, guard let doesn’t declare the bound variable for a nested scope, it declares it for this scope. Guys gave good examples on their answers about that. I would like to pay more attention on other possible cases as well.
A guard construct will also come in handy in conjunction with try?. Let’s presume we can’t proceed unless String(contentsOfFile:) succeeds. Then we can do the following:
guard let text = try? String(contentsOfFile: path) else {return}
There is also a guard case construct, forming the logical inverse of if case, e.g.:
guard case let .number(n) = error else { return }
There is one more interesting problem that you can solve using guard case construct. Suppose we have a function whose returned value we want to check in a guard statement:
guard howMany() > 10 else { return }
All looks good, but suppose that in the next line you want to use the value returned from that function. You may not want to call that function over again, because that might be time-consuming or it may have any side effects. You can't capture the result of calling the function with with guard let, because that requires an Optional, and the function howMany() doesn’t return an Optional.
In this case, you can use guard case construct in following way:
guard case let amount = howMany(), amount > 10 else { return }

I don't think for email id it would be that beneficial,
But consider example where there is a function where among students,
for one student you have to perform some crucial operation but what if
the student does not exist then there is no point in doing anything so
there comes your here as early exist which you can do using guard
statement.
let student = studentArr.filter{$0.id == "1234"}
guard let stdnt = student[0], stdnt != nil else {
return nil
}
// calculate marks of this student for all years' maths subject to prepare analytics

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.

Related

Checking if array is nil or not [duplicate]

This question already has answers here:
Check if optional array is empty
(8 answers)
Closed 4 years ago.
I have a array of my custom model, and I want to check if it is not nil and its size is greater then 0.
Following is my array with custom object
var listCountries : [Countries]? = nil
now In viewDIdLoad I want to make a check on it. I am new to Swift. I have good experience in working in Java.
I have read out Optional values concept and guard, if let statements. But I am unable to understand how efficiently they may be used. I have read too much SO questions but failed to figure out.
for example , if I want to check the upper given array in java I have only to do
if(listCountries != null && listCountries.size()>0){
//DO something
}
So to summarize my question:
How to make the upper given(Java code) check in to swift 4.? What is more smooth and reliable way.
What is a use of if let , guard, guard let statements. if I declare a variable (array, string) as optional I have to bear optional check like force wrapping each and every place. This is for me so making too much confusion.
Please help. I know this question has been asked in different ways. But this has some different context.
Just use ??.
if !(listCountries ?? []).isEmpty {
However, since you want to probably use listCountries in the if block, you should unwrap
if let listCountries = self.listCountries, !listCountries.isEmpty {
Ideally, if nil and empty means the same to you, don't even use an optional:
var listCountries: [Countries] = []
I would do it something like...
if let list = listCountries, !list.isEmpty { // Can also use list.count > 0
// do something
}
Even though you are not using the list inside the braces you are still using the list in the condition.
Or, like Sulthan said... make it non-optional to begin with if it makes no difference.
Obviously, I would assume that you are able to recognize the difference between nil array and empty array.
So, if we tried to implement a literal translation to your question:
I want to check if it is not nil and its size is greater then 0
For the first condition:
// "I want to check if it is not nil":
if let unwrappedList = listCountries {
// ...
}
and for the second condition:
// "I want to check if it is not nil":
if let unwrappedList = listCountries {
// "and its size is greater then 0":
if !unwrappedList.isEmpty {
// ...
}
}
However, you could combine both of the conditions by using the comma to achieve the multi-clause condition:
// I want to check if it is not nil and its size is greater then 0
if let unwrappedList = listCountries, !unwrappedList.isEmpty {
// ...
}
Or by using guard statement:
// I want to check if it is not nil and its size is greater then 0
guard let unwrappedList = listCountries, !unwrappedList.isEmpty else {
return
}
if let list = listCountries {
if(!list.isEmpty && list.count > 0) {
//value
}
}

How can I transform a signal with errors into a NoError one with ReactiveSwift? (and be elegant)

What is the most elegant way to transform my ReactiveSwift's SignalProducer<A, NetworkError> into a Signal<A, NoError>?
Most of the time, my signal producer is the result of a network call, so I want to split the results into two cases:
if a value is available, send a Signal<A, NoError>
if an error happened, send a Signal<String, NoError> with the error's localized description
(why? because i'm trying to be as MVVM as possible)
So far, I end up writing a lot of boilerplate like the following:
let resultsProperty = MutableProperty<SearchResults?>(nil)
let alertMessageProperty = MutableProperty<String?>(nil)
let results = resultsProperty.signal // `Signal<SearchResults?, NoError>`
let alertMessage = alertMessageProperty.signal // `Signal<String?, NoError>`
// ...
searchStrings.flatMap(.latest) { string -> SignalProducer<SearchResults, NetworkError> in
return MyService.search(string)
}
.observe { event in
switch event {
case let .value(results):
resultsProperty.value = results
case let .failed(error):
alertMessageProperty.value = error
case .completed, .interrupted:
break
}
}
ie:
using MutableProperty instances, that I have to set as optional to be able to initialize them
creating signals from those, ie getting a signal sending optionals as well
it feels dirty and makes the code so intertwined it kind of ruins the point of being reactive
Any help on (A) keeping my signals non optional and (B) splitting them into 2 NoError signals elegantly would be greatly appreciated.
Edit - Second Attempt
I will try to answer all your questions / comments here.
The errors = part doesn't work as flatMapError expects a SignalProducer (ie your sample code works just because searchStrings is a Signal string, which coincidently is the same as the one we want for errors: it does not work for any other kind of input)
You are correct, this is because flatMapError does not change the value type. (Its signature is func flatMapError<F>(_ transform: #escaping (Error) -> SignalProducer<Value, F>) -> SignalProducer<Value, F>). You could add another call to map after this if you need to change it into another value type.
the results = part behaves weirdly as it terminates the signal as soon as an error is met (which is a behavior I don't want) in my real-life scenario
Yes, this is because the flatMap(.latest) forwards all errors to the outer signal, and any error on the outer signal will terminate it.
Okay so here's an updated version of the code, with the extra requirements that
errors should have different type than searchStrings, let's say Int
Any error from MyService.search($0) will not terminate the flow
I think the easiest way to tackle both these issues is with the use of materialize(). What it does is basically "wrap" all signal events (new value, error, termination) into a Event object, and then forward this object in the signal. So it will transform a signal of type Signal<A, Error> into a Signal<Event<A, Error>, NoError> (you can see that the returned signal does not have an error anymore, since it is wrapped in the Event).
What it means in our case is that you can use that to easily prevent signals from terminating after emitting errors. If the error is wrapped inside an Event, then it will not automatically terminate the signal who sends it. (Actually, only the signal calling materialize() completes, but we will wrap it inside the flatMap so the outer one should not complete.)
Here's how it looks like:
// Again, I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
// Except this time, we wrap the events with `materialize()`
return MyService.search($0).materialize()
}
// Now Since `searchResults` is already `NoError` you can simply
// use `filterMap` to filter out the events that are not `.value`
results = searchResults.filterMap { (event) in
// `event.value` will return `nil` for all `Event`
// except `.value(T)` where it returns the wrapped value
return event.value
}
// Same thing for errors
errors = searchResults.filterMap { (event) in
// `event.error` will return `nil` for all `Event`
// except `.failure(Error)` where it returns the wrapped error
// Here I use `underestimatedCount` to have a mapping to Int
return event.error?.map { (error) in
// Whatever your error mapping is, you can return any type here
error.localizedDescription.characters.count
}
}
Let me know if that helps! I actually think it looks better than the first attempt :)
First Attempt
Do you need to access the state of you viewModel or are you trying to go full state-less? If state-less, you don't need any properties, and you can just do
// I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
return MyService.search($0)
}
// Use flatMapError to remove the error for the values
results = searchResults.flatMapError { .empty }
// Use flatMap to remove the values and keep the errors
errors = searchResults.filter { true }.flatMapError { (error) in
// Whatever you mapping from error to string is, put it inside
// a SignalProducer(value:)
return SignalProducer(value: error.localizedDescription)
}

segmentation fault 11 in swift2

I have no idea why I get this error.
The problem code is here
for i in 0..<itemDataJson?.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
When I print(itemDataJson?.count) it prints Optional(1).
What am I doing wrong?
Thank you.
It's printing Optional(1) because the variable itemDataJson is nullable, so the count would therefore have to be nullable, because we don't know if itemDataJson actually has a value.
The main problem that I see in your code is that you are force-unwrapping variables. Force-unwrapping a variable is a code smell (usually, although I do it myself from time to time, but you need to be careful).
When you force unwrap a variable, you need to ask yourself the question, "Do I want the app to crash here if this variable is nil?". If the answer is yes, then using a force unwrap is acceptable, otherwise, you should create a non-nullable variable or if that is not possible, you should use the guard statement in swift.
This could be used like this:
guard let itemDataJson = itemDataJson else {
// itemDataJson was null do something
return
}
You can use the if let construct as well, but be careful of the pyramid of doom if you don't use the if let construct correctly. See here for using it correctly, or use a guard statement.
I would recommend checking out the documentation on optionals if you have not done so already.
I would change the code to this version:
if (itemDataJson != nil) {
for i in 0..<itemDataJson!.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}
You should check all optionals before you try to access the underlying value. Best course of action would be to use if let statements.
if let itemDataJSON = itemDataJSON {
for i in 0..<itemDataJSON.count {
if let items = appDelegate.itemDataJSON {
if let imageURL = items[i]["imageURL"].string {
imageUrls.append(imageURL)
}
}
}
}
Other than that, it's a bad practice to store data in AppDelegate. You should create your own data layer that is not dependent of AppDelegate to make data handling more efficient and easier.
instead of checking for nil you should try this.
if let item = itemDataJson {
for i in 0..<item.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}

Access Optional property in multiple function for calculations - Swift

I have a NSObject Subclass. Say CityWalks
class CityWalks{
var totalCount:Int?
}
How do I use this property further? Should I check the nil coalescing every time this value is accessed.
example:
let aObject =
say in one fucntion (function1()) , I need to access this value, then it would like (aObject!.totalCount ?? 0)
func function1(){
...Some Access code for the object....
(aObject!.totalCount ?? 0)
}
Similarly in every other function(function2()) , I will have to write the same code.
func function2(){
...Some Access code for the object....
(aObject!.totalCount ?? 0)
}
So, what could be a better approach for such field, considering this property might receive a value from server or might not.
If you have a default value for this property just assign this value as default value.
class YourClass {
var totalCount = 0
}
I'd recommend you avoid using an optional value if it's possible. Because optional values its a first place when you can get an error.
As stated in the comments and the other answer using an optional is not really optimal in your case. It seems like you might as well use a default value of 0.
However, to clarify, you have to check the value when unwrapping the optional.
Sometimes it's possible to pass an optional to UIElement etc and then you don't really need to do anything with them
There are pretty ways of checking for nil in optional values built into swift so you can build pretty neat code even though you work with optional.
Look in to guard let and if let if you want to know more about unwrapping values safely.
if let
if let totalWalks = aObject?.totalCount {
//operate on totalWalks
}
guard
guard let totalWalks = aObject?.totalCount else { return }
//operate on totalWalks
There are also cases where you will want to call a function on an optional value and in this case you can do so with ?
aObject?.doSomething()
Any return values this function might have will now be wrapped in an optional and you might have to unwrap them as well with an if let or guard
When working with optionals you should try to avoid forcing the unwrap with ! as even though you at the moment know that the value is not null that might after a change in the code not be true anymore.

How to check for an undefined or null variable in Swift?

Here's my code:
var goBack: String!
if (goBack == "yes")
{
firstName.text = passFirstName1
lastName.text = passLastName1
}
All I want to do is execute the if-statement if 'goBack' is undefined. How can I do that? (I don't know what to put in the blank)
The overall program is more complicated which is why I need the variable to be undefined at first. In short, I'm declaring 'goBack', asking the user to type in their first and last name, then continuing to the next view controller. That view controller has a back button that brings us back to the first view controller (where I declared 'goBack'). When the back button is pressed, a 'goBack' string is also passed of "yes". I also passed the first and last name to the next view controller but now I want to pass it back. I'm able to pass it back, its just a matter of making the text appear.
EDIT: firstName and lastName are labels while passFirstName1 and passLastName1 are variables from the second view controller.
"All I want to do is execute the if-statement if 'goBack' is undefined. How can I do that?"
To check whether a variable equals nil you can use a pretty cool feature of Swift called an if-let statement:
if let goBackConst = goBack {
firstName.text = passFirstName1
lastName.text = passLastName1
}
It's essentially the logical equivalent of "Can we store goBack as a non-optional constant, i.e. can we "let" a constant = goBack? If so, perform the following action."
It's really interesting, you can define a variable as optional, which means it may or may not be defined, consider the following scenerio:
you want to find out if the app has been installed before...
let defaults = NSUserDefaults()
let testInstalled : String? = defaults.stringForKey("hasApplicationLaunchedBefore")
if defined(testInstalled) {
NSLog("app installed already")
NSLog("testAlreadyInstalled: \(testInstalled)")
defaults.removeObjectForKey("hasApplicationLaunchedBefore")
} else {
NSLog("no app")
defaults.setValue("true", forKey: "hasApplicationLaunchedBefore")
}
Then all you need to do is write a function to test for nil...
func defined(str : String?) -> Bool {
return str != nil
}
And you've got it. A simpler example might be the following:
if let test : String? = defaults.stringForKey("key") != nil {
// test is defined
} else {
// test is undefined
}
The exclamation mark at the end is to for unwrapping the optional, not to define the variable as optional or not
"All I want to do is execute the if-statement if 'goBack' is undefined"
The guard statement (new in Swift 2) allows exactly this. If goBack is nil then the else block runs and exits the method. If goBack is not nil then localGoBack is available to use following the guard statement.
var goBack:String?
func methodUsingGuard() {
guard let localGoBack = goBack else {
print("goBack is nil")
return
}
print("goBack has a value of \(localGoBack)")
}
methodUsingGuard()
From The Swift Programming Language (Swift 3.1):
Constants and variables created with optional binding in an if
statement are available only within the body of the if statement. In
contrast, the constants and variables created with a guard statement
are available in the lines of code that follow the guard statement, as
described in Early Exit.

Resources