How to unwrap double optionals?
Given a double optional such as this doubly wrapped String
:
let a: String?? = "hello"
print(a as Any) // "Optional(Optional("hello"))\n"
@Leo, showed that you could use optional binding twice:
if let temp = a, let value = temp {
print(value) // "hello\n"
}
or force unwrap twice:
print(value!!) // don't do this - you're just asking for a crash
Here are 5 more methods you can use to safely unwrap a double optional:
Method 1:
You can also use pattern matching:
if case let value?? = a {
print(value) // "hello\n"
}
As @netigger noted in their answer, this can also be written as:
if case .some(.some(let value)) = a {
print(value) // "hello\n"
}
which while less concise might be a bit easier to read.
Method 2:
Alternatively, you can use the nil coalescing operator ??
twice:
print((a ?? "") ?? "") // "hello\n"
Note: Unlike the other methods presented here, this will always produce a value. ""
(empty String
) is used if either of the optionals is nil
.
Method 3:
Or you can use the nil coalescing operator ??
with optional binding:
if let value = a ?? nil {
print(value) // "hello\n"
}
How does this work?
With a doubly wrapped optional, the value held by the variable could be one of 3 things: Optional(Optional("some string"))
, Optional(nil)
if the inner optional is nil
, or nil
if the outer optional is nil
. So a ?? nil
unwraps the outer optional. If the outer optional is nil
, then ??
replaces it with the default value of nil
. If a
is Optional(nil)
, then ??
will unwrap the outer optional leaving nil
. At this point you will have a String?
that is nil
if either the inner or outer optional is nil
. If there is a String
inside, you get Optional("some string")
.
Finally, the optional binding (if let
) unwraps Optional("some string")
to get "some string"
or the optional binding fails if either of the optionals is nil
and skips the block.
Method 4:
Also, you can use flatMap
with optional binding:
if let value = a.flatMap({ $0 }) {
print(value) // "hello\n"
}
Method 5:
Conditionally cast the value to the type. Surprisingly, this will remove all levels of optionals:
let a: String?? = "hello"
let b: String??????? = "bye"
if let value = a as? String {
print(value) // "hello\n"
}
print(b as Any) // "Optional(Optional(Optional(Optional(Optional(Optional(Optional("bye")))))))\n"
if let value = b as? String {
print(value) // "bye\n"
}
How to unwrap double optionals in switch statement -- Swift
This works, but I'm curious why I need to double unwrap the default
case? I was thinking once the lhs and rhs get unwrapped from the
switch inputs, the default case would work with a Bool? for lhs and
rhs.
You aren't changing lhs
and rhs
when you do lhs ?? nil
and rhs ?? nil
. You are creating new values. So when you get to the default
case, lhs
and rhs
are still Bool??
. You can use let lhs
and let rhs
to capture the unwrapped values as I did in my solution below.
Here is another way to do it. A bit cleaner with some pattern matching:
switch (lhs as? Bool, rhs as? Bool) {
case (nil, nil):
return nil
case (nil, let rhs):
return rhs
case (let lhs, nil):
return lhs
case (let lhs?, let rhs?):
return lhs || rhs
}
Explanation
Casting the Bool??
with as? Bool
leaves you with a Bool?
. The let rhs
and let lhs
in the pattern matching catch the Bool?
value so that it can be returned. In the final case
, let lhs?
and let rhs?
unwrap the Bool?
values to get Bool
values so that ||
can be performed.
Test Cases
test(nil, nil) // nil
test(nil, false) // false
test(nil, true) // true
test(nil, Optional(nil)) // nil
test(nil, Optional(false)) // false
test(nil, Optional(true)) // true
test(false, nil) // false
test(false, false) // false
test(false, true) // true
test(false, Optional(nil)) // false
test(false, Optional(false)) // false
test(false, Optional(true)) // true
test(true, nil) // true
test(true, false) // true
test(true, true) // true
test(true, Optional(nil)) // true
test(true, Optional(false)) // true
test(true, Optional(true)) // true
test(Optional(nil), nil) // nil
test(Optional(nil), false) // false
test(Optional(nil), true) // true
test(Optional(nil), Optional(nil)) // nil
test(Optional(nil), Optional(false)) // false
test(Optional(nil), Optional(true)) // true
test(Optional(false), nil) // false
test(Optional(false), false) // false
test(Optional(false), true) // true
test(Optional(false), Optional(nil)) // false
test(Optional(false), Optional(false)) // false
test(Optional(false), Optional(true)) // true
test(Optional(true), nil) // true
test(Optional(true), false) // true
test(Optional(true), true) // true
test(Optional(true), Optional(nil)) // true
test(Optional(true), Optional(false)) // true
test(Optional(true), Optional(true)) // true
Swift double unwrapping of Optionals
The problem is that NSWorkspace().runningApplications
returns an
array of AnyObject
which has to be cast to an array of NSRunningApplication
:
let apps = NSWorkspace().runningApplications as! [NSRunningApplication]
let filteredApps = apps.filter {
$0.activationPolicy == NSApplicationActivationPolicy.Regular
}
for app in apps {
let name: String = app.localizedName!
}
Why does Swift allow double optionals?
I'm going to try and answer this from a Swift perspective, so forgive me if my FP terminology is incorrect.
Why would I ever use double optionals?
There is one well known case for double optional. A collection that uses optional to indicate no value at this key, which is also storing values which might be optional. Here it might be important to know whether the collection or the element returned the nil.
let possibleNumbers: [String: Int?] = [:]
guard let possibleNumber = possibleNumbers["one"] else {
print("No number stored")
}
guard let number = possibleNumber else {
print("Number at key was nil")
}
Can we automatically convert T???????
to T?
?
No, but it can be achieved with flatMap
. Although you need to add a new map for each level of optional.
possibleNumbers["one"].flatMap { $0 }
Also I do not believe the Swift Optional
is ever explicitly called a monad. It is implemented as a generic enum internally and it follows the semantics of that type more closely. At least I cannot find any reference in the language guide https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
Use guard with double-optional
You can use Guard as well as if let
if let getValue = something, let value = getValue {
print(value)
}
guard let getValue = something , let value = getValue else {
return
}
You can also use FlatMap
if let value = something.flatMap({ $0 }) {
print(value)
}
If you have any level of optionals e.g 3,4,5,7 you will get the value with Conditionally cast
let something: Bool????? = true
if let value = something as? Bool {
print(value) // prints true
}
Flatten and unwrap optionals in Double dimensional array
flatMap<S : SequenceType>(transform: (Self.Generator.Element) throws -> S) rethrows -> [S.Generator.Element]
is a combination of map()
and flatten()
:
s.flatMap(transform)
is equivalent to
Array(s.map(transform).flatten())
You are using flatMap()
with the "identity transform" { $0 }
to concatenate the arrays; this can be simplified
by using flatten()
directly:
let flattened = array.flatten().flatMap { $0 }
Double optionals (??) and double unwrapping (!!) with Button text in Swift
This is because of AnyObject
. The first ?
is for "is it an object that responds to the titleLabel
method?", and the second ?
is for "is the title label nil?"
If you are only hooking up a button from Interface Builder, you can use
@IBAction func buttonTapped(sender: UIButton)
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
}
Related Topics
Any Way to Replace Characters on Swift String
Trying to Understand Asynchronous Operation Subclass
How to Create Usdz File Using Xcode Converter
How to Get the Count of a Swift Enum
Best Practice to Implement a Failable Initializer in Swift
Why Can't I Instantiate an Empty Array of a Nested Class
Swift Dictionary Get Key For Value
Realitykit VS Scenekit VS Metal - High-Quality Rendering
What Does "@Uiapplicationmain" Mean
Convert String to Url (Why Is Resulting Variable Nil)
Deleting a Row from a Uitableview in Swift
Swift Array - Check If an Index Exists
Uitextview With Hyperlink Text
How to Modify the Background Color of a List in Swiftui
Generic Function Taking a Type Name in Swift