In Swift, how do I avoid both optionals and nil object references?

You’re right, optionals can be a pain, which is why you shouldn’t overuse them. But they’re more than just something you have to deal with when using frameworks. They’re a solution to an incredibly common problem: how to handle a call that returns a result that might be OK, or might not.

For example, take the Array.first member. This is a handy utility that gives you the first element of an array. Why is it useful to be able to call a.first, when you could just call a[0]? Because at runtime the array might be empty, in which case a[0] will explode. Of course you can check a.count beforehand – but then again

a. you might forget,

and

b. that results in quite ugly code.

Array.first deals with this by returning an optional. So you are forced to unwrap the optional before you can use the value that is the first element of the array.

Now, to your issue about the unwrapped value only existing inside the block with if let. Imagine the parallel code, checking the array count. It would be the same, right?

if a.count > 0 {
    // use a[0]
}
// outside the block, no guarantee
// a[0] is valid

if let firstElement = a.first {
    // use firstElement
}
// outside the block, you _can't_
// use firstElement

Sure, you could do something like perform an early return from the function if the count is zero. This works but is a bit error-prone – what if you forgot to do it, or put it in a conditional statement that didn’t happen to run? Essentially you can do the same with array.first: check the count early in the function, and then later on do array.first!. But that ! is like a signal to you – beware, you are doing something dangerous and you will be sorry if your code isn’t completely correct.

Optionals also help make the alternatives slightly prettier. Suppose you wanted to default the value if the array was empty. Instead of this:

array.count > 0 ? a[0] : somedefault

you can write this:

array.first ?? somedefault

This is nicer in several ways. It puts the important thing up front: the value you want is how the expression starts, followed by the default. Unlike the ternary expression, which hits you first with the checking expression, followed by the value you actually want, then finally the default. It’s also more foolproof – much easier to avoid making a typo, and impossible for that typo to result in a runtime explosion.

Take another example: the find function. This checks if a value is in a collection and returns the index of its position. But the value may not be present in the collection. Other languages might handle this by returning the end index (which doesn’t point to a value but rather one past the last value). This is what’s termed a “sentinel” value – a value that looks like a regular result, but actually has special meaning. Like the previous example, you’d have to check the result was not equal to the end index before using it. But you have to know to do this. You have to look up the docs for find and confirm that’s how it works.

With find returning an optional it’s just natural, when you understand the optional idiom, to realize that the reason it does is because the result might not be valid for the obvious reason. And all the same things about safety mentioned above also apply – you can’t accidentally forget, and use the result as an index, because you have to unwrap it first.

That said, you can over-use optionals as they are a burden to have to check. This is why array subscripts don’t return optionals – it would create so much hassle to have to constantly check and unwrap them, especially when you know for a fact that the index you’re using is valid (for example, you’re in a for loop over a valid index range of the array) that people would be using ! constantly, and thus clutter their code without benefit. But then the helper methods like first and last are added to cover common cases where people do want to quickly do an operation without having to check the array size first, but want to do it safely.

(Swift Dictionaries, on the other hand, are expected to be regularly accessed via invalid subscripts, which is why their [key] method does return an optional)

Even better is if the possibility of failure can be avoided altogether. For example, when filter matches no elements, it doesn’t return a nil optional. It returns an empty array. “Well obviously it would”, you might say. But you’d be surprised how often you see someone making a return value like an array optional when actually they just ought to return an empty one. So you’re completely correct in saying you should avoid optionals except when they’re necessary – it’s just a question of what necessary means. In the examples above I’d say they’re necessary, and a better solution to the alternatives.

Leave a Comment