Optionals in Swift

Behind the scenes of one of Swift’s biggest differentiators

Moritz Lang
Slashkeys Engineering
8 min readJun 10, 2018

--

No matter how long your Swift journey has been so far, you definitely heard about Optionals or at least saw some ? or ! in your code. In this post, I’m going to show you what Optionals are, how they work under the hood and some best practices for dealing with them.

Although this post uses some more advanced terms like Generics, you should still be able to understand the key parts of this app if you never heard of such a thing.

What’s a statically typed language?

Swift is a statically typed language. This means that in order for the compiler to work successfully, every variable, constant, and function must have their type declared in advance (before the compilation).

// No type: Compiler Error ⚠️
var x

// Explicitly typed integer
var x: Int = 0

// Implicitely typed integer
var y = 0

As demonstrated in the example above, you can either explicitly specify the type, or let the compiler infer it based on its value.

Declaring a type is a one-time-thing. After declaring a variable, constant, or function, its type cannot change. The following could not be compiled:

var x = 0
x = "Evil String"

Strongly typed

Furthermore, Swift is strongly typed. This means you cannot e.g. call a function that expects a String with an Int value. Because it’s statically typed the language already knows all the types before compile time, so it can shout at you before even doing the hard work.

What’s an Optional?

Let’s imagine we work on an app that deals with GitHub user profiles. Each user must have a username and optionally have a bio. The JSON representation of a user could look like this:

{
"username": "slashmo",
"bio": "Freelance iOS-Developer"
}

If we were to write a Swift user type our first attempt could be this:

struct User {
let username: String
var bio: String
}

let user = User(username: "slashmo", bio: "Freelance iOS-Developer")

But, as stated above the bio of a user could also be empty, or nil in Swift terms. Our current User struct is not able to provide us with this functionality:

let user = User(username: "slashmo", bio: nil)

The reason for this is actually pretty simple. As you now know Swift is strongly typed, so it expects a value of type String for the bio property, because we explicitly said so in its declaration. And because nil is not a String value the compiler doesn’t want to compile it.

The Optional type

For such use cases, Swift has a specific type, the Optional<Wrapped>. It’s an enumeration that’s generic over Wrapped. The enumeration consists of two cases, .none and .some(Wrapped).

Put simply, an Optional is like a box that either holds a value or nothing. The Optional works with every other type by wrapping the optional value inside. This means you can have Optional Strings, Optional Ints, Optional Users and even Optional Optionals. 🔥

And because it’s just another type we can use it to declare variables, constants, and functions, just like String, Int, …

Applied to the GitHub example our User struct now looks like this:

struct User {
let username: String
var bio: Optional<String>
}

let user = User(username: "slashmo", bio: .none)
let nerd = User(username: "slashmo", bio: .some("Freelance iOS-Developer"))

Syntax Sugar

To be honest, the above doesn’t look as Swifty as we’re used to. Especially the .some case with its associated String is unnecessarily verbose because it’s quite obvious that we have some value if a String is associated with it 😊. Also, it’s a bit tedious to always declare optional types as Optional<Wrapped>.

There is a far simpler way to declare Optionals and set their values:

struct User {
let username: String
var bio: String?
}

let user = User(username: "slashmo", bio: nil)
let nerd = User(username: "slashmo", bio: "Freelance iOS-Developer")

By suffixing any type with ? we wrap it inside an Optional type. And because Optional conforms to ExpressibleByNilLiteral the .none case can be expressed as nil. Optional also has an initializer taking a non-optional instance of Wrapped which lets us skip the .some if we have a value. Much nicer, isn’t it? 💅

How can we work with optional values?

Now that we’re able to declare Optionals the next step is to actually work with them. Let’s say, e.g. that in our imaginary GitHub app we want to show the bio on a users profile if it’s set.

Before we can use a value wrapped inside an Optional we first have to unwrap it (get it out of the box).

There are several ways to unwrap an Optional. I’ll explain them one by one and compare the different approaches along the way.

Forced unwrapped Optional

The first one I’m going to show you is the forced unwrapped Optional.

bioLabel.text = user.bio!

The ! after the property tells the compiler that we are 100% sure that user.bio holds a value, or in other words that it is not nil. The result of this expression is the unwrapped String which we can assign to the labels text property.

If, in the above snippet, the users’ bio were nil our program would crash and throw this message:

Unexpectedly found nil while unwrapping an Optional.

To avoid this from happening we could first check if the Optional is nil and then force-unwrap the value only if the check was successful:

if user.bio != nil {
bioLabel.text = user.bio!
} else {
// bio is nil, hide the label
}

Again, this is not very Swifty because we basically repeat ourselves unnecessarily. First, we check that the Optional is not nil, but then we have to force-unwrap it anyways.

Optional binding

As hinted above, there’s a more elegant solution. Optional binding allows us to bind a value of an Optional, if one exists, to a constant or variable.

if let bio = user.bio {
bioLabel.text = bio
} else {
// bio is not available in here, because user.bio is nil
}

Now, we don’t have an explicit condition but rather an assignment to a new constant called bio. This constant is only available inside the if-block and it holds the unwrapped value. Not only is this more readable than the previous example, but it also gives us the benefit of only unwrapping once. Let’s say we wanted to use the users’ bio twice. In the case of force-unwrapping, we’d have to write the ! for each usage. With optional binding, we unwrap once and use the new constant every time we want to use the value.

Optional binding with early exit

Depending on the use case, the if let-block can get quite large, often times larger than the else-block. In this case, we might want to use a guard let instead which gives us the benefit of not having to go one indentation level deeper just to access an Optionals value.

guard let bio = user.bio else {
// bio is not available here, because user.bio is nil
return
}
bioLabel.text = bio

As you can see, the bio constant is now available in the same scope. Keep in mind that with guard statements we always have to have an else clause, which comes first, hence the name early exit. This else clause has to exit unless we call a non-returning function like fatalError().

Nil-coalescing

What if we want to show a default text if the users’ bio is nil?

if let bio = user.bio {
bioLabel.text = bio
} else {
bioLabel.text = "No bio"
}

This looks quite verbose. We repeated the assignment part, only to set a different value depending on the Optional’s case. Normally, we would use a ternary operation in such a case to clean things up a bit:

bioLabel.text = user.bio != nil ? user.bio! : "No bio"

Although this works it’s also not that clear, because we first check if the Optional is nil and then force-unwrap the value if that’s not the case. Swift provides another way by introducing the Nil-coalescing operator.

bioLabel.text = user.bio ?? "No bio"

This has the exact same result but now does the nil-check and unwrapping implicitly.

Optional chaining

For the sake of this example, let’s add an optional friend to our User type:

struct User {
// ...
var friend: User?
}

This friend is of type Optional<User> and therefore also has an optional bio. In another view of our imaginary app, we might want to show that friends bio if it’s available. With the power of optional binding we could come up with this:

if let friend = user.friend {
if let friendBio = friend.bio {
friendBioLabel.text = friendBio
}
}

However, this doesn’t scale very well as it adds another level of indentation, although in the second scope we only use the friend constant to unwrap the second optional, their bio. Let’s think of this as a chain instead.

user.friend?.bio

Voilà, now we have optional chaining. Because friend is an Optional we can’t directly access it’s bio. Instead, we start an optional chain by writing a ? between each part of the chain.

We can use this optional chain in combination with optional binding and end up with a pretty elegant solution:

if let friendBio = user.friend?.bio {
friendBioLabel.text = friendBio
} else {
// either friend or bio is nil. Which one is undefined
}

Optional chains can be endless and also contain non-optional parts in between optional parts.

Implicitly unwrapped Optional

As you may already know, to successfully initialize an instance of any type, all its non-optional properties have to be set before returning from the initializer. In other words: If we want to create an instance without providing a default value to a property that property has to be an Optional. This means that we’d always have to unwrap this Optional where we want to use it. Because this can be very cumbersome in use-cases where we are 100% sure that a property holds a value (but it was not provided by us), instead of explicitly unwrapping it for every usage we can implicitly unwrap it by suffixing the type with ! instead of ?.

@IBOutlet weak var bioLabel: UILabel!

If you’re an iOS-Develop working in interface builder you know this kind of line. It creates a connection between your visual interface and your code. Now, because we cannot set the value itself it needs to be an Optional. If a connection is established the value will never be nil, so it’s safe to implicitly unwrap it. However, if the value is nil, e.g. if we forgot to create the connection, and we use the property the program will crash, giving us the same error message as the forced unwrapped Optional.

Summary

Now that you know what Optionals are all about and how to work with them let’s recap some of the key points of this post.

  • Optional<Wrapped> is an enum with two cases: none & some(Wrapped)
  • Optionals are declared by suffixing another type with ?: String?
  • Instead of writing .none we use nil: user.bio = nil
  • Instead of writing .some(<value>) we use <value>: user.bio = "Bio"
  • To access an Optional’s value we have to unwrap it
  • By suffixing an Optional variable / constant with ! we force-unwrap it, which causes the program to crash if it was nil instead
  • With the help of optional binding we are able to bind the unwrapped value to a new constant / variable to use it inside a block: if let friend = user.friend { print(friend) }
  • Optional binding works with both if & guard
  • The nil-coalescing operator allows us to assign either the unwrapped value or a default value depending on the case of the Optional: label.text = user.bio ?? "Default"
  • With optional chaining we can create a call chain of optional variables, constants & functions: user.friend?.bio?.uppercased
  • If we precede a type with ! instead of ? we create an implicitly unwrapped Optional, which force unwraps the Optional every time we use it automatically

--

--

Moritz Lang
Slashkeys Engineering

Swift Developer / Software Engineering student @codeuniversity