Optionals

Swift types can be optional, which is indicated by appending ? to a type name.

    var anOptionalFloat: Float?
    var anOptionalArrayOfStrings: [String]?
    var anOptionalArrayOfOptionalStrings: [String?]?

An optional lets you express the possibility that a variable may not store a value at all. The value of an optional will either be an instance of the specified type or nil.

Throughout this book, you will have many chances to use optionals. What follows is an example to get you familiar with the syntax so that you can focus on the use of the optionals later.

Imagine a group of instrument readings:

    var reading1: Float
    var reading2: Float
    var reading3: Float

Sometimes, an instrument might malfunction and not report a reading. You do not want this malfunction showing up as, say, 0.0. You want it to be something completely different that tells you to check your instrument or take some other action.

You can do this by declaring the readings as optionals. Add these declarations to your playground.

var reading1: Float?                                         nil
var reading2: Float?                                         nil
var reading3: Float?                                         nil

As an optional float, each reading can either contain a Float or be nil. If not given an initial value, then the optional defaults to nil.

You can assign values to an optional just like any other variable. Assign floating-point literals to the readings:

reading1 = 9.8                                               9.8
reading2 = 9.2                                               9.2
reading3 = 9.7                                               9.7

However, you cannot use these optional floats like non-optional floats – even if they have been assigned Float values. Before you can read the value of an optional variable, you must address the possibility of its value being nil. This is called unwrapping the optional.

You are going to try out two ways of unwrapping an optional variable: optional binding and forced unwrapping. You will implement forced unwrapping first. This is not because it is the better option – in fact, it is the less safe one. But implementing forced unwrapping first will let you see the dangers and understand why optional binding is typically better.

To forcibly unwrap an optional, you append a ! to its name. First, try averaging the readings as if they were non-optional variables:

reading1 = 9.8                                               9.8
reading2 = 9.2                                               9.2
reading3 = 9.7                                               9.7
let avgReading = (reading1 + reading2 + reading3) / 3

This results in an error, because optionals require unwrapping. Forcibly unwrap the readings to fix the error:

let avgReading = (reading1 + reading2 + reading3) / 3
let avgReading = (reading1! + reading2! + reading3!) / 3     9.566667

Everything looks fine, and you see the correct average in the sidebar. But a danger lurks in your code. When you forcibly unwrap an optional, you tell the compiler that you are sure that the optional will not be nil and can be treated as if it were a normal Float. But what if you are wrong? To find out, comment out the assignment of reading3 - which will return it to its default value, nil - and run the code again.

reading1 = 9.8                                               9.8
reading2 = 9.2                                               9.2
reading3 = 9.7
// reading3 = 9.7

You now have an error. Xcode may have opened its debug area at the bottom of the playground with information about the error. If it did not, select ViewDebug AreaShow Debug Area. The error reads:

    Fatal error: Unexpectedly found nil while unwrapping an Optional value

If you forcibly unwrap an optional and that optional turns out to be nil, it will cause a trap, stopping your application.

A safer way to unwrap an optional is optional binding. Optional binding works within a conditional if-let statement: You assign the optional to a temporary constant of the corresponding non-optional type. If your optional has a value, then the assignment is valid and you proceed using the non-optional constant. If the optional is nil, then you can handle that case with an else clause.

Change your code to use an if-let statement that tests for valid values in all three readings.

let avgReading = (reading1! + reading2! + reading3!) / 3
if let r1 = reading1,
    let r2 = reading2,
    let r3 = reading3 {
        let avgReading = (r1 + r2 + r3) / 3
        print(avgReading)
} else {
    let errorString = "Instrument reported a reading that was nil."
    print(errorString)
}

reading3 is currently nil, so its assignment to r3 fails, and the sidebar shows the error string.

To see the other case in action, restore the line that assigns a value to reading3. Now that all three readings have values, all three assignments are valid, and when you run the code the sidebar updates to show the average of the three readings.

Subscripting dictionaries

Recall that subscripting an array beyond its bounds causes a trap. Dictionaries are different. The result of subscripting a dictionary is an optional:

let nameByParkingSpace = [13: "Alice", 27: "Bob"]           [13: "Alice", 27: "Bob"]
let space13Assignee: String? = nameByParkingSpace[13]       "Alice"
let space42Assignee: String? = nameByParkingSpace[42]       nil

If the key is not in the dictionary, the result will be nil. As with other optionals, it is common to use if-let when subscripting a dictionary:

let space13Assignee: String? = nameByParkingSpace[13]
if let space13Assignee = nameByParkingSpace[13] {
    print(space13Assignee)
}                                                           "Alice"
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset