Optional Values Revisited - ADDITIONAL TOPICS - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 3: ADDITIONAL TOPICS

26. Optional Values Revisited

In an earlier chapter (Chapter 9 on Optional Values) I described the basics of optional values. Optional values are a way of ensuring that variables that can have no value—that is, that can be set to nil—are handled with a high degree of safety. This is done by defining an optional version of a data type if a variable is going to contain nil. If a non optional (ordinary variable) is set to nil, a runtime error will result. I also said that a value assigned to an optional data type will be wrapped, and that it must be unwrapped before it can be used. I then described some different ways of unwrapping the value, including the preferred method of optional binding.

In this chapter we revisit optional values, and discuss a few more capabilities—the use of implicitly unwrapped optionals, the use of the nil coalescing operator, the use of optional chaining, and optional binding for multiple values.

Implicitly Unwrapped Optionals

An alternative to the basic wrapped optional is an implicitly unwrapped optional. This is used only when you are absolutely sure that a variable has a value. It is often used in situations such as a property defined in a class as a nil, but actually initialized with a value as part of an init(), perhaps when an instance is created with a parameter that sets the property. Care is then taken to not thereafter set the property to nil.

It is also common for the Cocoa Touch API to return optionals that are guaranteed to have a value, and these can be declared as implicitly unwrapped optionals.

A variable is defined as an implicitly unwrapped optional by using type annotation and providing an exclamation point ("!") just after the name of the type:

var n: Int! = 5

var p = n

Here is a very simplified example—the variable n is declared as an integer implicitly unwrapped optional with a value of 5 assigned. When the value is then assigned to the variable p, it is not necessary (or allowed) to do anything to unwrap it.

If, however, an implicitly unwrapped optional is accessed that does not have a value, there will be a runtime error.

A more complicated example is shown below:

class bird {

var species: String! = nil

init(species: String) {

self.species = species

}

}

let aParticularBird = bird(species: "Mallard Duck")

In this case we have a class that stores information about particular birds, namely, the name of their species. We define a property, species, as a variable with a type of an implicitly unwrapped optional string, with a value of nil. The initializer accepts a parameter when a new instance is created and sets the value of the property species (accessed as self.species) to the value contained in the parameter named species.

What happens in the last line is that a new instance is created, and the property species is first initialized to be nil. When the initializer is executed, however, the property is then set to the value of "Mallard Duck".

If this property is accessed, unwrapping is not required:

print("The species is \(aParticularBird.species)")

If it is later discovered that the species name is not correct, it is not allowed to simply set the value back to nil (for unknown). If the correct species name is not available the instance must be deleted.

Nil Coalescing Operator

Another approach to optionals ensures that a variable has a value, either that contained in an optional variable or a predefined default value. It is quite safe because unwrapping is done automatically and there is no possibility of a runtime error.

This is done with a nil coalescing operator. This operator is a double question mark and is used with an optional variable and a default value.

var errorMessageFromDisk: String? = "Bad File Sector"

let defaultErrorMessage = "Something Bad Happened"

let errorMessage = errorMessageFromDisk ?? defaultErrorMessage

Here the first line declares and sets an optional variable with an error message. (We’re pretending that we got this as the result of attempting to read a disk.) This is what might reasonably come from a file system. However, the variable storing the message is declared as an optional, because the file system might not always know the specifics of the error, in which case it would return a nil.

A default error message is defined, and then the nil coalescing operator is actually used.

In the case shown, errorMessage would be set to "Bad File Sector" after it was unwrapped from the optional version. This is because the left hand side of the ?? nil coalescing operator has a value. However, suppose the variable errorMessageFromDisk was nil. In such a case,errorMessage would be set to the default message, "Something Bad Happened", by the nil coalescing operation.

This is equivalent to the following more verbose, and less safe, statements:

var errorMessageFromDisk: String? = "Bad File Sector"

let defaultErrorMessage = "Something Bad Happened"

if errorMessageFromDisk == nil {

let errorMessage = defaultErrorMessage }

else {

let errorMessage = errorMessageFromDisk!

}

The name of the operator is a little obscure. "Coalesce" means to "come together", "combine", or "form one mass or whole". Thus presumably the coalescing operator allows the original optional value and the value that gets set if the original value is nil to "come together" and form a new variable or constant that contains the information that is relevant, given the situation. Of course, lots of operations do this sort of thing. The name isn't really very enlightening about what it does. (It’s hard to name operators.)

Optional Chaining

In situations in which there is more than one optional in a sequence of property relationships to unwrap, it takes less code and is more readable to use optional chaining for the unwrapping of optionals.

This accomplishes the same thing as optional binding. For optional chaining to have any effect, all of the values that are marked as optionals in the chain must in fact have values. If any of them are nil, the optional chaining statement does nothing.

To refresh your memory about how optional binding works, we can see it here:

var breedOfDog: String? = "Border Collie"

We have an optional with a value; we now have to unwrap it:

if let breed = breedOfDog {

print("Breed of dog is \(breed)")

}

else {

print("breedOfDog has no value")

}

This works fine if there is just one value to test for nil.

Now consider a more complex situation. Suppose we have a person who owns a dog, and breedOfDog is a property of the class Dog.

class Person {

var dog: Dog? = nil

}

class Dog {

var breedOfDog: String? = nil

}

var george: Person? = Person()

george!.dog = Dog()

george!.dog!.breedOfDog = "Border Collie"

We've created an instance of a Person, and assigned it to a variable george that is an optional Person (Person?). We've also created an instance of a Dog, and assigned it to a property of the variable george. And we have created a value for a property of Dog where the instance of Dogis now contained in a property of george.

All of these variables and properties have values, once all the code has been executed. However, they are all declared as optional versions of whatever type they are, and at any point after these values are assigned any or all of them could have nil assigned to them. Thus we need to take care in unwrapping them. Note here that unwrapping is just as necessary when using an optional on the left side of an assignment statement as it is on the right side. In the code above I have used forced unwrapping, which is a bad idea. I should use optional binding.

Before unwrapping the optional value of breedOfDog, we have to make sure that the other variables in the dot syntax chain have values.

The preferred way to do this is with optional chaining. For the example above, this requires:

if let breed = george?.dog?.breedOfDog {

print("If everything has a value, breedOfDog is \(breed)")

}

The question mark at the end of each variable, known as the chaining operator, indicates that the property or variable in question is an optional, and that the value should be unwrapped if it exists. If the value does not exist then the optional chaining should stop immediately. This is the main difference between using forced unwrapping (with a “!”) and optional chaining (with a “?”). With forced unwrapping, if any of the optional values is nil, you will get a runtime error. With optional chaining, the statement will simply be ignored.

The type of a value that is returned from an optional chaining statement is itself an optional.

If a particular variable or property in the chain is not an optional, it does not have a question mark at the end of it, and does not need to be unwrapped. Its value is assumed to exist because otherwise the system would have generated a runtime error.

Optional chaining is common for relationship chains in apps themselves, and especially common when a relationship chain is retrieved from an API like Cocoa Touch.

Optional chaining can be used to write to optional values as well as to read from them, and they can be used for methods and subscripts as well as properties.

Optional Binding for Multiple Values

In situations in which there is more than one optional value that needs to be unwrapped at one time, this can be done, beginning in Swift 1.2, compactly with a simplified syntax.

Suppose that a, b, and c are all declared as optionals and have all been assigned a true value. They can be unwrapped by the code below. First, the optional declarations:

var a: Int? = 5

var b: Int? = 6

var c: Int? = 7

var d: Int? = 8

Then the code:

if let a = a, b = b, c = c, d = d {

print("All of the optionals a, b, c, and d have values")

}

The syntax above does the same thing as the more wordy:

if let a = a {

if let b = b {

if let c = c {

if let d = d {

print("All of the optionals a, b, c, and d have values")

}

}

}

}

It's also possible to add a where clause to do an additional test beyond making sure that all of the optionals have values:

var a:Int? = 5

var b:Int? = 6

var c:Int? = 7

var d:Int? = 8

var m = 5

if let a = a, b = b, c = c, d = d where m > 4 {

print("M is > 4 and all of the optionals a, b, c, and d have values")

}

Hands-On Exercises

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

For Chapter 26 exercises, go to

understandingswiftprogramming.com/26