Logo for tanaschita.com

Understanding Swift Optionals

Learn about forced unwrapping, optional binding and implicit optionals. Understand how optionals work under the hood.

14 May 2021 · 5 min read

Optionals are a powerful core feature in Swift. The possibility to use optionals helps us to write more expressive and safe code.

Terms like forced unwrapping, optional binding or implicit optionals might be confusing at first, especially for those who are just starting out with Swift. We will go through those terms step-by-step in this article and also look at how optionals work under the hood.

Optional basics

An optional is used in situations where a value might be absent.

Imagine, we are building a user login wanting to leave the age field optional for the user. In situations like this where we need to model some kind of maybe missing state, an optional is an ideal choice.

struct User {
var age: Int? // the added ? makes an Int to an optional Int
}
var user = User()
user.age = 50
user.age = nil

By making the age variable an optional Int, we can know not only assign Int values to that variable, but also nil which represents the absence of a value.

To find out whether the age variable has a value or not, we can simply check for nil with an if statement:

if user.age == nil {
}

Unwrapping an optional

Sometimes we not only want to check if the value is nil or not but also to get the actual value. Unwrapping is the process of accessing the value inside the optional by converting the optional to a non-optional.

There are different ways to unwrap an optional, like optional binding or forced unwrapping.

Optional binding

A safe and common way to unwrap an optional is optional binding.

if let age = user.age {
// this block is executed if user.age has some value
// the local age variable is of type Int
} else {
// this block is executed if user.age is nil
}

In the example above, we create a new local variable age of type Int inside the if statement which binds the value of the optional if it has one.

Optional binding works the same way with a guard statement.

guard let age = user.age else {
// this block is executed if user.age is nil
return
}
// is executed if user.age has some value
// the local age variable is of type Int

Forced unwrapping

We can also use forced unwrapping to unwrap the optional by placing an exclamation point after the optional’s name.

let age = user.age! // age is of type Int

With forced unwrapping, the application will crash if the value is nil. Therefore, it should be used carefully. Forced unwrapping is also called unconditional unwrapping.

Optional chaining

Optional chaining allows to call properties and methods on an optional that might currently be nil.

if let street = user.address?.street {
// block is executed if address is not nil and street is not nil
}

This just means: If user has an address then go ahead and get its street. If the address is nil, just return nil as its street.

Using optional chaining is safe, the application will not crash if the optional is nil.

Nil coalescing

Sometimes we want to provide a default value when the optional is nil. This can be done in one line with nil coalescing.

let language = user.language ?? .en

The line means: If user.language is nil then use .en, otherwise use its unwrapped value.

Implicitly unwrapped optionals

Implicitly unwrapped optionals provide a way to declare an optional with no need of unwrapping when accessing.

struct User {
var age: Int! // the added ! makes an Int to an implicitly unwrapped Int
}
var user = User()
user.age = 15
user.age = nil

Just like an optional, the age property may contain a value or be nil, with the difference that it will be force unwrapped automatically when using.

It is useful in cases where we need to declare an optional since the value is not there yet, but we are sure that the variable will be set before we use it.

Like forced unwrapping, using implicitly unwrapped optionals is not safe. The application will crash if we access an implicitly unwrapped optional before assigning a value to it.

Optionals under the hood

When we look at the implementation of an optional, it is just an enum of two cases.

enum Optional<Wrapped>: ExpressibleByNilLiteral {
/// The absence of a value.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
}

But wait. Why are we writing Int? and not Optional<Int>? Or why are we using nil and not .none?

In fact, we can do both. Both ways do the same. The second one is just syntactic sugar for ease of reading and writing code.

var age: Optional<Int> = .none
// is the same as
var age: Int? = nil
age = .some(5)
// is the same as
age = 5

Since an optional is just an enum, we can also use it in a switch case.

switch user.age {
case .some(let age):
// if user.age has a value
case .none:
// if user.age is nil
}

This is also just another possibility to unwrap an optional.

Related tags

Written by