Given a double optional such as this doubly wrapped String
:
let a: String?? = "hello"
print(a as Any) // "Optional(Optional("hello"))
"
@Leo, showed that you could use optional binding twice:
if let temp = a, let value = temp {
print(value) // "hello
"
}
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
"
}
As @netigger noted in their answer, this can also be written as:
if case .some(.some(let value)) = a {
print(value) // "hello
"
}
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
"
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
"
}
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
"
}
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
"
}
print(b as Any) // "Optional(Optional(Optional(Optional(Optional(Optional(Optional("bye")))))))
"
if let value = b as? String {
print(value) // "bye
"
}