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
}
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
}
- 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.- 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.- 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, orviewControllers[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, andviewControllers[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
Show and Hide Window Instead of Terminating App on Close Click in Cocoa App
Uitableview Only Displays One Section
Trying to Access Error Code in Alamofire
Swift: Urlsession.Shared.Datatask Says Status Code 304 = 200
Dynamic Dispatching Protocol Extension Doesn't Work Multiple Targets
Cannot Decode Object of Class (Ampathpopupbutton)'
Navigationlink Doesn't Work with New .Searchable Modifier on Swiftui 3.0
Binary to Hexadecimal in Swift
How to Specify Japanese Encoding for a UIlabel
Swift Lose Precision in Decimal Formatting
Detect What Is The Location of The Tap/Press Inside UIbutton Inside Sender Action Method
Get Applicationdidfinishlaunching Call in a View Controller. Parse Not Initialized Yet
Using Getters and Setters to Modify Values W/O Subclassing in Swift