6.
Optionals
Written by Matt Galloway
So far, all the variables and constants you’ve dealt with have concrete values. When you had a string variable, like var name, it had a string value associated with it, like "Matt Galloway". It could have been an empty string, like "", but there was a value to which you could refer.
That’s one of the built-in safety features of Swift: If the type says Int or String, then there’s an actual integer or string there, guaranteed.
This chapter will introduce you to the concept of optionals, a special Swift type that can represent a value and the absence of that value. By the end of this chapter, you’ll know why you need optionals and how to use them safely.
Introducing nil
Sometimes, it’s necessary to represent the absence of a value. Imagine a scenario where you need to refer to a person’s identifying information; you want to store the person’s name, age and occupation. Name and age are both things that must have a value — everyone has them. But not everyone is employed, so the absence of a value for occupation is something you need to handle.
Without knowing about optionals, this is how you might represent the person’s name, age and occupation:
var name = "Matt Galloway"
var age = 30
var occupation = "Software Developer & Author"
But what if I become unemployed? Maybe I’ve won the lottery and want to give up work altogether (I wish!). This is when it would be useful to be able to refer to the absence of a value.
Why couldn’t you just use an empty string? You could, but optionals are a much better solution. Read on to see why.
Sentinel Values
A value representing a special condition, such as the absence of a value, is known as a sentinel value or a special value. That’s what your empty string would be in the previous example.
Let’s look at another example. Say your code requests something from a server, and you use a variable to store any returned error code:
var errorCode = 0
You represent the lack of an error with a zero in the success case. That means 0 is a sentinel value.
This arrangement works like the empty string for occupation, but it’s potentially confusing for the programmer because it arbitrarily steals a value. 0 might be a valid error code — or could be in the future if the server changed how it responded. Either way, you can’t be completely confident that the server didn’t return an error without consulting the documentation about special values.
In these two examples, it would be much better if a special type could represent the absence of a value. It would then be explicit when a value exists and when one doesn’t. The compiler could check for you.
nil is the name given to the absence of a value, and you’re about to see how Swift incorporates this concept into the language in a rather elegant way.
Some other programming languages simply embrace sentinel values. Some, like Objective-C, have the concept of nil, but it is merely a synonym for zero (a sentinel value).
Swift introduces a new type, Optional, that handles the possibility that a value could be nil. It removes the ambiguity introduced by using sentinel values. If you’re handling a non-optional type, you’re guaranteed to have a value and don’t need to worry about a sentinel value with special meaning. Similarly, if you use an optional type, you know you must handle the nil case.
Introducing Optionals
Optionals are Swift’s solution to the problem of representing both a value and the absence of a value. An optional can hold either a value or nil.
Think of an optional as a box: it either contains exactly one value or is empty. When it doesn’t contain a value, it’s said to contain nil. The box itself always exists; it’s always there for you to open and look inside.
On the other hand, a string or an integer doesn’t have this box around it. Instead, there’s always a value, such as "hello" or 42. Remember, non-optional types are guaranteed to have an actual value.
Note: Those who’ve studied physics may be thinking about Schroedinger’s cat right now. Optionals are a little bit like that, except it’s not a matter of life and death!
You declare a variable of an optional type by using the following syntax:
var errorCode: Int?
The only difference between this and a standard declaration is the question mark at the end of the type. In this case, errorCode is an “optional Int”. This means the variable is like a box containing either an Int or nil.
Note: You can add a question mark after any type to create an optional type. This optional type is said to wrap the regular non-optional type. For example, optional type
String?wraps typeString. In other words: an optional box of typeString?holds either aStringornil.Also, note how an optional type must be made explicit using a type annotation (here
: Int?). Optional types can never be inferred from initialization values, as those values are of a regular, non-optional type, ornil, which can be used with any optional type.
Setting the value is simple. You can either set it to an Int, like so:
errorCode = 100
Or you can set it to nil, like so:
errorCode = nil
This diagram may help you visualize what’s happening:
The optional box always exists. When you assign 100 to the variable, you’re filling the box with the value. When you assign nil to the variable, you’re emptying the box.
Take a few minutes to think about this concept. The box analogy will be a big help as you go through the rest of the chapter and use optionals.
Mini-Exercises
- Make an optional
StringcalledmyFavoriteSong. If you have a favorite song, set it to a string representing that song. If you have more than one favorite song or no favorite, set the optional tonil. - Create a constant called
parsedIntand set it equal toInt("10"), which tries to parse the string10and convert it to anInt. Check the type ofparsedIntusing Option-Click. Why is it an optional? - Change the string being parsed in the above exercise to a non-integer (try
dog, for example). What doesparsedIntequal now?
Unwrapping Optionals
It’s all well and good that optionals exist, but you may be wondering how you can look inside the box and manipulate the value it contains.
Take a look at what happens when you print out the value of an optional:
var result: Int? = 30
print(result)
This prints the following:
Optional(30)
Note: You will also see a warning on this line which says, “Expression implicitly coerced from ‘Int?’ to Any”. This is because Swift warns that you’re using an optional in place of the
Anytype as it’s something that usually means you did something wrong. You can change the code toprint(result as Any)to silence the warning.
That isn’t really what you wanted, although it makes sense if you think about it. Your code has printed the box, and the result says, “result is an optional that contains the value 30”.
To see how an optional type is different from a non-optional type, see what happens if you try to use result as if it were a normal integer:
print(result + 1)
This code triggers an error:
Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
It doesn’t work because you’re trying to add an integer to a box — not to the value inside the box but to the box itself, which doesn’t make sense.
Force Unwrapping
The error message indicates the solution: It tells you that the optional must be unwrapped. You need to unwrap the value from its box. It’s like Christmas!
Let’s see how that works. Consider the following declarations:
var authorName: String? = "Matt Galloway"
var authorAge: Int? = 30
There are two different methods you can use to unwrap these optionals. The first is known as force unwrapping, and you perform it like so:
var unwrappedAuthorName = authorName!
print("Author is \(unwrappedAuthorName)")
This code prints:
Author is Matt Galloway
Great! That’s what you’d expect.
The exclamation mark after the variable name tells the compiler that you want to look inside the box and take out the value. The result is a value of the wrapped type. This means unwrappedAuthorName is of type String, not String?.
The word “force” and the exclamation mark ! probably convey a sense of danger to you, and they should.
You should use force unwrapping sparingly. To see why consider what happens when the optional doesn’t contain a value:
authorName = nil
print("Author is \(authorName!)")
This code produces the following error that you will see in your console:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
The error occurs because the variable contains no value when you try to unwrap it. What’s worse is that you get this error at runtime rather than compile-time – which means you’d only notice the error if you happened to execute this code with some invalid input.
Worse yet, if this code were inside an app, the runtime error would cause the app to crash!
How can you play it safe?
To stop the runtime error here, you could wrap the code that unwraps the optional in a check, like so:
if authorName != nil {
print("Author is \(authorName!)")
} else {
print("No author.")
}
The if statement checks if the optional contains nil. If it doesn’t, it contains a value you can unwrap.
The code is now safe, but it’s still not perfect. If you rely on this technique, you’ll have to remember to check for nil every time you want to unwrap an optional. That will become tedious, and one day you’ll forget and again end up with the possibility of a runtime error.
Back to the drawing board, then!
Optional Binding
Swift includes a feature known as optional binding, which lets you safely access the value inside an optional. You use it like so:
if let unwrappedAuthorName = authorName {
print("Author is \(unwrappedAuthorName)")
} else {
print("No author.")
}
You’ll immediately notice that there are no exclamation marks here. This optional binding gets rid of the optional type. If the optional contains a value, this value is unwrapped and stored in, or bound to, the constant unwrappedAuthorName. The if statement then executes the first block of code, within which you can safely use unwrappedAuthorName, as it’s a regular non-optional String.
If the optional doesn’t contain a value, then the if statement executes the else block. In that case, the unwrappedAuthorName variable doesn’t even exist.
You can see how optional binding is much safer than force unwrapping, and you should use it whenever an optional might be nil. Force unwrapping is only appropriate when an optional is guaranteed to contain a value.
Because naming things is so hard, it’s common practice to give the unwrapped constant the same name as the optional (thereby shadowing that optional):
if let authorName = authorName {
print("Author is \(authorName)")
} else {
print("No author.")
}
You can even unwrap multiple values at the same time, like so:
if let authorName = authorName,
let authorAge = authorAge {
print("The author is \(authorName) who is \(authorAge) years old.")
} else {
print("No author or no age.")
}
This code unwraps two values. It will only execute the if part of the statement when both optionals contain a value.
You can combine unwrapping multiple optionals with additional Boolean checks. For example:
if let authorName = authorName,
let authorAge = authorAge,
authorAge >= 40 {
print("The author is \(authorName) who is \(authorAge) years old.")
} else {
print("No author or no age or age less than 40.")
}
Here, you unwrap name and age and check that age is greater than or equal to 40. The expression in the if statement will only be true if name is non-nil, and age is non-nil, and age is greater than or equal to 40.
Now you know how to safely look inside an optional and extract its value if one exists.
Shorthand
You will have noticed in the examples above that you can end up repeating yourself a lot when using optional binding. Take the following example:
if let authorName = authorName {
print("The author is \(authorName)")
}
It might seem quite strange to write authorName twice there. Well, Swift has an answer to that. You can use a shorthand form of optional binding in cases where you don’t want to change the variable’s name. The above example can be rewritten as:
if let authorName {
print("The author is \(authorName)")
}
You can, of course, also unwrap multiple optionals at the same time with this syntax, like so:
if let authorName, let authorAge {
print("The author is \(authorName) who is \(authorAge) years old.")
}
You will find this comes in handy and saves some extra typing. It’s very common to need to simply check if a variable is non-nil and, if so, do some logic.
Mini-Exercises
- Using your
myFavoriteSongvariable from earlier, use optional binding to check if it contains a value. If it does, print out the value. If it doesn’t, print"I don’t have a favorite song." - Change
myFavoriteSongto the opposite of what it is now. If it’snil, set it to a string; if it’s a string, set it tonil. Observe how your printed result changes.
Introducing guard
Sometimes you want to check a condition and only continue executing a function if the condition is true, such as when you use optionals. Imagine a function that fetches some data from the network. That fetch might fail if the network is down. The usual way to encapsulate this behavior is using an optional, which has a value if the fetch succeeds, and nil otherwise.
Swift has a useful and powerful feature to help in situations like this: the guard statement. Let’s take a look at it with this contrived example for now:
func guardMyCastle(name: String?) {
guard let castleName = name else {
print("No castle!")
return
}
// At this point, `castleName` is a non-optional String
print("Your castle called \(castleName) was guarded!")
}
The guard statement comprises guard followed by a condition that can include both Boolean expressions and optional bindings, followed by else, followed by a block of code. The block of code covered by the else will execute if the condition is false. The block of code that executes if the condition is false must return. If you accidentally forget, the compiler will stop you — this is the guard statement’s true beauty.
You may hear programmers talking about the “happy path” through a function; this is the path you’d expect to happen most of the time. Any other path followed would be due to an error or another reason why the function should return earlier than expected.
Guard statements ensure the happy path remains on the left-hand side of the code; this is usually a good thing as it makes code more readable and understandable. Also, because the guard statement must return in the false case, the Swift compiler knows that if the condition was true, anything checked in the guard statement’s condition must be true for the remainder of the function.
You could simply use an if-let binding and return when it’s nil. However, when you use guard, you are explicitly saying that this must return if the statement in the guard is false. Thus the compiler can make sure that you have added a return. The compiler is providing some excellent safety for you!
Let’s see guard in a more “real world” example. Consider the following function:
func calculateNumberOfSides(shape: String) -> Int? {
switch shape {
case "Triangle":
return 3
case "Square":
return 4
case "Rectangle":
return 4
case "Pentagon":
return 5
case "Hexagon":
return 6
default:
return nil
}
}
This function takes a shape name and returns the number of sides the shape has. If the shape isn’t known, or you pass something that isn’t a shape, it returns nil.
You could use this function like so:
func maybePrintSides(shape: String) {
let sides = calculateNumberOfSides(shape: shape)
if let sides = sides {
print("A \(shape) has \(sides) sides.")
} else {
print("I don’t know the number of sides for \(shape).")
}
}
There’s nothing wrong with this, and it would work.
However, the same logic could be written with a guard statement like so:
func maybePrintSides(shape: String) {
guard let sides = calculateNumberOfSides(shape: shape) else {
print("I don’t know the number of sides for \(shape).")
return
}
print("A \(shape) has \(sides) sides.")
}
When your functions get more complex, guard comes into its own. You may have multiple guards at the top of the function that set up the initial conditions correctly. You’ll see it used extensively in Swift code.
Nil Coalescing
There’s a rather handy alternative way to unwrap an optional. You use it when you want to get a value out of the optional no matter what — and in the case of nil, you’ll use a default value. This operation is called nil coalescing. Here’s how it works:
var optionalInt: Int? = 10
var mustHaveResult = optionalInt ?? 0
The nil coalescing happens on the second line, with the double question mark (??), known as the nil coalescing operator. This line means mustHaveResult will equal either the value inside optionalInt or 0 if optionalInt contains nil. In this example, mustHaveResult contains the concrete Int value of 10.
The previous code is equivalent to the following:
var optionalInt: Int? = 10
var mustHaveResult: Int
if let unwrapped = optionalInt {
mustHaveResult = unwrapped
} else {
mustHaveResult = 0
}
Set the optionalInt to nil, like so:
optionalInt = nil
mustHaveResult = optionalInt ?? 0
Now mustHaveResult equals 0.
Challenges
Before moving on, here are some challenges to test your knowledge of optionals. It is best to try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: You Be the Compiler
Which of the following are valid statements?
var name: String? = "Ray"
var age: Int = nil
let distance: Float = 26.7
var middleName: String? = nil
Challenge 2: Divide and Conquer
First, create a function that returns the number of times an integer can be divided by another integer without a remainder. The function should return nil if the division doesn’t produce a whole number. Name the function divideIfWhole.
Then, write code that tries to unwrap the optional result of the function. There should be two cases: upon success, print "Yep, it divides \(answer) times", and upon failure, print "Not divisible :[".
Finally, test your function:
- Divide 10 by 2. This should print
"Yep, it divides 5 times." - Divide 10 by 3. This should print
"Not divisible :[."
Hint 1: Use the following as the start of the function signature:
func divideIfWhole(_ value: Int, by divisor: Int)
You’ll need to add the return type, which will be an optional!
Hint 2: You can use the modulo operator (%) to determine if a value is divisible by another; recall that this operation returns the remainder from the division of two numbers. For example, 10 % 2 = 0 means that 10 is divisible by 2 with no remainder, whereas 10 % 3 = 1 means that 10 is divisible by 3 with a remainder of 1.
Challenge 3: Refactor and Reduce
The code you wrote in the last challenge used if statements. In this challenge, refactor that code to use nil coalescing instead. This time, make it print "It divides X times" in all cases, but if the division doesn’t result in a whole number, then X should be 0.
Challenge 4: Nested Optionals
Consider the following nested optional — it corresponds to a number inside a box inside a box inside a box.
let number: Int??? = 10
If you print number you get the following:
print(number)
// Optional(Optional(Optional(10)))
print(number!)
// Optional(Optional(10))
Do the following:
- Fully force unwrap and print
number. - Optionally bind and print
numberwithif let. - Write a function
printNumber(_ number: Int???)that usesguardto print the number only if it is bound.
Key Points
-
nilrepresents the absence of a value. - Non-optional variables and constants are never
nil. -
Optional variables and constants are like boxes that can contain a value or be empty (
nil). - To work with the value inside an optional, you must first unwrap it from the optional.
- The safest way to unwrap an optional’s value is by using optional binding or nil coalescing. Use forced unwrapping only when appropriate, as it could produce a runtime error.
- You can
guard letto bind an optional. If the binding fails, the compiler forces you to exit the current function (or halt execution). This construction guarantees that your program never executes with an uninitialized value.