Facets of Swift, Part 1: Optionals
Facets of Swift is a series of articles on various aspects of the Swift 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:
- It can not only hold values of type T, but also the empty value nil.
- It is initialized to nil.
- 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 != nil {
let index = indexOrNotFound!
return index + 1
} else {
return nil
}
}
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 successor() on index:
func naturalIndexOfItem(item: Item) -> Int? {
if let index = indexOfItem(item) {
return index.successor()
} 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 successor 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).successor()
}
That will lead to a compiler error, as the method successor 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)!.successor()
}
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)?.successor()
}
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.
Optionals of Bool values are quite interesting, as these give us three choices. Let’s have a look on how to cover these three choices in code.
Optional Bool
If we use an optional boolean type (Bool?), it can be unwrapped to true or false, or be nil. There are a couple of ways to cover all three cases. We can unwarp the optional variable:
var authenticated: Bool? = false
if let authenticated = authenticated {
if authenticated {
println("Authenticated")
} else {
println("Not authenticated")
}
} else {
println("Unknown")
}
If we don’t like the structure of the code, we can cover the three cases on the same level:
var authenticated: Bool? = false
if authenticated == true {
println(“Authenticated”)
} else if authenticated == false {
println(“Not authenticated”)
} else {
println(“Unknown”)
}
We need the comparisons above as authenticated is an optional, and since Swift beta 5, we can’t use optionals directly as logic values, however, we can use them directly in comparisons with ==. Besides, the respective Objective-C code using NSNumber *authenticated would definitely look worse.
If we prefer to, we can also use a switch statement.
var authenticated: Bool? = false
switch authenticated {
case .Some(true):
println(“Authenticated”)
case .Some(false):
println(“Not authenticated”)
default:
println(“Unknown”)
}
I don’t like it that much though for a couple of reasons. First, we have to use .Some which is part of the implementation of optionals (it’s done with an enum type, but that’s a story for another article). Second, even if we cover all three possible cases in the switch statement, the compiler will complain we have not exhausted all cases. That’s because of the current internals of Bool. Don’t ask. So we have to use a default instead of the third case.
What about other gotchas with Swift’s optionals?
Gotchas
Having optionals is something we don’t have in Objective-C, so a couple of things may be confusing at first. The biggest 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:
- 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.
Third, try to avoid the implicitly unwrapped optional, except when you’re really sure that it will be always filled. A lot of the Cocoa APIs are still not annotated correctly and return T!, but from a programmer’s perspective, most should either return T?, or 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.