Keywords

These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

Programmers tend to be optimistic because when they write code, they assume that it will work correctly. However, it’s often better to be more pessimistic when it comes to programming. Instead of assuming your code will work the first time, it’s safer to assume your code won’t work at all. This forces you to be extra careful when writing Swift code to make sure it does exactly what you want and doesn’t do anything unexpected.

No program is error-free. Rather than waste time hunting down problems, it’s far better to anticipate them and do your best to prevent them from creeping into your code in the first place. While it’s not possible to write error-free code 100 percent of the time, the more defensive you are in writing code, the less likely you’ll need to spend time hunting down errors that keep your code from working properly.

In general, assume nothing. Assumptions will surprise you every time, and rarely for the better. By anticipating problems ahead of time, you can plan for them so they won’t wreck your program and force you to spend countless hours trying to fix a problem that you assumed should never have existed at all.

Test with Extreme Values

The simplest way to test your code is to start with extreme values to see how your program handles data way out of range from the expected. When working with numbers, programs often assume that the user will give it valid data within a fixed range, but what happens if the user types a letter or a symbol such as a smiley face instead of a number? What if the user types an integer instead of a decimal number (or vice versa)? What if the user types an extreme number such as 42 billion or -0.00000005580?

When working with strings, do the same type of test. How does your program react when it expects a string but receives a number instead? What happens if it receives a huge string consisting of 3,000 characters? What happens if it receives foreign language symbols with accent marks or unusual letters?

By testing your program with extreme values, you can see if your program responds gracefully or simply crashes. Ultimately the goal is to avoid crashes and respond gracefully. That might mean asking the user for valid data over and over again, or at least warn the user when the program receives invalid data. Reliable programs need to guard against anything that threatens to keep it from working properly.

Be Careful with Language Shortcuts

Every programming language offers shortcuts to make programming faster by requiring less code. However, those language shortcuts can often trip up even the most experienced programmers so use shortcuts with care.

One simple mistake is mixing up prefix and postfix operators such as counter++ or ++counter. Although the postfix and prefix increment operators look nearly identical, they create two completely different results as shown in Figure 21-1.

Figure 21-1.
figure 1figure 1

Using the prefix and postfix increment shortcut

Notice that the postfix increment operator (counter++) first uses the value of counter and then adds 1 to it. That’s why the print (counter++) line prints 20. Right after it prints 20, the postfix increment operator adds 1 to the counter variable.

On the other hand, the prefix increment operator (++counter) adds 1 first and then uses the value of the counter. After the counter++ operator adds 1 to 20 and gets 21, the ++counter prefix increment operator adds 1 to it before using it. That’s why it prints 22.

Depending on what you need, mixing these two increment operators up can give you slightly different results, which can keep your program from working properly. If you’re not sure how these increment operators work, just play it safe and use counter = counter + 1 instead. Clarity is always better than confusion even if it means writing additional lines of code.

To see how the postfix increment operator differs from the prefix increment operator, follow these steps:

  1. 1.

    From within Xcode choose File ➤ New ➤ Playground. Xcode now asks for a playground name.

  2. 2.

    Click in the Name text field and type DefensePlayground.

  3. 3.

    Make sure the Platform pop-up menu displays OS X.

  4. 4.

    Click the Next button. Xcode asks where you want to store the playground.

  5. 5.

    Choose a folder to store your project and click the Create button.

  6. 6.

    Edit the playground code as follows:

    import Cocoa

    var counter : Int

    counter = 20

    print (counter++)

    print (++counter)

The postfix increment operator and the prefix increment operator can be a shortcut, but could have unintended consequences if used incorrectly. Another shortcut is Swift’s if-else statement. Normally it looks like this:

if Booleancondition {

    // Do something if true

} else {

    // Do something if false

}

However, Swift also offers an if-else shortcut that looks like this:

Booleancondition ? Result1 : Result2

Unless you’re familiar with this shortcut version of the if-else statement, it can be confusing even though it’s shorter. Any time code is confusing, there’s a risk you’ll use it incorrectly. To see how this if-else statement works, follow these steps:

  1. 1.

    Make sure the DefensePlayground is loaded into Xcode.

  2. 2.

    Edit the playground code as follows:

    import Cocoa

    var i : Int

    i = 1

    // Normal if-else statement

    if i < 5 {

        print ("Less than 5")

    } else {

        print ("Greater than 5")

    }

    i = 1

    // Ternary operator version of if-else statement

    print ((i < 5) ? "Less than 5" : "Greater than 5"

    )

Notice that both if-else statements work exactly alike as shown in Figure 21-2, but the shortcut version is much harder to understand in exchange for typing less code. Any time code is hard to understand, there’s always the risk that you’ll use it incorrectly. When in doubt, don’t use shortcuts.

Figure 21-2.
figure 2figure 2

The shortcut version of the if-else statement

Working with Optional Variables

One of Swift’s most powerful features is optional variables. An optional variable can contain a value or nothing at all (identified with a nil value). However, you must use optional variables with care because if you try using them when they contain a nil value, your program could crash.

When working with optional variables, you need to worry about the following:

  • Whether the optional variable holds a nil value or not

  • Unwrapping an optional variable to retrieve its actual value

Normally you can access the value stored inside an optional by using the exclamation mark, which is called unwrapping an optional. The big problem with unwrapping an optional is if you assume its value is not nil. Suppose you had the following:

var test : String?

print (test)

print (test!)

Since the "test" optional variable hasn’t been assigned a value, its default value is nil. The first print command simply prints nil. However the second print command tries to unwrap the optional, but since its value is nil, this causes an error as shown in Figure 21-3.

Figure 21-3.
figure 3figure 3

Unwrapping an optional that has a nil value causes an error

When working with optionals, always test to see if they’re nil before you try using them. One way to test for a nil value is to simply test if the optional is equal to nil such as:

import Cocoa

var test : String?

if test == nil {

    print ("Nil value")

} else {

    print (test!)

}

In this example, the if-else statement first checks if the "test" optional is equal to nil. If so, then it prints "Nil value." If not, then it can safely unwrap the optional using the exclamation mark.

Another way to check for a nil value is to assign a constant to an optional, which is known as optional binding. Instead of using the optional variable, you assign or bind its value to a constant such as:

if let constant = optionalVariable {

    // Optional is not nil

} else {

    // Optional is a nil value

}

If you need to test several optional variables, you can list them on a single line like this:

if let constant = optionalVariable, constant2 = optionalVariable2 {

    // No optionals are nil

} else {

    // One or more optionals are nil

}

If the constant holds a value, then the if-else statement can use the constant value. If the constant does not hold a value (contains nil), then the if-else statement does something else to avoid using a nil value. By using a constant to store the value of an optional variable, you avoid unwrapping an optional variable using the exclamation mark.

Let’s see how to use optional binding:

  1. 1.

    Make sure the DefensePlayground is loaded into Xcode.

  2. 2.

    Edit the playground code as follows:

    import Cocoa

    var test : String?

    var final : String?

    if let checkMe = test, let checkMe2 = final {

        print (checkMe)

    } else {

        print ("Nil value in one or both optionals")

    }

    test = "Hello"

    if let checkMe = test, let checkMe2 = final {

        print (checkMe)

    } else {

        print ("Nil value in one or both optionals")

    }

    final = "Bye"

    if let checkMe = test, let checkMe2 = final {

        print (checkMe + " & " + checkMe2)

    } else {

        print ("Nil value in one or both optionals")

    }

The above code assigns two different optional variable values to two different constants. Only if both constants contain a non-nil value will the first part of the if-else statement run. If one or both constants contain a nil value, then the else part of the if-else statement runs as shown in Figure 21-4. Also notice that none of the code unwraps any optional variable using the exclamation mark.

Figure 21-4.
figure 4figure 4

Using optional binding to assign an optional variable value to a constant

Working with Optional Chaining

When dealing with optionals, you typically declare an optional variable with a question mark ? such as Int?, String?, Float?, and Double? Besides using common data types, you can declare any type as an optional variable including classes.

To declare an ordinary data type as an optional variable, you just add a question mark to the data type name like this:

var myNumber : String?

Normally properties in a class hold a common data type such as String, Int, or Double. However, a class property can also hold another class. If you can declare a class as a type, you can also declare a class as an optional variable, too, like this:

class Dreamer {

    var candidate: Politician?

}

class Politician {

    var name = ""

}

Now if you create an object based on the Dreamer class, you’ll wind up with a candidate property that holds an optional variable.

let person = Dreamer()

The person object is based on the Dreamer class, which means it also holds a property called candidate. However the candidate property is also an optional variable object based on the Politician class. At this point the candidate property has an initial value of nil.

To see how an object can have another object as an optional variable, follow these steps:

  1. 1.

    Make sure the DefensePlayground is loaded into Xcode.

  2. 2.

    Edit the playground code as follows:

    import Cocoa

    class Dreamer {

        var candidate: Politician?

    }

    class Politician {

        var name : String = ""

    }

    let person = Dreamer()

    if let check = person.candidate?.name {

        print ("Name = \(check)")

    }

    else {

        print ("Nil value here")

    }

This code fails to create an object from the Politician class, so the candidate property is nil. Because we declared the candidate property as an optional variable, we can use what’s called optional chaining to access its name property in the if-else statement as shown in Figure 21-5.

Figure 21-5.
figure 5figure 5

Optional chaining can access an optional variable property of a class

To store a value in the candidate property, we need to create another object like this:

person.candidate = Politician ()

Then we need to store data in the Politician class’s name property. To do this, we need to unwrap the optional variable (the Politician class) to access its name property like this:

person.candidate!.name = "Sally"

Remember, unwrapping optionals with the exclamation mark should only be done when you’re absolutely sure the optional variable contains a value. In this case, we’re assigning a value (“Sally”) to the optional variable so it’s safe to unwrap the optional variable.

Let’s see how to modify our previous code to store a value in an optional variable property:

  1. 1.

    Make sure the DefensePlayground is loaded into Xcode.

  2. 2.

    Edit the playground code as follows:

    import Cocoa

    class Dreamer {

        var candidate: Politician?

    }

    class Politician {

        var name : String = ""

    }

    let person = Dreamer()

    if let check = person.candidate?.name {

        print ("Name = \(check)")

    }

    else {

        print ("Nil value here")

    }

    person.candidate = Politician ()

    person.candidate!.name = "Sally"

    if let check = person.candidate?.name {

        print ("Name = \(check)")

    }

    else {

        print ("Nil value here")

    }

Notice that the candidate optional variable (Politician) is undefined or nil until we specifically store a value in it. As soon as we store a string (“Sally”) into the name property, it no longer holds a nil value as shown in Figure 21-6.

Figure 21-6.
figure 6figure 6

You need to unwrap an optional variable to store a value in it

The key when working with optional variables is to make sure you use the question mark ? and exclamation mark ! symbols correctly. Fortunately if you omit either symbol, Xcode can often prompt you to choose the right symbol as shown in Figure 21-7.

Figure 21-7.
figure 7figure 7

Xcode’s editor can prompt you when to use the ? or ! symbols when working with optional variables

The question mark ? symbol is used to create optional variables or safely access optional variables. The exclamation mark ! symbol is used to unwrap optional variables, so make sure those unwrapped optional variables hold a non-nil value.

In general, any time you see a question mark ? used with optional variables, your code is likely safe when dealing with nil values. However, any time you see an exclamation mark ! with optional variables, make sure the optional variable holds a value because if it holds a nil value, your code may be unsafe and could crash.

Error Handling

Since you can’t expect to write bug-free code every time, Swift offers a feature called error handling. The idea behind error handling is to identify and catch likely errors so your program handles them gracefully rather than let the program crash or act unpredictably. To handle errors, you need to go through several steps:

  • Define descriptive errors that you want to identify

  • Identify one or more functions to detect an error and identify the type of error that occurred

  • Handle the error

Defining Errors with enum and ErrorType

In the old days, programs often displayed cryptic error messages filled with hexadecimal numbers and odd symbols. Those type of error messages are generally useless to identify the problem that may have occurred. That’s why the first step to handling errors is creating a list of descriptive error types.

The names you give different errors are completely arbitrary, but you should give meaningful names to different types of error messages. To create a list of descriptive error types, you need to create an enum that uses the ErrorType protocol like this:

enum enumName : ErrorType {

    case descriptiveError1

    case descriptiveError2

    case descriptiveError3

}

Replace enumName with a descriptive name that identifies the type of error you want to identify. Then type a descriptive error name as one word (without spaces) using the case keyword. Your list of possible errors can range from one to as many errors as you wish to describe so you’re not limited to a fixed number. If you wanted to create an enum that lists three error types, you might use code like this:

enum ageError : ErrorType {

    case negativeAge // Negative numbers

    case unrealAge   // Numbers > 140

    case underAge    // Under 21

}

Creating a Function to Identify Errors

Once you’ve created a list of descriptive error types, you now need to create one or more functions where you think the error might occur. Normally you would define a function like this:

func functionName () {

        // Code goes here

}

To make a function identify errors, you need to insert the throws keyword like this:

func functionName () throws {

        // Code goes here

}

If the function returns a value, the “throws” keyword appears before the return data type like this:

func functionName () throws -> dataType {

        // Code goes here

}

The throws keyword means that the function “throws” the error for some other part of your code to handle. Inside a function with the throws keyword, you need to use one or more guard statements.

A guard statement identifies what’s allowed and works similar to an if-else statement. If the guard statement’s Boolean condition is true, then nothing happens. However if the guard statement is false, then the guard statement can use the throw keyword to identify a specific type of error like this:

guard Booleancondition else {

        throw enumName.descriptiveError

}

If you wanted to allow only values greater than 21, you could create a guard statement like this:.

guard myAge > 21 else {

        throw ageError.underAge

}

This guard statement would do nothing if the Boolean condition (myAge > 21) is true. Otherwise it would throw the ageError.underAge error where “ageError” is the name of the enum that lists different errors to identify and “underAge” is one of the possible errors likely to occur.

The guard statement always defines acceptable behavior (myAge > 21) or else it identifies an error with the throw keyword. A function (identified with the throws keyword) can have one or more guard statements.

Handling the Error

When you’ve identified one or more functions with the throws keyword where it uses one or more guard statements to identify possible errors, you finally need a way to handle those errors. In Swift you do that using a do-try-catch statement like this:

do {

    try callFunctionThatThrows

} catch enumName.descriptiveError {

    // Code to handle the error

}

The do-try-catch statement tries to run a function that can identify errors using the throws keyword. If the function doesn’t identify any errors with one or more guard statements, then the do-try-catch statement works exactly like a normal function call.

However if the function identifies an error, the do-try-catch statement is where you need to write code to handle that error. How you handle this error can be as simple as printing a message to let you know of a problem or as complicated as running an entirely new set of code just to deal with the problem.

The point is that if you anticipate possible errors and write code to handle those errors, your program will be less likely to crash. If your program does crash, simply find out why (which may not be easy), create a new descriptive error name to identify it, and then create a new catch statement to deal with that error.

Let’s see how error handling works:

  1. 1.

    Make sure the DefensePlayground is loaded into Xcode.

  2. 2.

    Edit the playground code as follows:

    import Cocoa

    enum ageError : ErrorType {

        case negativeAge // Negative numbers

        case unrealAge   // Numbers > 140

        case underAge    // Under 21

    }

    func checkAge(myAge : Int) throws {

        print (myAge)

        guard myAge > 0 else {

            throw ageError.negativeAge

        }

        guard myAge <= 140 else {

            throw ageError.unrealAge

        }

        guard myAge > 21 else {

            throw ageError.underAge

        }

    }

    var oldAge = -9

    do {

        try checkAge (oldAge)

    } catch ageError.negativeAge {

        print ("An age can’t be a negative number")

    } catch ageError.unrealAge {

        print ("An age can’t be greater than 140")

    } catch ageError.underAge {

        print ("Sorry, you have to be 21 or older")

    }

    print (oldAge)

Notice how a negative number gets identified and handled with a simple message as shown in Figure 21-8.

Figure 21-8.
figure 8figure 8

Error handling involves an enum, a function with guard statements, and a do-try-catch statement

Change the value of the oldAge variable to 250 to see how the do-try-catch statement catches that number as higher than 140, which makes it unrealistic. Change the value of the oldAge variable to 15 to see how the do-try-catch statement identifies an age under 21.

To use error handling in your programs, you must first identify possible problems, then you need to decide how to handle these problems. Error handling can only identify errors you already know about, but it can make your programs less fragile when confronted with common types of problems your program may face.

Summary

No matter how talented, skilled, or educated you may be as a programmer, you will make mistakes occasionally. Sometimes those mistakes will be simple errors you can easily correct, but sometimes you’ll make mistakes that cause a variety of subtle problems that will challenge and frustrate you to find and fix. That’s just the nature of programming.

However, any problems you accidentally create are also problems you can learn to discover and fix. Once you learn how one mistake works, you’ll likely remember how to fix those types of mistakes in the future if you or someone else makes them again. That frees you up to make new mistakes you’ll likely never have experience facing again.

The best way to reduce errors in your program is to examine your code carefully and assume nothing will go right. Then look for every possible way to make sure it can’t go wrong. The more you code with a defensive mindset, the more likely you’ll prevent problems before they occur.

Programming is more of an art than a science, so learn the best ways that work for you to reduce introducing errors in your programs. When experimenting with new code, it’s usually safer to test it out in a Swift playground or in a simple program separate from your real program.

That way you can isolate and test your code safely. When it works, then you can copy and paste it into your program.

Errors are a way of life in the world of programming. Ideally you want to spend more time writing code and less time debugging it.