The Swift programming language brings a number of new features that make developing apps quicker, easier and safer than ever before. One of these new features is Optionals. They’re everywhere in Swift but can be confusing for people who are new to the language and frustrating to others who don’t understand them fully. The primary goal for his article then, is to bring some light to this potentially confusing world by taking a deep-dive into Optionals.
Table of Contents
What Problem Do Optionals Solve?
We’ll start by first looking at the problems optionals are trying to solve.
The problem of dealing with non-existent values in code has existed since the dawn of computing. In an ideal world, our code would always works with complete and well-defined data but this can’t always be the case. In many programming languages, programmers resort to using special values in these situations, values that they use to try and represent the absence of a value. One commonly used example of this is nil
which is often used to represent no value at all. There are however, issues with this.
In most programming languages nil
can only be used to represent the absence of reference types and generally they don’t support using nil
to represent the absence of value types. The result then is that for value types, developers still have to invent their own encodings to represent no value at all.
On top of this, although developers are able to come up with these special encodings, there is nothing built into most languages to communicate when the encodings may or may not be used. There is generally nothing in the declaration of functions or methods that highlights whether a nil
value will be accepted and similarly nothing to indicate whether those functions or methods can return a nil
value either.
In many cases, the only place for this type of information is the documentation. Many times though, the documentation either isn’t available or simply isn’t complete. The result is that we’re often left guessing about exactly how to use a particular function or method and this inevitably leads to mistakes and the introduction of hard to find bugs.
With Swift, things are different. Swift attempts to tackle these problems head-on by baking special syntax directly into the language to deal with the absence of values. This comes in the form of Optionals.
What Are Optionals?
In Swift, an optional type is a type that is used to indicate the potential absence of a value.
Optionals indicate a combination of two things. They indicate that if there is a value the value will be of a specified type whilst simultaneously indicating that equally there may be no value at all. In similar fashion to other languages, Swift uses nil
to represent the situation where there is no value at all.
By incorporating optional types directly into the language, Swift forces us to be clear about whether a particular variable, constant, parameter or return value can be nil
and does so right there in the code rather than forcing us to rely on the documentation for that sort of information.
If a type of a parameter or return value is marked as being an optional type, a value may be nil
. If not, then we can be 100% sure that the return value will never be nil
and the compiler will actually enforce this fact.
There are a number of advantages to this.
Firstly, it saves a lot of mental baggage when reading Swift code and significantly reduces our reliance on the documentation.
Secondly, by forcing us to think about and deal with the potential for nil
values whilst we’re writing the code it helps eliminate issues that may have previously only surfaced at run-time.
Finally, by being explicit about when values may or may not be nil
, we can also have the compiler perform a number of checks on our behalf. This last one is a huge advantage.
Whether we like it or not, our human brains have a limited capacity for thinking through the multitude of paths through our code. In many cases, identifying all the paths and associated edge cases can be extremely difficult. Add into that the task of identifying which of them may result in a value being nil
and it becomes almost impossible. The compiler however, is much better at this than we are and can help identify and check for these edge cases, looking for any nil
values that we may have missed. In doing so it prevents a whole class of runtime issues from ever occurring.
So, we’ve looked at what optionals are and we’ve looked at some of the advantages of having them baked into the language, but how do we actually declare them? Let’s take a look at that next.
Declaring Optional Types
In Swift, we declare an optional type by adding a question mark character (?
) to the end of a normal type. We can do this for any type of value in Swift, whether it be reference type or value type.
For example, say I wanted to declare an optional integer value, I would normally indicate this by declaring the constant or variable to be of type Int
. If instead I wanted to declare the constant or variable to be an optional though, I would add a ?
after the type to declare the constant or variable to be of a new optional integer type:
var optionalInt : Int?
The code above declares the optionalInt
variable to be of type Int?
which indicates that it may contain a value of type Int
but in certain circumstances may contain nil
.
Now, there is one thing I should point out at this point. Optional types are NOT the same type as their non-optional counterparts. We can see this if we try to run the following code:
a : Int = 1
b : Int? = 2
a + b // displays the following error:
// "Value of optional type 'Int?' not unwrapped: did you mean to use '!' or '?'?"
From the compilers perspective the optional type and its non-optional counterpart are two distinct types and cannot be used together in the same expression. If you think about it it’s actually logical. It doesn’t really make sense to try to add an integer value to something that may or may not contain another integer value.
In essence, we can think of an optional as a box which may or may not contain a value of the type we specify. In order to use the value in that box we must first open it up and check whether a value is actually present. In Swift, this box-opening process is called unwrapping and there are three major ways we can achieve it.
Unwrapping Optionals
Forced Unwrapping
The first way to unwrap an optional in Swift is something called force unwrapping. Forced unwrapping uses the force unwrapping operator and we write it as an exclamation mark (!
) directly after the name of the optional constant or variable.
The exclamation mark tells the compiler to unwrap the value in the optional and to use it. No if’s, no buts, no checking. Essentially it turns off all the normal checks that the compiler would perform to ensure that the box actually contained a value and instead moves that responsibility to you as the programmer. With this responsibility then comes some associated risk.
A lot of the time, we don’t really know whether an optional contains a value or not and if we use the forced unwrapping operator and it turns out that the optional doesn’t contain a value, we can inadvertently trigger a runtime exception which will crash our app.
If we want to use this forced unwrapping operator then, it’s prudent to first check that the optional contains a value before we force unwrap it. The simplest way of doing this is by using an if
statement:
var c : Int = 3
var d : Int? = 4
var result : Int
if (d != nil) {
result = c + d!
}
As you might expect though, performing these checks in Swift is pretty common and writing if
statements everywhere isn’t ideal so Swift provides us with an alternative syntax that pretty much eliminates the need to use forced unwrapping. This is something called optional binding.
Optional Binding
Optional binding is the second way to unwrap optionals in Swift and has syntax is built directly into the language to support it.
Optional binding allows us to check an optional and extract its value (if it exists) into a temporary constant or variable all in a single line of code. Support for this is provided in both the if
and while
statements in Swift. Let’s look at an example:
if let e = d {
// Inside the if statement 'e' contains the unwrapped value
// from the optional `d` if it is not nil.
result = c + e
}
As you can see, the syntax for optional binding is relatively simple.
We use the if
statement followed by the var
or let
keywords (depending on whether we want to declare a temporary variable or constant), followed by the assignment operator and then the name of the optional we want to check.
In simple terms, the code can be read as: “If the optional d
contains a value, set a new constant called e
to the value contained within that optional”.
If the optional contains a value (which we check using the if
statement), the temporary constant e
is then available as a local constant within the body of the if
statement eliminating the need for us to force unwrap the value held in d
. This completely removes the need to use the force unwrapping operator within the body of the if
statement.
Shadowing
It might seem strange, but when unwrapping an optional in this way, we can also unwrap the optional into a new temporary constant with the exactly same name as the original optional:
if let d = d {
// Inside the if statement `d` contains the unwrapped value
// from the original optional `d` if it is not nil.
result = c + d
}
This is an example of something called shadowing and the effect is to create, in this case, a new temporary constant called d
that is available within the inner scope of the if
statement that hides or shadows the variable d
in the outer scope.
You can see this if you check the types of the values. d
in the outer scope is of type Int?
(an optional) whilst d
in the inner scope is of type Int
(a normal value type). By using shadowing it means you don’t have to use explicit unwrapping to be able to use the value held in d
within the inner scope.
There are pluses and minuses to this approach though.
Some people like it as it means that they don’t have to think of or remember, a new variable name for the unwrapped value.
Others, however, dislike the approach as they believe that the unwrapped version should have a different name from the original (mainly to highlight the fact that the optional has been unwrapped).
When it boils down to it, there is no right or wrong answer to this though. My preference is to use a different variable or constant name as I think it makes things clearer but the choice is a purely stylistic one and one that you will have to choose for yourself.
Now before we wrap up optional binding, there is a couple of other examples I want to show you.
Binding Multiple Optionals on a Single Line
When Swift first came out, we could only perform optional binding upon a single optional at a time. This used to lead to something similar to the following (often nicknamed the pyramid of doom) where, as the number of optionals we wanted to bind increased, we got ever increasing levels of nested if
statements:
var k : Int? = 4
var l: Int? = 8
if let m = k {
if let n = l {
// Do something with m and n
} else {
print("l contained nil")
}
} else {
print("k contained nil")
}
Swift 1.2 however, introduced the ability to bind multiple optionals on a single line. This helped us write code in a more compact form with the first branch of the if
statement only being executed if ALL of the optional bindings were successful:
if let m = k, n = l {
print(m + n)
} else {
print("k or l contained nil")
}
// prints "12"
We also have the ability to combine one or more bindings with an optional boolean expression using a where
clause. Again, all on a single line. In this case, the binding only takes place if all the optional(s) contain a value and the where
clause evaluates to true
:
var o : Int? = 4
if let p = o where o > 2 {
print(p)
}
// prints "4"
Implicitly Unwrapped Optionals
If you start using optionals for any length of time, you’ll soon notice the additional layer of syntax that is associated with them, a layer of syntax that can often make our code more difficult to read. In certain specific cases though, cases where we know that our optionals will contain a non-nil value, Swift allows us to dispense with the additional optionals syntax and allows us to use optionals just like any other constant or variable. To do this though we have to mark our optionals as being implicitly unwrapped, the third mechanism by which we can unwrap optionals in Swift.
Implicitly unwrapped optionals are a bit of a strange beast. On the one hand, they behave like optionals (in that they can be set to nil and we can check them for nil
equality) but on the other hand, the compiler will automatically unwrap them every time they are accessed which allows us to dispense with all the optional syntax we’ve been using up until now.
To mark an optional as being an implicitly unwrapped (rather than a plain old optional) we use an exclamation mark (!
) after the type instead of a question mark (?
). You can see this in the example below:
// Without Implicit Unwrapping
let possibleInt : Int? = 4
let forcedInt: Int = possibleInt!
// With Implicit Unwrapping
let assumedInt : Int! = 4
let implicitInt = assumedInt
As you can see, by marking the assumedInt
variable as being implicitly unwrapped, we no longer need to use the forced unwrapping operator that we used in the first part of the example. Instead, we can simply access the variable as if it were a non-optional type.
There is a catch though.
By marking an optional as being implicitly unwrapped, we are making a promise to the compiler that when the optional is accessed, the optional will always contain a non-nil
value.
In similar fashion to the forced unwrap operator we saw earlier, if we break this promise (and the implicitly unwrapped optional doesn’t contain a value when accessed), Swift will trigger a runtime error and crash our application:
var greeting: String! = "hello world"
greeting.capitalizedString // Returns "Hello World"
greeting = nil // As it's an optional we can set its value to nil
greeting.capitalizedString // CRASH! - Can't send messages to nil!
With this in mind then, implicitly unwrapped optionals come with all the same caveats as forced unwrapping.
We should only ever mark an optional as being implicitly unwrapped if we are 100% sure that it will NOT be nil
at the time it is accessed and if there is any doubt we should either use a non-optional value (if we can) or a normal optional value if need be.
In general, implicitly unwrapped optionals are pretty dangerous beasts and run a high risk of causing runtime exceptions but with that said, there are a couple of specific situations in Swift where using them is essential. Let’s have a look at a these next.
Using Implicitly Unwrapped Optionals During Initialisation
When it comes to the initialisation of classes in Swift, there are some pretty strict rules. One of these rules (and I quote directly from the Swift 2.0 Programming Language Guide) is the fact that:
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
In reality, always satisfying this statement is pretty tricky. There are a number of reasons for this.
Sometimes we don’t have enough information at the time we’re creating a class or struct to provide a sensible set of initial values. Sometimes it just doesn’t make sense to initialise the properties at that time. Whatever the reason, it is not uncommon to want to exit the initialisation phase of a class or struct without having fully initialised the class. UIViewControllers are one example of this.
In the case of a UIViewController
(and many other view-based classes for that matter), initialisation is separated into two distinct phases.
In the first phase, the instance of the UIViewController
class is created and initial values are assigned to the different properties of the instance (usually via some variant of their init
functions). The problem though is that at that point, the IBOutlet
s for that class will not have been connected because the views for the class have not been loaded. This leaves the class partially initialised at the end of the init
function.
It is not until the second phase of the initialisation that the views for the class are loaded. At this point the view and any of the sub-views created within the loadView
or viewDidLoad
methods are added to the view hierarchy and the IBOutlets
are hooked up before being presented on screen. The key point to note here though is that this is done after the main initialisation of the class is complete.
The problem then is how to satisfy Swift’s requirements that all the stored properties of the class have appropriate initial values by the end of initialisation even though we can’t yet connect the views and IBOutlets
. This is where implicitly unwrapped optionals come in.
By defining our IBOutlet
properties as being implicitly unwrapped, were are able to satisfy Swift’s requirements. This is because as optionals they are, by default, assigned an initial value of nil
and are therefore deemed to be initialised in the eyes of the Swift compiler.
The advantage of marking them as implicitly unwrapped optionals (rather than being simple optionals), is that once hooked up, the properties can still be referred to like normal non-optional properties instead of having to use the additional optional syntax we already seen:
var label : UILabel!
//...
label.text = "Hello World"
As you can see, it’s a pretty specific case, but works quite nicely with implicitly unwrapped optionals.
Using Implicitly Unwrapped Optionals With Failable Initializers
A second, similar example of using implicitly unwrapped optionals in Swift is using them within failable initialisers.
When writing Swift code, it can sometimes be useful to define a class, structure or enumeration for which initialisation can fail. There could be any number of reasons for this. Incorrect or missing initialisation parameters, the absence of some external resources or one of a whole host of other reasons.
To cope with these situations, Swift allows us to define one or more failable initializers as part of a structure, class or enumeration definition. These failable initialisers are failable because they may either return an initialised object of the specified type or they can fail and return nil
instead.
To mark an intializer as failable, we add a ?
after the init
keyword but before the method parentheses:
// This WON'T compile!!
class PersonClass {
let name : String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
// Returns the error: All stored properties of a class must be
// initialized before returning nil from an initializer.
As you can see though, in this example we have an issue. It won’t compile.
In this example, I’m declaring a class that has a failable init
method and in this case, intialization will fail if the name
that is passed into the initialiser is an empty String
.
Now, the general rule within failable initialisers is to return nil
as soon as an error is encountered. By definition though, this means that not all properties within the object may have been initialized by the time the initialiser returns. Swift has picked up on this fact and as a result won’t compile our code as it contravenes the ‘all properties must be initialised rule’.
As we’ve just discussed though, implicitly unwrapped optionals allow us to define a property to have an initial value of nil
when a valid value cannot yet be assigned and yet still allow us to access those values without all baggage of additional optional syntax. We can make use of this fact with failable initialisers.
In this example then if we change the name
property from being a normal String
type to being an implicitly unwrapped optional, we can satisfy Swifts requirements and also still access the properties with normal property syntax:
class PersonClass {
var name : String!
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
As a side note, in order to make this work in Swift 2.0, we also have to change the name
property from being a constant to being a variable.
This is due to a loophole in earlier versions of Swift that could result in constant properties being assigned values multiple times within an initialiser. Apple has since closed this loophole but as a result, we now have to use a variable in this situation rather than a constant. As can be seen from this forum thread Chris Lattner’s view is that this a short-coming in the language, so it may yet be fixed in future Swift versions but for now, just keep this in mind.
Anyway, I’m going to leave implicitly unwrapped optionals there for now. There’s a lot to them and a lot to get your head around but just try to remember that they are designed for some pretty specific use cases in Swift and in those situations their great, but beyond those specific scenarios they (like force unwrapping) run a high-risk of runtime errors so use them with care.
So with implicitly unwrapped optionals put to bed, let’s wrap things up for today with a look at the nil
coalescing operator, a new operator in Swift that can be particularly useful when used in conjunction with optionals.
The nil
Coalescing Operator
Sometimes when an optional has no value, you want to provide a default one. We could obviously do this with an if statement or use the ternary operator:
let optionalValue = myFunc() // May return `nil` 7 + (optionalValue != nil ? optionalValue! : 0)
Note: In this case, the question mark is not used to indicate an optional type, it is part of the syntax for a ternary operator.
If you’re not familiar with the ternary operator, the second line of code in the example essentially says: “If optionalValue
has a value, use it, otherwise use 0
“.
With the nil
coalescing operators, we can improve on this though.
The nil coalescing operator is written as a double question mark (??
) and is a way of shortening the expression above. It has a syntax similar to the ternary operator but allows us to dispense with the check for nil
:
7 + (optionalValue ?? 0)
All it does is return the unwrapped optional value if the optional contains a value or the value after the operator if the optional is nil
. It’s a convenience more than anything else but does make our code slightly easier to read.
Anyway, I’m going to leave it there for today. As ever, if you have any questions, observations, or if I’ve got anything wrong, then please get in touch.