Chapter 10 Optionals
You can’t go very far with Swift without running into optionals. Whenever you declare a variable, the Swift compiler assumes a value must be present, unless you tell it otherwise by using an optional.
Optionals in Swift don’t have a direct analogy in many languages. But they are similar to the pattern of sometimes using nil
or null
to indicate when something is absent or has no value.
Swift formalizes this notion of something having an optional nil
value using optionals. Optionals allow for the possibility of a variable having a nil
value - they are a way to indicate to the compiler that a value may possibly be nil
or blank. I.e. it indicates a situation where you’re not sure that a value will be present - the value might be present or it might be nil
. If a variable is not optional, then it must always have a value. The result is that the Swift compiler does a lot more work at ensuring that you don’t use a nil
value somewhere that it isn’t expected, and you only use nil
values in places where you’ve explicitly told the compiler that they’re allowed.
10.1 Declaring optionals
var name1 = "Jill"
var name2: String? = "Jack"
name1 // "Jill"
name2 // {Some "Jack"}
We can declare an value as optional by adding a ?
to the type declaration. In the above example, setting the type to String?
indicates that name2
is an optional string which we’ve initialized to the value “Jack” (if we didn’t initialize its value, its initial value is nil
). name1
is a string which is not optional. Note that when name1
is evaluated in the playground we get its value back (“Jill”). But when name2
is evaluated, instead of getting the value we get an optional containing the value ({Some "Jack"}
).
There are a couple of ways for us to get the value from the optional. We can unwrap it or we can use if let
.
10.2 Forced unwrapping
When we access an optional value, we get an optional back. We can force Swift to retrieve an optional value by adding a !
to the variable name. This is known as forced unwrapping and you shouldn’t do this unless you know there is a value present. But when you do this you get the value back instead of the optional. In the above example we can unwrap the value of name2 like this:
name2! // "Jack"
When we do this we get the value of the optional back instead of the optional itself.
If we try to unwrap an optional that has no value we’ll get a runtime error. E.g. we can remove a value from an optional by setting it to nil
. Once we’ve done that we’ll get an error if we try to unwrap it.
name2 = nil
name2 // nil
name2! // Error (Runtime)
10.3 Conditional unwrapping
We can also use if
or if let
to conditionally do something with an optional value.
10.3.1 with if
We can use an optional value in an if
statement to check if the value is equal to some specific value. E.g.
if name2 == "Jack"{
println("Hello Jack")
}
else{
println("Hello there")
}
This checks both
that name2
is present and that the value of the name2
optional is equal to "Jack"
and performs different operations depending on the value.
You can use an if
statement with any operator that takes a pair of values and returns a boolean to perform a check against the value of an optional. These operators will generally return false
if the optional is not present and if it is present will evaluate it against the condition. Here’s an example of how optionals are evaluated in boolean operators:
1 var i:Int? = 10
2 var j:Int?
3
4 i // {Some 10}
5 j // nil
6
7 i == 10 // true
8 i == 8 // false
9 i > 0 // true
10 i == nil // false
11 i != nil // true
12
13 j == 10 // false
14 j > 0 // false
15 j == nil // true
Here we declare two optionals. i
gets initialized to 10 and j
is not initialized so it has initial value of nil
. We can use any of our comparison operators with both optionals. For optional i
, which has a value, they return the result of comparing the value of the optional (lines 7-11. For optional j
, which has no value, the comparisons generally return false (lines 13-15), except for the specific case where we check that it’s nil
(line 15). Also note here that we can use ==
and !=
to check whether or not a variable has a value (lines 10, 11, 15). E.g. i != nil
evaluates to true
when i
has a value and j == nil
evaluates to true when j
has no value.
10.3.2 with if let
We can use if let
to unwrap the value of an optional into a temporary variable and then do something only if the variable has a value.
1 if let n = name2 {
2 println("Hello \(n)")
3 }else{
4 println("Hello person with no name")
5 }
In this example, if the name2
optional has a value, the temporary variable n
is assigned its value and the first branch of the statement runs (line 2). If name2
has no value then the second branch of the if let
statement runs (line 4). Note that if let
only checks if the optional has a value - it doesn’t check if that value is truthy. See section 10.5 for discussion of this.
10.4 Using optionals
Here are a few things to think about when using optionals:
- We’ve covered how to declare optionals, unwrap their values and use their values with conditionals.
- You can use optionals in comparison operators.
- You remove a value from an optional by assigning it
nil
. - You can only assign
nil
to an optional. If you try to assignnil
to a variable that is not an optional you’ll get an error. - you can only use an optional in places where an optional is expected. If you try to use an optional value where an optional isn’t expected or allowed you’ll get an error.
Now that we understand optionals, let’s look at using them. Swift assumes by default that all variables have a value. Unless you tell it otherwise by declaring an optional you can’t have a nil
value in a place where your program expects there to be a value. In this example, our function expects its name
parameter to have a value. If we pass nil
as an argument we get an error (line 12). Even though our program isn’t doing anything other than printing the value of the parameter, the fact that we see nil
in a place where we expect the parameter to have a value is enough to raise an error.
1 var name1 = "Jill"
2 var name2: String? = "Jack"
3
4 func hello(name: String) -> String {
5 return "Hello \(name)"
6 }
7
8 hello(name1) // Hello Jill
9 hello(name2) // Error
10 hello(name2!) // Hello Jack
11 name2 = nil
12 hello(name2!) // Error (Runtime)
Here we declare name2
as an optional and name1
is not optional. Our hello
function takes a string as an argument. In this example this argument is not an optional. Note that if we try to pass an optional as a function argument we get an error (line 9). This error gets picked up by the compiler because we tried to pass an optional as an argument to a function which didn’t expect an optional. We can still use that optional as an argument to the function if we unwrap its value (line 10) but if we are doing this we should be sure that it has a value. If we try to unwrap it when it doesn’t have a value we get a runtime error (line 12).
Let’s look at a similar function that takes an optional as an argument.
1 var name1 = "Jill"
2 var name2: String? = "Jack"
3
4 func optionalHello(name: String?) -> String {
5 if let n = name {
6 return "Hello \(n)"
7 }
8 else {
9 return "Hello"
10 }
11 }
12
13 optionalHello(nil) // Hello
14 optionalHello(name2) // Hello Jack
15 optionalHello(name1) // Hello Jill
In this example the optionalHello
function takes an optional string argument called name
. It uses an if let
statement to append the name to the greeting if name
has a value and return a default greeting otherwise. Since this is an optional, we can call it with a nil
value (line 13). We can also call it with our optional name2
(line 14) and it will work whether this optional has a value or is nil
. We can also call it with our non-optional name1
(since it’s not an optional name1
will always have a value).
10.5 Dictionaries
We’ve already encountered optionals in chapter 7 when we looked at dictionaries. Let’s revisit that example now that we’ve discussed optionals in more detail.
You may have noticed in our dictionary example from chapter 7 (when you enter it in a playground) that when we access a value using a key we get a result like {Some "dog"}
. That’s because dictionaries return an optional. Lets take a closer look.
1 var pets = ["Polly" : "dog", "Joey" : "goldfish"]
2
3 pets["Polly"] // {Some "dog"}
4
5 pets["Polly"]! // "dog"
The reason for this is that we’re not certain that a dictionary key exists when we try to access it - it may be nil
. Returning an optional allows us to handle the case when the key is blank. In the above example we see that when we access an element of the pets dictionary we get an optional back (line 3) and when we forcibly unwrap an element of the pets array we get the value for that element back (line 5). Dictionaries return optionals as we don’t always know if there is a value present for a certain key. Thus we can process dictionary keys the same way as we did the optionals in the above example. E.g. when we’re not certain if a key is present we can use if
or if let
to do something with the key and we can force unwrapping of keys that we know to be present.
1 var preferences = [
2 "receiveEmail" : true,
3 "showAds" : true
4 ]
5
6 if let subscribed = preferences["receiveEmail"]{
7 if subscribed {
8 println("Send them an email")
9 }else{
10 println("User has unsubscribed")
11 }
12
13 }
14
15 if preferences["showAds"]!{
16 println("Show them an Ad")
17 }
18
19 if preferences["showAds"] == true {
20 println("Show them an Ad")
21 }
In this example we use both if
and if let
to do something depending on the value of a key in the preferences dictionary. At line 6 we use if let
to check if a key is present and do something if that value is present. Note that we have an if statement inside the if let
to process the value. The if let
statement body runs if the subscribed
variable has a value - it doesn’t check that value to determine whether or not to run. It will run whether subscribed
is true or false - all that is needed for the if let
body to run is that subscribed
has a value. Since subscribed
is boolean we have a further if
statement within the if let
to check whether it is true or false.
This showcases a subtle difference in Swift that can be confusing coming from other languages that don’t make the distinction between having no value and having a falsey value - there is a difference between something having no value and having a value that is false. In this example subscribed
has a value but that value is true (but line would still run even if subscribed
has value false
). So the if let
checks if there is a value present, and then the if
within that checks if that value is true or false.
We can also process the values in the array by unwrapping them - this assumes that the value is always present. E.g. If we know that preferences["showAds"]
is always present we can unwrap it and use it as a condition to an if statement (line 13). Note that we need to unwrap it here as the if statements requires a boolean for its condition, whereas if we didn’t unwrap it we would get an optional which would result in a compiler error.
Finally, we can also use if to check if the value is both present and true. The statement at line 17 will only run if preferences["showAds"]
is both present and true.