LEARN SWIFT

Buy the PDF

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 assign nil 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.