Optional Values - FUNDAMENTALS - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 1: FUNDAMENTALS

9. Optional Values

A major source of errors in programming results from the situation in which a variable is expected to have a value but has in fact no value. The problem is not that this is particularly difficult to deal with. The problem is that it is often simply not dealt with. The code might be prototype code that suddenly is deemed to be production code. Or it is code where the programmer defers dealing with the problem and then forgets, or disappears to work on some other project. The consequences vary from some function simply not working in some circumstances to an app crashing. In some cases the problem only manifests itself at a later time and is difficult to debug.

It is common for iOS and Mac APIs to return a nil (that is, the absence of a value) when a value, such as the contents of a file from a remote server or on the device's own file system, is not available. Some functions will return nil rather than a value: For example, the function toInt()normally takes a number coded as a string (e.g., "1234") and converts it to an integer. However, if the function is given a string like "abcd", it will return a nil. Similarly, an attempt to access a dictionary with a key that does not exist will return a nil.

If type safety refers to the goal of designing a language that tries to help programmers avoid making errors about the correct data type, then the goal of designing a language that tries to help programmers avoid making errors when variables lack values might be called value safety.

Swift has a capability for quickly catching errors resulting from the absence of values. This capability is implemented in the form of what are known as optional values, or optionals.

Ordinary variables, known as non optionals in this scheme, are required to have values. They are also required to have an initial value. If an attempt is later made to set them to nil, a runtime error will result.

In order for a variable to be allowed to not contain a value, that is, be set to nil, that variable must have been declared as some form of "optional" data type. (Constants aren't involved in this discussion, because they can't change value and thus can't suddenly become nil unexpectedly.)

The fact that a runtime error will result from setting a non optional variable to nil and that nil can only be set to a variable declared with an optional data type might be considered a first line of defense (of two) in avoiding this kind of error.

The First Line of Defense—Only Optional Variables Can Not Have a Value

Every data type that can contain a value has an optional version of it that allows a variable of that type to not have a value.

Thus, an integer data type has an optional version formally defined as:

Optional<Int>

and known as an "optional integer".

You won't actually see a notation like Optional<Int> much. What you will commonly see is an alias (meaning the same thing) that consists of the type name followed by a question mark—in this case Int?.

Thus, the following code will declare a variable as an optional integer:

var n: Int? = 5

var n: Int? = nil

In the first line n must be explicitly declared as an optional with the : Int?. If n is simply declared with no type annotation and then set to 5, the type inferred will just be Int, a non optional.

In the case of the second line, an explicit declaration is also required, because although setting it to nil makes it clear that it is an optional, the compiler would otherwise not know that the type desired was an integer.

When a variable with an optional type is set to a value that value is wrapped. This means that it cannot be directly accessed in the normal way. To access it, it must be unwrapped.

The Second Line of Defense—Values in Optionals Must Be Unwrapped

The second line of defense in using optionals is the necessity to unwrap them before use. If n is an optional and there is an attempt to execute the following code, the program will have a runtime error.

var n: Int? = 5

var p = n // Program will crash

The rule is that a reference to an optional data type that is made without unwrapping it will cause a runtime error as a way of reminding the programmer that some effort must be put into ensuring that the value is actually there.

The simplest (but a dangerous) way to do this is by forced unwrapping. This is done by appending an exclamation point to the variable being unwrapped:

var n: Int? = 5

var p = n! // Works but dangerous

This works but is not particularly recommended and should only be used if you are absolutely sure that the value is not nil. If the value is nil, a runtime error will result.

The best practice here is to use what is known as optional binding:

var n: Int? = 5

if let p = n {

print("The unwrapped value is \(p)")

}

else {

print("There is no value")

}

// Prints: The unwrapped value is 5

What this does is to set p to the value contained in the wrapped value n. The value of p is then tested in the if expression: if the value exists then the test succeeds and the first part of the if statement is executed; if there is no value the test fails and the second part of the if statement is executed.

Once the value of p has been set the value is available in an unwrapped form by referencing p. Thus, when it is referenced the value can be assigned or printed without any use of an exclamation point to unwrap it: it is already unwrapped.

This particular behavior is an idiom that only sets a value if the second variable is an optional type.

Note that this if statement and the ability to assign a value to a variable in it is against the general rule in Swift that assignment statements cannot be made in if clauses. This is allowed because the constant is declared within the if statement and thus its scope is local to it. It is thus relatively safe. The usual practice is to use a constant. It is also possible to use a variable, which can be useful in some circumstances. This behavior also works with while statements as well as if statements.

In optional binding it is common to use the same variable name in the binding clause as was used for the optional value.

var n: Int? = 5

if let n = n {

print("The unwrapped value is \(n)")

}

else {

print("There is no value")

}

Optional binding is a better practice than testing for nil, which I will describe just below, because it is a little safer. I'm just describing this alternative here for completeness so that you understand it. But you should use optional binding in preference to testing for nil.

This alternative simply tests whether the variable has a value or not:

var m: Int? = 5

if m != nil {

print("The value of m is \(m!)")

}

else {

print("m has no value")

}

// Prints: The value of m is 5

This is just a simple test of whether the variable has a value or not. If there is a value, it is necessary to do forced unwrapping (with an !) on the variable to extract the value. The reason that this is considered less safe than optional binding is that the programmer may accidentally omit the exclamation point and thus cause a runtime error.

In the present chapter I've covered the basics of optional values—why they make code safer, albeit at some cost of some annoyance to the programmer. I've described the reality that non optionals that get set to nil will crash, and that values stored in optional variables are wrapped and must be unwrapped before use. I've then described three ways of unwrapping—the potentially dangerous forced unwrapping, the best practice of optional binding, and the less safe alternative of just testing for nil before unwrapping.

In a later chapter, Chapter 26 on “Optional Values Revisited”, I will describe some of the less known aspects of optionals. This includes marking an optional as implicitly unwrapped, so that you do not have to unwrap it. It also includes the use of the nil coalescing operator, which forces any optional variable that is nil to have a default value. It includes the description of how to do optional chaining, which is a simplified syntax for unwrapping when you have dot syntax that includes multiple optional variables. And, finally, it includes a description of how, in Swift 1.2, a simpler syntax allows optional binding for multiple optionals to be done more easily.

Hands-On Exercises

Go to the following web address with a Macintosh or Windows PC to do the Hands-On Exercises.

For Chapter 9 exercises, go to

understandingswiftprogramming.com/9