Optionals in Swift explained: 5 things you should know

Optionals are in the core of Swift and exist since the first version of Swift. An optional value allows us to write clean code with at the same time taking care of possible nil values.

If you’re new to Swift you might need to get used to the syntax of adding a question mark to properties. Once you get used to them you can actually start benefiting from them with, for example, extensions.

What is an optional value in Swift?

Before we dive into the list of things you should know about optionals it’s first good to know the basics.

Properties, methods, and subscripts can return an optional which basically means that it either returns a value if it exists or otherwise nil. Multiple queries can be chained together which is called Optional chaining. This is an alternative to Force Unwrapping which is explained in more detail later on.

The following code example shows the definition of an optional String and optional chaining to print out the count of characters.

let name: String? = "Antoine van der Lee"
print(name?.count ?? 0)

Note: The ?? operator (nil coalescing operator) will be explained later on.

1. Forced Unwrapping optionals in Swift

Force Unwrapping an optional either returns the value if it exists or triggers a runtime error when the optional is nil.

But before we dive into force unwrapping, lets first go over the possibilities of unwrapping an optional without force.

How to unwrap an optional?

There are multiple ways to unwrap an optional in Swift. You can use a guard statement:

let name: String? = "Antoine van der Lee"
guard let unwrappedName = name else {
    return
}
print(unwrappedName.count)

Or you can use an if let statement:

let name: String? = "Antoine van der Lee"
if let unwrappedName = name {
    print(unwrappedName.count)
}

Or you can use the double question marks operator, also known as the nil coalescing operator. This will either return the optional value if it exists or the default value, which in this case is defined as zero:

let name: String? = "Antoine van der Lee"
print(name?.count ?? 0)

Force unwrapping an optional using the exclamation mark (!)

An optional can be forced unwrapped using the exclamation mark (!) directly after the optional value.

var name: String? = "Antoine van der Lee"
print(name!.count)

Whenever the name variable in the above example would be set to nil it would cause a fatal runtime error as following:

Fatal error: Unexpectedly found nil while unwrapping an Optional value

Unwrapping can be chained

Optional chaining can be done like this:

struct BlogPost {
    let title: String?
}

let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post?.title?.count ?? 0)

And the same counts for force unwrapping optionals:

let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post!.title!.count)

But be aware that if you would only unwrap the last optional you would still end up with an optional. In the following example, we’re only unwrapping the title but not the post. This means that if the post is nil we would still not get back a title:

let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post?.title!.count) // Prints: Optional(35)

Optionals as best practice, force unwrapping to catch programming errors

It’s best practice to work with optionals by default and to not use the exclamation mark if it’s not needed. Some even recommend to enable the force unwrapping SwiftLint rule. This will prevent you from introducing a lot of unexpected crashes.

However, sometimes it’s also good to use force unwrapping when it’s programming error if a value is nil. Therefore, you can help yourself debugging by force unwrapping and catch a bug in an early stage.

2. An optional is an enum of two cases

It’s good to know that an optional is basically an enum of two cases:

enum Optional<Wrapped> {
    /// The absence of a value.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)
}

However, instead of using the .none case you would use nil to indicate the absence of a value.

With that in mind, you can define the above name variable as optional as well by using the enum:

let name = Optional.some("Antoine van der Lee")
print(name!.count)

Or you can switch an optional just like you would with a normal enum:

func printName(_ name: String?) {
    switch name {
    case .some(let unwrappedValue):
        print("Name is \(unwrappedValue)")
    case .none:
        print("Name is nil")
    }
}

printName(nil) // Prints: "Name is nil"
printName("Antoine van der Lee") // Prints: "Name is Antoine van der Lee"

And looking at its documentation you can see that an optional comes with quite some handy methods. A great example is the map method:

let sideLength: Int? = Int("20")
let possibleSquare = sideLength.map { $0 * $0 }
print(possibleSquare) // Prints: "Optional(400)"

Or the flatMap method, which in this case only returns the name if it passed the validation of having at least 5 characters:

var name: String? = "Antoine van der Lee"
let validName = name.flatMap { name -> String? in
    guard name.count > 5 else { return nil }
    return name
}
print(validName) // Prints: "Optional("Antoine van der Lee")"

If you want to know more about the differences between map, flatMap, and compactMap, check out my blog post: CompactMap vs flatMap: The differences explained

Extending optionals

Now you know that an optional is defined as an enum you can guess that you can also write extensions for it!

The most common example is to extend a String optional and handle an empty value:

extension Optional where Wrapped == String {
    var orEmpty: String {
        return self ?? ""
    }
}

var name: String? = "Antoine van der Lee"
print(name.orEmpty) // Prints: "Antoine van der Lee"
name = nil
print(name.orEmpty) // Prints: ""

3. Writing unit tests for optionals

When you’re writing tests there is a nice way to work with optionals without force unwrapping. If you would use a force unwrap you take the risk of causing a fatal error that will stop all your tests from succeeding.

You can use XCTUnwrap which will throw an error if the optional does not contain a value:

func testBlogPostTitle() throws {
    let blogPost: BlogPost? = fetchSampleBlogPost()
    let unwrappedTitle = try XCTUnwrap(blogPost?.title, "Title should be set")
    XCTAssertEqual(unwrappedTitle, "Learning everything about optionals")
}

4. Optional protocol methods

If you’ve had experience with Objective-C you might miss the optional protocol methods. Although there is a better way to write optional protocol methods in Swift, the most common way in standard libraries looks as followed:

@objc protocol UITableViewDataSource : NSObjectProtocol {

    @objc optional func numberOfSections(in tableView: UITableView) -> Int

    // ...
}

This allows you to call the method using the question mark:

let tableView = UITableView()
let numberOfSections = tableView.dataSource?.numberOfSections?(in: tableView)

You can read more about protocol methods here: Optional protocol methods in Swift.

5. Nested optionals is a thing

Although SE-0230 – Flatten nested optionals resulting from ‘try?’ removed one of the most common causes of a nested optional, it’s still a thing!

var name: String?? = "Antoine van der Lee"
print(name!!.count)

You’ve unwrapped an optional which still returns an optional. This used to be the case when you used the try? operator in earlier Swift versions.

A common example is when you’re working with dictionaries that contain optional values:

let nameAndAges: [String:Int?] = ["Antoine van der Lee": 28]
let antoinesAge = nameAndAges["Antoine van der Lee"]
print(antoinesAge) // Prints: "Optional(Optional(28))"
print(antoinesAge!) // Prints: "Optional(28)"
print(antoinesAge!!) // Prints: "28"

You can see that it basically only requires to use an extra exclamation or question mark.

Conclusion

That’s it! We covered a lot of things you need to know when working with optionals in Swift. From the basics of unwrapping an optional using the exclamation mark (!!) to the more advanced implementations of extending the Optional enum.

If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.

Thanks!