9 min
Next in trending

Facets of Swift, Part 1: Optionals

Facets of Swift is a series of articles on different aspects of Swift.

Facets of Swift, Part 1: Optionals


Facets of Swift is a series of articles on different aspects of Swift. Well, this is the first one, but more will follow. I write these articles to organize my thoughts on Apple’s new programming language.

I chose optionals as the topic for the first article, as these are one of the first features of Swift you will notice. The idea of optionals is simple: To prevent errors and to model things in code more accurately, Swift distinguishes between optional and non-optional values.

What are Optionals?

In Objective-C, there is no explicit optional value. To illustrate where that can pose problems, let’s have a look at a method that returns the index of an item in a todo list:

- (NSInteger)indexOfItem:(Item *item) {
// ...
}

Here, the item is implicitly optional: We can either pass an instance of Item, or nil. On the other hand, the return value is implicitly non-optional: As NSInteger is not an object type, there is no way not to return a value.

For the item parameter, it would make sense to require a non-nil value. But that’s not possible in Objective-C. For the return value, it would make sense to be able to return nil if the item is not in the list. But that’s not possible in Objective-C. We can only add documentation what happens if a nil is passed as an item (error or not?), and which value is returned in case the item is not in the list (NSNotFound which is defined as NSIntegerMax? Or NSIntegerMin? Or -1?).

Wouldn’t it be great if we had to explicitly write down which values are optional? Well, that’s the case in Swift. A type T is non-optional. To allow the absence of a value, we use the optional type T?. We may define the method like this:

func indexOfItem(item: Item) -> Int? {
// ...
}

Here the method declaration requires that we pass an item. Trying to pass nil would result in a compile-time error. Also, the method explicitly expresses that the return value is optional, so we don’t need to define a special Int value to model the absence of the index.

A variable v of type T? has a couple of differences to a variable of non-optional type T:

  1. It can not only hold values of type T, but also the empty value nil.
  2. It is initialized to nil.
  3. To access the value of type T which may be stored in v, we have to unwrap the value first.

Unwrapping Optionals

Let’s say we would like to write a method that returns the one-based index of the item in a list (let’s call it the natural index, because it is more natural to start counting from 1). In Objective-C, the implementation may look like this:

- (NSInteger)naturalIndexOfItem:(Item *)item {
NSInteger indexOrNotFound = [self indexOfItem:item];
if (indexOrNotFound != NSNotFound) {
return indexOrNotFound + 1;
} else {
return NSNotFound;
}
}

If we write the respective code in Swift, a first attempt may look like this:

func naturalIndexOfItem(item: Item) -> Int? {
let indexOrNotFound = indexOfItem(item)
if indexOrNotFound {
let index = indexOrNotFound!
return index + 1
} else {
return nil
}
}

We can use optional variables directly as a logic value in the if statement to detect whether the value is present. The ! behind indexNotFound unwraps the optional value to a non-optional. So index has the type Int, and the compiler does not complain about the addition.

If indexOrNotFound would be nil, then unwrapping would fail with a runtime error. The expression indexNotFound! is for that reason called a forced-value expression.

Optional Binding

Of course, the first attempt is not the best solution: The code is now longer than in Objective-C. We can do much better. First, as we often need to unwrap a value and then use it, Swift provides something called optional binding:

func naturalIndexOfItem(item: Item) -> Int? {
let indexOrNotFound = indexOfItem(item)
if let index = indexOrNotFound {
return index + 1
} else {
return nil
}
}

If we use let or var in the condition of an if statement, the constant/variable is set to the unwrapped value, so we don’t need to unwrap anymore. We can now inline indexOrNotFound:

func naturalIndexOfItem(item: Item) -> Int? {
if let index = indexOfItem(item) {
return index + 1
} else {
return nil
}
}

Instead of adding 1, we can call succ() on index:

func naturalIndexOfItem(item: Item) -> Int? {
if let index = indexOfItem(item) {
return index.succ()
} else {
return nil
}
}

But even that is way too much code in Swift. Enter optional chaining.

Optional Chaining

The essence of the method above is calling the succ function on indexOfItem(item). The rest is just the handling of the optional value. However, we can’t write

func naturalIndexOfItem(item: Item) -> Int? {
return indexOfItem(item).succ()
}

That will lead to a compiler error, as the method succ is not defined on the optional type returned by indexOfItem(item). And it should not compile, as we ignored the optional case. What about unwrapping with the forced value expression?

func naturalIndexOfItem(item: Item) -> Int? {
return indexOfItem(item)!.succ()
}

Now as soon as the item is not in the list, we would get a runtime error. What works is optional chaining which is done with ?:

func naturalIndexOfItem(item: Item) -> Int? {
return indexOfItem(item)?.succ()
}

If an object a is of optional type, a?.b returns nil if a is nil, otherwise it returns b. In Objective-C we have implicit optional chaining on objects: If we have a variable item that holds an item or nil, which can belong to a list or not, and a list can have a name or not, we get the name of the list or nil by calling

NSString *listName = item.list.name;

In Swift, for the same case we would need to write:

var listName = item?.list?.name

Is that preferable? Yes, it is. We have to explicitly state how we handle optional values. Even better, we can narrow down which parts are really optional and which aren’t. We can get rid ot the first question mark by making item a non-optional variable. We can get rid of the second question mark by making the list property non-optional. Finally, we can make the variable listName a String instead of a String? by making the name property non-optional.

Bottom line: In Objective-C, we can’t make objects non-optional and we can’t make other types optional. In Swift, we can.

Implicitly Unwrapped Optionals

There is a second optional type in Swift: The implicitly unwrapped optional. It is needed for various reasons, one of them is better compatibility with Objective-C libraries. A good example is the method +currentDevice on UIDevice. In Objective-C, it looks like this:

+ (UIDevice *)currentDevice;

How could the signature of such a method be bridged to Swift? First try:

class func currentDevice() -> UIDevice

That won’t work. While we are pretty sure that currentDevice will always return a value, that is not true for all Objective-C methods and properties. For example, both the method anyObject on NSSet and the property superview on UIView may be nil.

We could use an optional:

class func currentDevice() -> UIDevice?

That would work fine. The disadvantage is that our code would be cluttered with lots of unwrapping, optional chaining and so on, even though most of the methods in UIKit return non-nil objects. Imagine we would like to have the name of the current device as an NSString. Even though we know that both the device and its name are not nil, we would have to write:

var name = UIDevice.currentDevice()!.name!

Not pretty. What we would like to have is something that can still be nil, but that we do not have to unwrap ourselves. That’s the implicitly unwrapped optional, which is marked with an exclamation mark:

class func currentDevice() -> UIDevice!

A variable v of type T! has one difference to a variable of optional type T?: It will be implicitly unwrapped to type T. Because of that, we don’t need to unwrap anything here:

var name = UIDevice.currentDevice().name

name will now inferred to be of type NSString!, but that’s not a problem since it will be automatically unwrapped when it is accessed.

So whenever we have an Objective-C method or property that we know will not return nil, we can use it just like a non-optional. If we know that the value may be nil, we can use optional binding and chaining to prevent runtime errors.

The implicitly unwrapped optional has not been added for Objective-C support alone, though. In Swift it helps us with chicken-egg problems where an object needs a reference to another object which is not available at initialization time. The simplest example I could come up with is a recursive closure. Let’s say we would like to define factorial, and print out factorial(12). First try:

var factorial: Int -> Int
factorial = { x in (x == 0) ? 1 : x * factorial(x — 1) }
println(factorial(12))

The compiler will rightly complain that we can’t use factorial inside the closure, because at that point, the variable is not initialized. We could work around by making factorial an optional:

var factorial: (Int -> Int)?
factorial = { x in (x == 0) ? 1 : x * factorial!(x — 1) }
println(factorial!(12))

Now our code is cluttered with unwrapping even though it is glaringly obvious that factorial will be defined when accessed. An implicitly unwrapped optional is the best solution:

var factorial: (Int -> Int)!
factorial = { x in (x == 0) ? 1 : x * factorial(x — 1) }
println(factorial(12))

In Swift, we have non-optionals, optionals and implicitly unwrapped optionals. That’s quite different to Objective-C, so a couple of things may be confusing at first.

Gotchas

The biggest gotcha we may stumble upon is that an optional value evaluates to true if the value is set. At first glance, we may expect this snippet to print “Not authenticated”:

var authenticated: Bool? = false
if authenticated {
println("Authenticated")
} else {
println("Not authenticated")
}

However, it will print “Authenticated”, as authenticated as an optional evaluates to true as it is non-nil, no matter what the unwrapped value is. To handle all three cases, we need to unwrap authenticated:

var authenticated: Bool? = false
if let authenticated = authenticated {
if authenticated {
println("Authenticated")
} else {
println("Not authenticated")
}
} else {
println("Unknown")
}

or

var authenticated: Bool? = false
if authenticated && authenticated! {
println("Authenticated")
} else if authenticated {
println("Not authenticated")
} else {
println("Unknown")
}

Before we start to complain about this, we should realize that the respective Objective-C code using NSNumber *authenticated would look worse.

Another gotcha is that question mark and exclamation mark as postfix on a type T are quite different to their counterparts used on an expression v:

  • T? is the optional type
  • T! is the implicitly unwrapped optional type
  • v? is used for optional chaining
  • v! is used for unwrapping

Things that may be confusing here when starting with Swift:

  • If we have a non-optional type T, we can use ? and ! on it. If we have an expression v of non-optional type T, we can’t use ? and ! on it, as both chaining and unwrapping don’t make sense on a non-optional.
  • We can use ! on both expressions of type T! and T?
  • We can use ? on both expressions of type T? and T!

The last bit of confusion is the behavior of the ? postfix on an expression v if we use it without calling anything on it. Then it just returns v as an optional. So if v is either of type Int? or of type Int!, and we declare

var v2 = v?
var v3 = v???????????

, both v2 and v3 will be of type Int?.

Recommendations for using Optionals

At this point, Swift is only a couple of weeks old. So take the following recommendations with a grain of salt.

First, don’t use the optional chaining operator ? on its own, but always with a following dot. Its name already points to its main use: optional chaining.

Second, while technically possible, don’t use optionals of optionals. Yes, you can define something like var v: Int?!!. But don’t. Please don’t.

Last but not least, only use optionals where you need them, and where the extra case actually makes sense. For example, if you model a todo list that has a name and items, the items property can always be a non-optional Array. The name property can be a non-optional String. Only make the name optional if you really need to distinguish between a sensible default like the empty String and the case that the name is unset. Swift’s optionals and implicitly unwrapped optionals are great. But non-optionals are the default for a reason.


In the next article of this series, we’ll have a look at Swift’s tuples.