There I want to enumerate all possible ways to unwrap optional variables that possible in Swift.
- Force Unwrapping
- Check for nil (with !=) and Force Unwrapping
- Ternary Operator
- if let Construction or Single Optional Binding
- Multiple Optional Binding
- Guard Operator
- Nil Coalescing Operator
- Switch Operator
- Optional Chaining
Let's review each of them.
Force Unwrapping
When you have optional and you are sure that this optional has a value you always can just use force unwrapping with ! (exclamation point operator). But it always for your responsibility use this because there can be situations when optional will have no value and force unwrapping will produce error and app crashing.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
let name1 = dict[1]
let name4 = dict[4]
print(name1!) // "Jack"
print(name4!) // Fatal error: Unexpectedly found nil while unwrapping an Optional value
Check for nil
But there is solution for ensure from errors when using force unwrapping - just check optional for
nil before that.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
print(dict[1]) // Optional("Jack")
print(dict[4]) // nil
func getName(for index: Int) -> String {
if dict[index] != nil {
return dict[index]!
}
return "No name for such index"
}
print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index
Ternary Operator
The same check for
nil can be done with ternary operator.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
print(dict[1]) // Optional("Jack")
print(dict[4]) // nil
func getName(for index: Int) -> String {
return dict[index] != nil ? dict[index]! : "No name for such index"
}
print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index
if let Construction or Optional Binding
This construction
if let allows you safety unwrap optional value. If optional has value then this value will be unwrapped to constant/variable. Unwrapped variable/constant will be available only in scope inside this
if let statement.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
print(dict[1]) // Optional("Jack")
print(dict[4]) // nil
func getName(for index: Int) -> String {
if let name = dict[index] {
return name
}
return dict[index] ?? "No name for such index"
}
print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index
Multiple Optional binding
There can be optional property of optional object, so you can do some chaining of
if let statements. All this
if let statements can be written in on line with one
if and with
commas. Every next statement will be executed only if previous value is unwrapped.
struct A {
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
func getName(for index: Int) -> String? {
return dict[index]
}
}
struct B {
var names: A?
init() {
self.names = A()
}
}
let b: B? = B()
if let b = b {
if let names = b.names {
if let name = names.getName(for: 1) {
print(name) // Jsck
}
}
}
if let b = b, let names = b.names, let name = names.getName(for: 1) {
print(name) // Jack
}
Guard Operator
Guard operator or "Early Exit" check that optional has value and can be unwrapped. If optional has no value then else statement will be executed. Unwrapped value will be available in scope next to guard, opposite to
if let statement.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
print(dict[1]) // Optional("Jack")
print(dict[4]) // nil
func getName(for index: Int) -> String {
guard let name = dict[index] else {
return "No name for such index"
}
return name
}
print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index
Nil Coalescing Operator or ??
Special operator
?? works following way: if there is a value in optional then this value will be return else default value will be return.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
print(dict[1]) // Optional("Jack")
print(dict[4]) // nil
func getName(for index: Int) -> String {
return dict[index] ?? "No name for such index"
}
print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index
Switch Operator
We can use
switch operator for unwrapping optionals because if we open source code of Optional we can see that Optional is an enum with 2 cases: none and some.
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
}
In case of
.none - there is no value (it means nil). In case of
.some - there is a value that we can use and this value will be unwrapped.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
func getName(for index: Int) -> String {
let name = dict[index]
switch name {
case .some(let value):
return value
case .none:
return "No name for such index"
}
}
print(getName(for: 1)) // Jack
print(getName(for: 4)) // No name for such index
Optional Chaining
In some cases optional instance of struct or class can have optional properties. So, when we need to get optional property of optional object it is a chain of optionals. Every next optional value will be unwrap only if previous is can be unwrapped. For, example
b?.names?.getName(for: 1)?.count
Property
count will be getting only if
b can be unwrapped,
names can be unwrapped and value which will be returned by
getNames() can be unwrapped too. If any of values in chain cannot be unwrapped then property
count will be nil.
struct A {
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
func getName(for index: Int) -> String? {
return dict[index]
}
}
struct B {
var names: A?
init() {
self.names = A()
}
}
let b: B? = B()
b?.names?.getName(for: 4)?.count
print(b?.names?.getName(for: 1)?.count) // Optional(4) because "Jack" has 4 letters
print(b?.names?.getName(for: 4)?.count) // nil because no name for such index