How to Unwrap Double Optionals

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



Leave a reply



Submit