Swift: Nested Optionals in a Single Guard Statement

Swift: Nested optionals in a single guard statement

Optional has a map function made just for this:

guard let v = Float("x").map(Int.init) else {
return nil
}

Can you safely unwrap nested optionals in swift in one line?

You could do it like so:

if let title = view.annotation?.title as? String {

}

view.annotation?.title is a double optional string: String?? since both the property annotation of an MKAnnotationView, and its own property title, are optional.


You could also use the guard statement like so:

guard let title = view.annotation?.title as? String else {
return
}
//use title in the rest of the scope

you could also use a switch statement :

switch title {
case .some(.some(let t)):
//use the title here
print(t)
default:
break
}

Multiple conditions in guard statement in Swift

Check this code

func demo(){

var str = [String: String]()

str["status"] = "blue"
str["asd"] = nil

guard let var2 = str["asd"], let var1 = str["status"]
else
{
print("asdsfddffgdfgdfga")
return
}
print("asdasdasd")
}

Guard will check one by one condition. If the first is true then it will check the next. Otherwise, it will execute the else part.

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.

Swift unwrap multiple optionals depending on each other in one if statement

If you only wish to work with the (possible) instance inner variable b (type B) of a (type A), you could use optional chaining in a single optional binding clause

class A {
var optionalB : B?
}

class B {}

var optionalA : A?

// in case you needn't actually use optionalA, but only its
// possibly non-nil instance member `optionalB`
if let b = optionalA?.optionalB {
//do something with b
}

Is there a better way of coping with Swift's nested if let pyramid of doom?

As commenters have said, Swift 1.2 now has multiple-let syntax:

if let jsonValue = users?.first,
json = jsonValue.object,
userIDValue = json[ "id" ],
doubleID = userIDValue.double,
userID = doubleID.map({ String(Int(doubleID))})
{
println( userID )
}

That said, in this instance it looks like you could might be able to do it all via optional chaining in 1.1, depending on what your objects are:

if let userID = users?.first?.object?["id"]?.double.map({String(Int($0))}) {

println(userID)

}

Note, much better to use first (if this is an array) rather than [0], to account for the possibility the array is empty. And map on the double rather than ! (which would blow up if the value is not double-able).

Swift Combine, Avoid nested subscribers on optionals

Yes, there is a way. You can simply use Combine's flatMap operator:

Transforms all elements from an upstream publisher into a new publisher up to a maximum number of publishers you specify.

My working example:

import Foundation
import Combine

class User: ObservableObject {
@Published var name: String

init(name: String) {
self.name = name
}

var debugDescription: String {
return self.name
}
}

class AccountService {
@Published var user: User? = nil
var userCancel: AnyCancellable?

init() {
self.userCancel = self.$user
.filter { $0 != nil }
.flatMap { $0!.$name }
.sink(receiveValue: { name in
print(name)
})
}

func setUseru(user: User) {
self.user = user
}

func changeUserName(name: String) {
self.user?.name = name
}
}

let x = AccountService()
x.setUseru(user: User(name: "Philipp"))
x.changeUserName(name: "Tom")
x.setUseru(user: User(name: "Anna"))

The program's output will be:

Philipp
Tom
Anna

Multiple if let and OR condition in swift

if let value1 = profile.value1, value1 != “” {}
else if value2 = profile.value2, value2 != “” {}
else if value3 = profile.value3, value3 != “” {}

here , acts like &&

Pay attention:
For Swift 2 replace , with where

Swift's guard keyword

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
}
  1. 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)")

How to avoid too many nested if when operating on optional UIViewController array?

Think about when you don't want to call setViewControllers. That is when the following is false:

viewControllers is nil, or empty, or viewControllers[0].pageIndex != self.selectedTabIndex

Using De Morgan's Law, we can see that it the negation of that is:

viewControllers is not nil, and not empty, and viewControllers[0].pageIndex == self.selectedTabIndex

Wiring that in an if statement, we get:

if let vcs = self.pageViewController.viewControllers,
let firstVC = vcs.first as? PageIndexable, // this checks for non-empty
firstVC.pageIndex == self.selectedTabIndex {
// do stuff
} else {
self.pageViewController.setViewControllers([viewController(At: self.selectedTabIndex)!], direction: direction, animated: true, completion: nil)
self.tabCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
// do other stuff

If the method would return immediately after doing setViewControllers and scrollToItem (i.e. you don't have any code in the // do other stuff position), then you can use a guard statement instead:

guard let vcs = self.pageViewController.viewControllers,
let firstVC = vcs.first as? PageIndexable,
firstVC.pageIndex == self.selectedTabIndex else {
self.pageViewController.setViewControllers([viewController(At: self.selectedTabIndex)!], direction: direction, animated: true, completion: nil)
self.tabCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
return
}
// do stuff

As Alexander said in the comments, you can also do this in a single expression:

if !((self.pageViewController.viewControllers?.first as? PageIndexable)?.pageIndex == self.selectedTabIndex) {

self.pageViewController.setViewControllers([viewController(At: self.selectedTabIndex)!], direction: direction, animated: true, completion: nil)
self.tabCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)

}

But I think the intention is less clear this way.



Related Topics



Leave a reply



Submit