Category: Swift Language

Swift Optionals

I saw in a talk by Paul Hudson at NSSpain 2018 that optionals are what people learning Swift struggle with the most. That makes optionals a good topic to cover on a site called Swift Dev Journal.

What Is an Optional?

An optional is a data type for a variable where the variable either exists or doesn’t exist, in which case it’s nil. Pretty much anything in Swift can be an optional, including integers, strings, views, structs, and classes. Add ? to a variable’s type to make that variable an optional.

var description: String?

In this example if there’s a description, the description variable contains a string with the description. If there’s no description, description is nil.

Implicitly Unwrapped Optionals

A special kind of optional is an implicitly unwrapped optional. An implicitly unwrapped optional assumes the value of the optional is not nil. If the value is nil and you access that value in your app, the app crashes. Add ! to a variable’s type to make that variable an implicitly unwrapped optional.

@IBOutlet var textView: UITextView!

Outlets are the most common use of implicitly unwrapped optionals. You can assume your outlets are not nil after loading a storyboard or xib file. If you forget to connect the outlets, your app will crash, but in that case, you want the app to crash so you can connect the outlets.

Outside of outlets you should avoid using implicitly unwrapped optionals because they’re not safe. Every implicitly unwrapped optional in your code is a potential crash.

Force Unwrapping

Force unwrapping an optional uses the value without checking if it’s nil. Add ! to an optional variable to force unwrap it.

print(description!)

Don’t force unwrap implicitly unwrapped optionals. They’re already unwrapped. If you add ! to an implicitly unwrapped optional, you’ll get a compiler error.

Force unwrapping is dangerous. In this example if description is nil, the app will crash.

You should avoid force unwrapping optionals because force unwrapping isn’t safe. Every optional you force unwrap is a potential crash.

Safely Unwrapping with if-let and guard

When writing Swift code, you often end up checking if an optional value is not nil, then assigning a constant with something from the optional value. Suppose you wanted to get a view controller’s parent (which is an optional because a view controller may not have a parent) and do something with it, you could write code like the following:

if parent != nil {
    let parentViewController = parent
    // Do something with parentViewController
}

But this code is a bit tedious to write. Swift provides the if-let statement to combine the nil check and the assignment into one statement.

if let parentViewController = parent {
    // Do something with parentViewController
}

If parent is nil, the code inside the if block won’t execute.

Another way to safely unwrap an optional is to use a guard statement. The guard statement lets you exit quickly if the optional value is nil.

guard let parentViewController = parent else { 
    return 
}
// Do something with parentViewController   

Crashing with Swift Optionals

If you have done any iOS or Mac programming in Swift, you have probably had your app crash with the following message in Xcode’s debugger:

Fatal error: Unexpectedly found nil while unwrapping an Optional value

If you don’t understand this error message, keep reading. In this article you’ll learn what this error message means, common causes of the message, and ways to fix your code so your app stops crashing.

What Does the Message Mean?

Before I can tell you what the error message means, I need to explain Swift optionals. An optional is a data type for a variable where the variable either exists or doesn’t exist, in which case it’s nil. Pretty much anything in Swift can be an optional, including integers, strings, arrays, table views, structs, and classes. The following code shows an example of declaring an optional variable:

var description: String?

A special kind of optional is an implicitly unwrapped optional. While an optional can either exist or not exist, an implicitly unwrapped optional must exist. If an implicitly unwrapped optional is nil and you attempt to use it, the app crashes. The following code shows an example of using an implicitly unwrapped optional:

print(description!)

If description exists, the code will print the description, but if description is nil, the code crashes.

Now to answer the question in the section heading. The error message Fatal error: Unexpectedly found nil while unwrapping an Optional value is saying the app is implicitly unwrapping a nil optional value. The error is similar to accessing a nil pointer in C, C++, or Objective-C.

What Causes the Error?

The general cause of the error is having an implicitly unwrapped optional that’s nil. But you’re looking for specific causes. I can think of three common sources of implicitly unwrapped optionals that are nil.

The first common source is forgetting to connect the outlets for your user interface elements. Outlets in Interface Builder are usually declared as implicitly unwrapped optionals because you can assume the outlet is going to exist after loading the storyboard or xib file. But if you forget to connect the outlet, the outlet is nil and your app will crash when it tries to use the outlet. The following screenshot shows what a disconnected outlet looks like in Xcode:

DisconnectedOutlet

The disconnected outlet is the circle above Line 14. A connected outlet has a filled-in circle.

The second common source is using as! to downcast to a specific type. Look at the following code to instantiate a view controller from a storyboard:

let documentViewController = storyBoard.instantiateViewController(
    withIdentifier: "DocumentViewController") 
    as! DocumentViewController

This code will crash in any of the following situations:

  • I forgot to give the view controller an identifier in the storyboard.
  • The identifier in the storyboard doesn’t match the string in the code.
  • I forgot to set the class of the view controller in the storyboard.

With implicitly unwrapped optionals it doesn’t take much to cause a crash.

The last common source is using implicitly unwrapped optionals in your code. If you see an exclamation point at the end of any of your variable names, you have a potential crash in your code. What doesn’t help matters is when you have a compiler syntax error in your code involving optionals, Xcode’s suggested fix is to make an implicitly unwrapped optional. Following Xcode’s advice makes your code more likely to crash.

How Do You Fix the Error?

The general fix to avoid these crashes is to avoid using implicitly unwrapped optionals. The following techniques will help you avoid crashes caused by implicitly unwrapped optionals:

  • Connect your user interface elements to the outlets in your code.
  • Use as? instead of as! when downcasting.
  • Use if let or guard statements to safely unwrap your optionals.

To connect your user interface elements to your outlets, open Xcode’s assistant editor so your source code file and your storyboard or xib file are both open. Select the user interface element in the storyboard or xib file. Hold the Control key down and drag to the outlet in your code to make the connection.

I can demonstrate the last two techniques by fixing the earlier example of instantiating a view controller from the storyboard. Using an if let statement and as? to downcast is enough to fix the code. The following code makes sure the view controller has been instantiated, then presents the view controller:

if let documentViewController = storyBoard.instantiateViewController(
    withIdentifier: "DocumentViewController") 
    as? DocumentViewController {

    present(documentViewController, animated: true, completion: nil)
}

Now if I forgot to give the view controller an identifier or misspelled the name of the identifier, the app won’t crash. The app won’t present the view controller, but at least there won’t be a crash.