On Optionals in Swift

Let me start by saying if you’re new to Swift and having trouble understanding Optionals, this is a good read. If you’ve been around the Swift block a few times, this will probably be a repeat to you, but still, hopefully, useful. Also, if you’re an Obj-C developer who just can’t let go of your []s and @s, know that I’m simplifying what nil is in Objective-C when I mention it.

All right, you’ve been warned. Let’s dive in.


One of the easiest, and at the same time hardest, things for me to grasp when I was learning Swift was Optionals. I understand the basic idea of having variables that can never be nil and having ones that can. For example, this is simple for me to understand:


let neverNil: String = "This string can never be nil";
var maybeNil: String? = "This string might be nil at some point."

Obviously neverNil can (spoiler) never be nil and maybeNil can be nil… maybe. If you set it to nil. One of the reasons neverNil can’t be nil is because it’s an initialized constant and you can’t change constants once they’re intialized.

However, even the following doesn’t work:


var neverNil: String = "This string can STILL never be nil";
neverNil = nil // compiler error

Trying to set that thing to nil will give you a nice, fat compiler error. I think the basic concept up until here really is easy to understand.

Now, here is where I used to get confused. These are are both string values, right? So why doesn’t this work:


var neverNil: String = "It won't be nil"
var maybeNil: String? "It might be nil"

neverNil.uppercaseString // IT WON'T BE NIL
maybeNil.uppercaseString // compiler error

You can call uppercaseString on one of the strings, but not the other.

That’s because one is of type String and the other is of type String?. The ? in Optionals doesn’t just denote that it can be empty, it is literally a different type. That’s the beauty and simplicity of the system that I missed before. It’s so important, that I want to repeat it:

String and String? are different types.

One is a String and the other is a String value wrapped in a box that you have to open up before you can use. There are three common ways to open that box and get that value: Optional Binding, Optional Chaining and Forced Unwrapping.

Optional Binding

Hang around a Swift codebase long enough and you’ll see this pretty quickly:


var maybeNil: String? = "It might be nil"

if let definitelyNotNil = maybeNil {
    neverNil.uppercaseString // IT MIGHT BE NIL
}

What’s important here is that because String and String? are not the same type, the compiler is forcing you to DEAL with the fact that you could have a nil value at some point. You are BINDING the optional value to the definitelyNotNil constant. Essentially, the Optional gift box is opened and its contents are handed off to definitelyNotNil, which is used in the if statement’s block. If the Optional has nothing inside of it (i.e. it’s nil) then the if control structure returns false (basically) and the code that requires it to be a String is bypassed.

This is Swift’s way of dealing with the problem that your object might be nil versus just ignoring it and assuming it’ll be fine.

One more thing to note: this example is fairly contrived and isolated. Swift 2.0 introduced a new, slightly different way of doing this. Here’s how you might use this using the guard keyword inside of a method:


func someMethod() {
    guard let definitelyNotNil = maybeNil else {
        return;
    }
    
    definitelyNotNil = "This definitely contains a value now"
}

It’s not that functionally different from before, but it has the advantage of putting all your method’s logic in the body instead of buried in an if statement block in addition to exiting early (which is usually a good thing). FWIW, this style is becoming more preferable compared to the “older” way (Swift is only 1½ years old, you know).

Optional Chaining

Now, that’s a lot more code than we need for our example. Instead, we can unwrap our Optional to get the value inline beside to the method call like this:


var maybeNil: String? = "It might be nil"
maybeNil?.uppercaseString // IT MIGHT BE NIL

This is where the fact that String and String? are completely different types matters. You can’t call maybeNil.uppercaseString because an Optional object doesn’t have a method called uppercaseString. The ? in the statement unwraps the value from the optional (so it’s then a String) and uses that instead.

What makes this more useful though is that if there isn’t a value (i.e. maybeNil is nil), then the execution chain just stops and uppercaseString is never called on a nil object.

Once again, Swift has forced you to deal with the problem instead of just ignoring it. It’s just more succinct this time.

Forced Unwrapping

This is the simplest unwrapping method of all:


var maybeNil: String? = "It might be nil"
maybeNil!.uppercaseString // IT MIGHT BE NIL

That ! at the end of maybeNil is telling the compiler, “Don’t worry. I’ve got this. It’s not nil.” It’s basically the Swift equivalent of sudo. Do what I say because I know what I’m talking about.

This simplifies the logic if you know it will never be nil. Our example above is a great one because we know the value isn’t nil because we’re setting it on the line right above—we’re handling all the mutation (changes to the variable) ourselves.

However, what about this example?


var maybeNil: String? = "It might be nil"
someMethod(maybeNil)
maybeNil!.uppercaseString // prints "IT MIGHT BE NIL" (hopefully)

There’s a problem here.

This compiles because we’re explicitly telling the compiler that we are sure maybeNil isn’t nil. But, we actually have no way of knowing for sure what exactly someMethod does. It could even assign nil to that variable at some point in its execution—but we’re telling the compiler this isn’t nil! That’s why forced unwrapping with ! should be used sparingly. It’s handy for code brevity and when you’re absolutely sure a value is there. But, when you’re not, you’re going to get a runtime error.

And nobody likes runtime errors.

Conclusion

This post was much longer than I started out to write about Optionals. I think that’s because they are a supremely simple concept with a ton of intricacies. It’s easy to think Swift code is more complex because of Optionals compared to Objective-C. However, the code isn’t more complex; that complexity was still there in Objective-C, it just wasn’t dealt with properly (or oftentimes at all). Swift puts it forward so you have to deal with it. The result is a much safer language, which translates directly to less crashes.

Tune in next time, when I talk about how Objective-C developers can get nil safety with the new _Nullable syntax.