A Swift Read: Understanding and Using Optionals

Tuesday July 10, 2018 at 03:15 pm CDT

originally published 11/8/2016 on the DevMynd blog

Optionals: The Basics

In Swift, the optional type is used to indicate that a variable (or constant) might not hold a value, i.e. that it might equal nil. Based on this, you may suspect that a non-optional variable cannot be assigned a value of nil, and you would be correct.

var cannotBeNil: String = nil
// nil cannot initialize specified type 'String'. WTH!

You might be wondering if you can make use of type inference and simply assign nil to a variable for which no type has been assigned. Nice try but, alas:

var stillCannotBeNil = nil
// nil requires a contextual type, for chrissakes.

The compiler is trying to infer the type here, but you haven’t really given it one. You’ve only indicated that stillCannotBeNil is an optional, not if it’s an optional String or Int or what have you.

Anyhow, let’s go ahead and declare an optional variable:

var canTotesBeNil: String?
print(canTotesBeNil)
// nil

We could also make the declaration like so:

var canTotesBeNil: Optional<String>
print(canTotesBeNil)
// nil

Optional declarations can be formally declared with Optional<TypeName> as well as the provided syntactic sugar in the form of TypeName?. Either way is acceptable, but be aware that the latter is more common.

You’ll note in the example above that canTotesBeNil can totes be nil and, in fact, is nil. When you declare an optional variable, its default value is nil.

Let’s assign a value to canTotesBeNil and print it out.

canTotesBeNil = "I optional, therefore I might be."
print(canTotesBeNil)
// Optional("I optional, therefore I might be.")

Gross.

What’s happening here? If an optional is equal to nil, then you can go ahead and treat it as you’d expect. However, if an optional has a non-nil value, you have to unwrap it using the force-unwrap operator (!) to get access to its value.

print(canTotesBeNil!)
// "I optional, therefore I might be."

Boom!

This works great when canTotesBeNil is totes not nil. If canTotesBeNil is totes nil, there’s some awfulness.

canTotesBeNil = nil
print(canTotesBeNil!)
// fatal error: unexpectedly found nil while unwrapping an Optional value, you blockhead!

One way to get around this would be to check if canTotesBeNil is equal to nil before unwrapping it.

if canTotesBeNil != nil {
print(canTotesBeNil!)
} else {
print("Is totes nil.")
}

There’s a simpler way to do this that we’ll talk about later.

At this point, you’re probably wondering about optional constants and when in the heck you’d ever use them.

I mean, you’d likely never do something like this:

let cheekyText: String? = "I am such a string!"

You would more likely use them in cases where a method returns an optional value and you have no intention of changing said value.

let text: String? = someService.methodOutOfMyControl()
if text != nil {
print(text)
} else {
print("Nothing to see here.")
}

So that’s optionals in a nutshell.

Right now optionals probably seem like some pain-in-the-ass BS, what with having to constantly check if they have a value and the insulting error messages they give you. Fortunately, Swift provides a number of ways to avoid peppering your code with if myOptional != nil.

Optionals and Control Flow

So, if you want to run one bit of code when your optional has a value, and another when it doesn’t, Swift has you covered with optional binding. Optional binding allows you to check for and assign the value of an optional

You see this mess?

let text: String? = methodThatReturnsAnOptionalString()
if text != nil {
print(text!)
} else {
print("Nothing to see here.")
}
// "An optional string."

This is lame and you shouldn’t do it. Instead, use optional binding:

if let textToPrint = methodThatReturnsAnOptionalString() {
print(textToPrint)
} else {
print("Nothing to see here.")
}
// "An optional string."

Notice anything special about the print statement on line 2? We didn’t have to force unwrap the argument to print. If methodThatReturnsAnOptionalString() returns a non-nil optional, then textToPrint is set to that value. Otherwise, the code in the else branch is executed.

There is one gotcha, however. The variable declaration after the if is not a normal declaration. textToPrint is only available in the first branch of the conditional. If you attempt to use it in the else branch or outside of the conditional, you will get an error.

Optional binding also allows for variables, meaning you can do this:

if var textToPrint = methodThatReturnsAnOptionalString() {
print(textToPrint)
}

Note, however, that if textToPrint is never mutated you will get an error from the compiler asking you to change it to a constant.

You can also make use of optional binding in guard statements.

guard let textToPrint = methodThatReturnsAnOptionalString() else {
print("Nothing to see here.")
return
}
print(textToPrint)

Optional binding in the context of a guard statement is a little different. Unlike the if conditional above, textToPrint is only available outside of the guard statement. Hence, we’re able to print it.

Nil-Coalescing Operator

Let’s say you would like to set a variable to the value of an optional if the optional is non-nil or to a default value if it’s nil. You can accomplish this using the ternary operator.

var allTheThings: String? = methodThatMightReturnAllTheThings()
var getStuff = allTheThings != nil ? allTheThings! : "This string is a thing!"

Alternatively, you could make use of the nil-coalescing operator.

var getStuff = allTheThings ?? "This string is a thing!"

If the optional is not nil, the nil-coalescing operator will assign its value to getStuff. If the optional isnil, getStuff is assigned the provided default. One potential gotcha lies in a type mismatch between the optional and the default. The default must be of the same type as the optional. For instance, you couldn’t do something like this:

var getIntegerStuff = allTheThings ?? 3
// Binary operator '??' cannot be applied to operands of type 'String?' and 'Int', which you'd know if you read the goddamned documentation!

Ouch.

Optional Chaining

What about situations in which you want to call a property or method on an optional? You might, for instance, want to get the size of an optional array. In order to do that, however, you’d have to force unwrap it, potentially triggering an ego-obliterating error.

Optional chaining lets you call whatever properties or methods you would like to on an optional and to get back nil if the optional is empty.

var optionalArray: Array<Int>?
var count = optionalArray?.count
print(count)
// nil

In contrast to the force-unwrap operator, which has no chill, the question mark operator loves and accepts you for who you are and does not trigger an error.

One important thing to note is that optional chaining always returns an optional value, given that its result could be nil.

var optionalArray: Array? = [1, 2, 3]
if let count = optionalArray?.count {
print(count)
} else {
print("Nope")
}

I imagine some part of you wants to write something like this to get ahold of the value of count directly:

var arrCount = optionalArray?.count!
// error: cannot force unwrap value of non-optional type 'Int', you verminous nit.

I see what you’re trying to do, but the above will bring the wrath of the compiler upon you. What you’re actually aiming for is

var count = (optionalArray?.count)!

Implicitly Unwrapped Optionals

It’s likely you won’t make much use of implicitly unwrapped optionals outside of class initialization. I don’t want to get too deep into classes, but we’ll talk about them briefly here.

Implicitly unwrapped optionals are used whenever you are absolutely, positively sure that your application will not make any attempt to get an optional’s value before it has been set. Implicitly unwrapped optionals are kinda unsafe, so you want to be dead certain that they’re what you want. If you’re not sure, just use a plain old optional.

var dangerString: String! = "I'm scared."
print(dangerString)

Note the absence of the force-unwrap operator in the print statement. That’s because dangerString has been unwrapped for you.

Keep in mind, though, that if you were to try to print dangerString without first giving it a value (or if, like some kind of adrenaline-junkie, you set it to nil later), you would get an error. That’s what makes implicit unwrapping so dangerous. So, you know, be careful.

Optionals in the Wild

Try to keep in mind that optionals will always be used whenever it is possible that a value will not be present. That’s not simply a guideline for how you should write your own code, but a general rule for what you should expect when interacting with Swift objects. For instance, if you try to receive a value from a dictionary, you will get an optional.

var things = ["what": "blah", "huh": "nope"]
var what = things["what"]
print(what!)

Similarly, if you attempt to convert a String to an Int using the Int initializer, you get an optional.

var totesANumber = "2035"
var boom = Int(totesANumber)

Optionals. Optionals everywhere.

Get Swifty

Great. So…get out there and use some optionals. Be super inspired. Be the best. Dream big. Swift hard. Good night.


Photo by Vincent van Zalinge on Unsplash