From Apple's documentation:
You can use
if
andlet
together to work with values that might be missing. These values are represented as optionals. An optional value either contains a value or containsnil
to indicate that the value is missing. Write a question mark (?
) after the type of a value to mark the value as optional.
Why would you want to use an optional value?
Best Answer
An optional in Swift is a type that can hold either a value or no value. Optionals are written by appending a
?
to any type:Optionals (along with Generics) are one of the most difficult Swift concepts to understand. Because of how they are written and used, it's easy to get a wrong idea of what they are. Compare the optional above to creating a normal String:
From the syntax it looks like an optional String is very similar to an ordinary String. It's not. An optional String is not a String with some "optional" setting turned on. It's not a special variety of String. A String and an optional String are completely different types.
Here's the most important thing to know: An optional is a kind of container. An optional String is a container which might contain a String. An optional Int is a container which might contain an Int. Think of an optional as a kind of parcel. Before you open it (or "unwrap" in the language of optionals) you won't know if it contains something or nothing.
You can see how optionals are implemented in the Swift Standard Library by typing "Optional" into any Swift file and ⌘-clicking on it. Here's the important part of the definition:
Optional is just an
enum
which can be one of two cases:.none
or.some
. If it's.some
, there's an associated value which, in the example above, would be theString
"Hello". An optional uses Generics to give a type to the associated value. The type of an optional String isn'tString
, it'sOptional
, or more preciselyOptional<String>
.Everything Swift does with optionals is magic to make reading and writing code more fluent. Unfortunately this obscures the way it actually works. I'll go through some of the tricks later.
Note: I'll be talking about optional variables a lot, but it's fine to create optional constants too. I mark all variables with their type to make it easier to understand type types being created, but you don't have to in your own code.
How to create optionals
To create an optional, append a
?
after the type you wish to wrap. Any type can be optional, even your own custom types. You can't have a space between the type and the?
.Using optionals
You can compare an optional to
nil
to see if it has a value:This is a little confusing. It implies that an optional is either one thing or another. It's either nil or it's "Bob". This is not true, the optional doesn't transform into something else. Comparing it to nil is a trick to make easier-to-read code. If an optional equals nil, this just means that the enum is currently set to
.none
.Only optionals can be nil
If you try to set a non-optional variable to nil, you'll get an error.
Another way of looking at optionals is as a complement to normal Swift variables. They are a counterpart to a variable which is guaranteed to have a value. Swift is a careful language that hates ambiguity. Most variables are define as non-optionals, but sometimes this isn't possible. For example, imagine a view controller which loads an image either from a cache or from the network. It may or may not have that image at the time the view controller is created. There's no way to guarantee the value for the image variable. In this case you would have to make it optional. It starts as
nil
and when the image is retrieved, the optional gets a value.Using an optional reveals the programmers intent. Compared to Objective-C, where any object could be nil, Swift needs you to be clear about when a value can be missing and when it's guaranteed to exist.
To use an optional, you "unwrap" it
An optional
String
cannot be used in place of an actualString
. To use the wrapped value inside an optional, you have to unwrap it. The simplest way to unwrap an optional is to add a!
after the optional name. This is called "force unwrapping". It returns the value inside the optional (as the original type) but if the optional isnil
, it causes a runtime crash. Before unwrapping you should be sure there's a value.Checking and using an optional
Because you should always check for nil before unwrapping and using an optional, this is a common pattern:
In this pattern you check that a value is present, then when you are sure it is, you force unwrap it into a temporary constant to use. Because this is such a common thing to do, Swift offers a shortcut using "if let". This is called "optional binding".
This creates a temporary constant (or variable if you replace
let
withvar
) whose scope is only within the if's braces. Because having to use a name like "unwrappedMealPreference" or "realMealPreference" is a burden, Swift allows you to reuse the original variable name, creating a temporary one within the bracket scopeHere's some code to demonstrate that a different variable is used:
Optional binding works by checking to see if the optional equals nil. If it doesn't, it unwraps the optional into the provided constant and executes the block. In Xcode 8.3 and later (Swift 3.1), trying to print an optional like this will cause a useless warning. Use the optional's
debugDescription
to silence it:What are optionals for?
Optionals have two use cases:
Some concrete examples:
middleName
orspouse
in aPerson
classweak
properties in classes. The thing they point to can be set tonil
at any timeBoolean
Optionals don't exist in Objective-C but there is an equivalent concept, returning nil. Methods that can return an object can return nil instead. This is taken to mean "the absence of a valid object" and is often used to say that something went wrong. It only works with Objective-C objects, not with primitives or basic C-types (enums, structs). Objective-C often had specialized types to represent the absence of these values (
NSNotFound
which is reallyNSIntegerMax
,kCLLocationCoordinate2DInvalid
to represent an invalid coordinate,-1
or some negative value are also used). The coder has to know about these special values so they must be documented and learned for each case. If a method can't takenil
as a parameter, this has to be documented. In Objective-C,nil
was a pointer just as all objects were defined as pointers, butnil
pointed to a specific (zero) address. In Swift,nil
is a literal which means the absence of a certain type.Comparing to
nil
You used to be able to use any optional as a
Boolean
:In more recent versions of Swift you have to use
leatherTrim != nil
. Why is this? The problem is that aBoolean
can be wrapped in an optional. If you haveBoolean
like this:it has two kinds of "false", one where there is no value and one where it has a value but the value is
false
. Swift hates ambiguity so now you must always check an optional againstnil
.You might wonder what the point of an optional
Boolean
is? As with other optionals the.none
state could indicate that the value is as-yet unknown. There might be something on the other end of a network call which takes some time to poll. Optional Booleans are also called "Three-Value Booleans"Swift tricks
Swift uses some tricks to allow optionals to work. Consider these three lines of ordinary looking optional code;
None of these lines should compile.
String
the types are differentI'll go through some of the implementation details of optionals that allow these lines to work.
Creating an optional
Using
?
to create an optional is syntactic sugar, enabled by the Swift compiler. If you want to do it the long way, you can create an optional like this:This calls
Optional
's first initializer,public init(_ some: Wrapped)
, which infers the optional's associated type from the type used within the parentheses.The even longer way of creating and setting an optional:
Setting an optional to
nil
You can create an optional with no initial value, or create one with the initial value of
nil
(both have the same outcome).Allowing optionals to equal
nil
is enabled by the protocolExpressibleByNilLiteral
(previously namedNilLiteralConvertible
). The optional is created withOptional
's second initializer,public init(nilLiteral: ())
. The docs say that you shouldn't useExpressibleByNilLiteral
for anything except optionals, since that would change the meaning of nil in your code, but it's possible to do it:The same protocol allows you to set an already-created optional to
nil
. Although it's not recommended, you can use the nil literal initializer directly:Comparing an optional to
nil
Optionals define two special "==" and "!=" operators, which you can see in the
Optional
definition. The first==
allows you to check if any optional is equal to nil. Two different optionals which are set to .none will always be equal if the associated types are the same. When you compare to nil, behind the scenes Swift creates an optional of the same associated type, set to .none then uses that for the comparison.The second
==
operator allows you to compare two optionals. Both have to be the same type and that type needs to conform toEquatable
(the protocol which allows comparing things with the regular "==" operator). Swift (presumably) unwraps the two values and compares them directly. It also handles the case where one or both of the optionals are.none
. Note the distinction between comparing to thenil
literal.Furthermore, it allows you to compare any
Equatable
type to an optional wrapping that type:Behind the scenes, Swift wraps the non-optional as an optional before the comparison. It works with literals too (
if 23 == numberFromString {
)I said there are two
==
operators, but there's actually a third which allow you to putnil
on the left-hand side of the comparisonNaming Optionals
There is no Swift convention for naming optional types differently from non-optional types. People avoid adding something to the name to show that it's an optional (like "optionalMiddleName", or "possibleNumberAsString") and let the declaration show that it's an optional type. This gets difficult when you want to name something to hold the value from an optional. The name "middleName" implies that it's a String type, so when you extract the String value from it, you can often end up with names like "actualMiddleName" or "unwrappedMiddleName" or "realMiddleName". Use optional binding and reuse the variable name to get around this.
The official definition
From "The Basics" in the Swift Programming Language:
To finish, here's a poem from 1899 about optionals:
Yesterday upon the stair
I met a man who wasn’t there
He wasn’t there again today
I wish, I wish he’d go away
Antigonish
More resources: