Making Decisions with Branches
After a program receives data, it needs to manipulate that data somehow to return a useful result. Simple programs manipulate data the same way, but more complicated programs need to make decisions on how to manipulate that data.
For example, a program might ask the user for a password. If the user types in a valid password, then the program gives the user access. If the user does not type in a valid password, then the program must display an error message and block access.
When a program analyzes data and makes a decision based on that data, it uses something called Boolean values and branching statements.
Boolean values represent either a true or false value. Branching statements give your program the option of choosing between two or more different sets of commands to follow. Boolean values combined with branching statements give programs the ability to respond to different data.
Understanding Comparison Operators
You can create a Boolean variable by declaring a Bool data type such as:
var flag : Bool
Bool represents a Boolean data type. Any variable defined as a Bool data type can only hold one of two values: true or false, such as:
flag = true
flag = false
While you can directly assign true and false to variables that represent Boolean data types, it’s far more common to calculate Boolean values using comparison operators. Some common comparison operators are shown in Table 7-1.
Table 7-1. Common comparison operators in Swift
Note The equal (==) comparison operator is the only comparison operator that can also compare strings such as “Joe” == “Fred” (which evaluates to false) or “Joe” == “Joe” (which evaluates to true).
Comparison operators can compare values such as 38 == 8 or 29.04 > 12. However, using actual values with comparison operators always returns the same true or false value. What makes comparison operators more versatile is when they compare a fixed value with a variable or when they compare two variables such as:
let myValue = 45
myValue > 38 // Evaluates to true
let yourValue = 39
myValue < yourValue // Evaluates to false
When you compare one or more variables, the results can change depending on the current value of those variables. However, when using the equal (==) comparison operator, be careful when working with decimal values. That’s because 45.0 is not the same number as 45.0000001. When working with decimal values, it’s best to use greater than or less than comparison operators rather than the equal comparison operator.
Understanding Logical Operators
Any comparison operator, such as 78 > 9 evaluates to a true or false value. However, Swift provides special logical operators just for working with Boolean values as shown in Table 7-2.
Table 7-2. Common logical operators in Swift
Comparison operator |
Purpose |
---|---|
&& |
And |
|| |
Or |
! |
Not |
Both the And (&&) and Or (||) logical operators take two Boolean values and convert them into a single Boolean value. The Not (!) logical operator takes one Boolean value and converts it to its opposite. Table 7-3 shows how the Not (!) logical operator works:
Table 7-3. The Not (!) logical operator
Example |
Value |
---|---|
!true |
false |
!false |
true |
The And (&&) operator takes two Boolean values and calculates a single Boolean value based on Table 7-4.
Table 7-4. The And (&&) logical operator in Swift
First Boolean Value |
Second Boolean Value |
Result |
---|---|---|
true && |
true |
true |
true && |
false |
false |
false && |
true |
false |
false && |
false |
false |
The Or (||) operator takes two Boolean values and calculates a single Boolean value based on Table 7-5.
Table 7-5. The Or (||) logical operator in Swift
First Boolean Value |
Second Boolean Value |
Result |
---|---|---|
true || |
true |
true |
true || |
false |
true |
false || |
true |
true |
false || |
false |
false |
To see how Boolean values work with comparison and logical operators, create a new playground by following these steps:
import Cocoa
var x, y, z : Int
x = 35
y = 120
z = -48
x == y
x < y
x > z
y != z
z > -48
// And && operator
(y != z) && (x > z)
(y != z) && (x == y)
(x == y) && (x < y)
(z > -48) && (x == y)
// Or || operator
(y != z) || (x > z)
(y != z) || (x == y)
(x == y) || (x < y)
(z > -48) || (x == y)
Notice how the comparison operators evaluate to true or false and how the logical operators (And and Or) take two Boolean values and return a single true or false value as shown in Figure 7-1.
Figure 7-1. Evaluating Boolean values with comparison and logical operators
Ultimately every Boolean value must be:
Boolean values are crucial for letting programs make choices. If someone types in a password, a program checks to see if the password is valid. If true (the password is valid), then the program grants access. If false (the password is not valid), then the program blocks access.
To decide what to do next, every program needs to evaluate a Boolean value. Only then can it decide what to do next based on the value of that Boolean value.
The simplest type of branching statement in Swift is an if statement, which looks like this:
if BooleanValue == true {
}
To simplify this if statement, you can eliminate the “== true” portion so the if statement looks like this:
if BooleanValue {
}
This shortened version says, “If the BooleanValue is true, then run the code inside the curly brackets. If the BooleanValue is false, then skip all the code inside the curly brackets without running it.”
To see how Boolean values work with an if statement, follow these steps:
import Cocoa
var BooleanValue : Bool = true
if BooleanValue {
print ("The BooleanValue is true")
}
Figure 7-2 shows the results of the if statement running. Change the value of BooleanValue to false and you’ll see that the if statement no longer prints the string “The BooleanValue is true.”
Figure 7-2. Running an if statement
The if statement either runs code or it runs nothing. If you want to run code if a Boolean value is false, you could write two separate if statements as follows:
if BooleanValue {
}
if !BooleanValue {
}
The first if statement runs if the BooleanValue is true. If not, then it does nothing.
The second if statement runs if the BooleanValue is false. If not, then it does nothing.
The problem with writing two separate if statements is that the logic of your program isn’t as clear. To fix this problem, Swift offers an if-else statement that looks like this:
if BooleanValue {
// First set of code to run if true
} else {
// Second set of code to run if false
}
An if-else statement offers exactly two different branches to follow. If a Boolean value is true, then the first set of code runs. If a Boolean is false, then the second set of code runs. At no time is it possible for both sets of code to run in an if-else statement.
To see how Boolean values work with an if-else statement, follow these steps:
import Cocoa
var BooleanValue : Bool = true
if BooleanValue {
print ("The BooleanValue is true")
} else {
print ("The BooleanValue is false")
}
Figure 7-3 shows how the if-else statement works when the BooleanValue is true. Change the BooleanValue to false and see how the if-else statement now runs the second set of code.
Figure 7-3. Running an if-else statement
An if statement either runs one set of code or it runs nothing. An if-else statement always runs one set of code or a second set of code. However, what if you want a choice of running two or more possible sets of code? In that case, you need to use an if-else if statement.
Like an if statement, an if-else if statement may not run any code at all. The simplest if-else if statement looks like this:
if BooleanValue {
// First set of code to run if true
} else if BooleanValue2 {
// Second set of code to run if true
}
With an if-else if statement, a Boolean value must be true to run code. If no Boolean value is true, then it’s possible that no code will run at all. An if-else if statement is equivalent to multiple if statements like this:
if BooleanValue {
// First set of code to run if true
}
if BooleanValue2 {
// Second set of code to run if true
}
With an if-else if statement, you can check for as many Boolean values as you want such as:
if BooleanValue {
// First set of code to run if true
} else if BooleanValue2 {
// Second set of code to run if true
} else if BooleanValue3 {
// Third set of code to run if true
} else if BooleanValue4 {
// Fourth set of code to run if true
} else if BooleanValue5 {
// Fifth set of code to run if true
}
As soon as the if-else if statement finds one Boolean value is true, then it runs the accompanying code trapped within curly brackets. However, it’s possible that no Boolean value will be true, in which case no code inside the if-else if statement will run.
If you want to ensure that at least one set of code will run, then you need to add a final else clause to the end of an if-else if statement such as:
if BooleanValue {
// First set of code to run if true
} else if BooleanValue2 {
// Second set of code to run if true
} else if BooleanValue3 {
// Third set of code to run if true
} else if BooleanValue4 {
// Fourth set of code to run if true
} else if BooleanValue5 {
// Fifth set of code to run if true
} else {
// This code runs if every other Boolean value is false
}
To see how Boolean values work with an if-else statement, follow these steps:
import Cocoa
var BooleanValue : Bool = false
var BooleanValue2 : Bool = false
var BooleanValue3 : Bool = false
if BooleanValue {
print ("BooleanValue is true")
} else if BooleanValue2 {
print ("BooleanValue2 is true")
} else if BooleanValue3 {
print ("BooleanValue3 is true")
} else {
print ("This prints if everything else is false")
}
Notice that because every Boolean value is false, the only code that runs occurs in the final else portion of the if-else if statement as shown in Figure 7-4.
Figure 7-4. Running an if-else if statement with multiple Boolean values
If you change different Boolean values to true, you can see how the if-else if statement behaves differently by running a different set of code.
With an if-else if statement:
Since an if-else if statement checks multiple Boolean values, what happens if two or more Boolean values are true? In that case, the if-else if statement only runs the set of code associated with the first true Boolean value and ignores all the rest of the code as shown in Figure 7-5.
Figure 7-5. Running an if-else if statement with multiple true Boolean values
The switch Statement
An if-else if statement lets you create two or more sets of code that can possibly run. Unfortunately, an if-else if statement can be hard to understand when there’s too many Boolean values to check. To fix this problem, Swift offers a switch statement.
A switch statement works much like an if-else if statement except that it’s easier to read and write. The main difference is that instead of checking multiple Boolean values, a switch statement checks the value of a single variable. Based on the value of this single variable, the switch statement can choose to run different sets of code.
A typical switch statement looks like this:
switch value/variable/expression {
case value1: // First set of code to run if variable = value1
case value2: // Second set of code to run if variable = value2
case value3: // Third set of code to run if variable = value3
default: // Fourth set of code to run if nothing else runs
}
A switch statement starts by examining a fixed value (such as 38 or “Bob”), a variable that represents data), or an expression (such as 3 * age where “age” is a variable). Ultimately, the switch statement needs to identify a single value and match that value to the first “case” statement it finds. The moment it finds an exact match, it runs one or more lines of code associated with that “case” statement.
To see how Boolean values work with a case statement, follow these steps:
import Cocoa
var whatNumber : Int = 3
switch whatNumber {
case 1: print ("The number is 1")
case 2: print ("The number is 2")
case 3: print ("The number is 3")
print ("Isn’t this amazing?")
case 4: print ("The number is 4")
case 5: print ("The number is 5")
default: print ("The number is undefined")
}
Because the value of the “whatNumber” variable is 3, the switch statement matches it with “case 3:” and then runs the Swift code that appears after the colon as shown in Figure 7-6. Note that you can store multiple lines of code after the colon and do not need to enclose the code within curly brackets. Change the value of the whatNumber variable to see how it affects the behavior of the switch statement.
Figure 7-6. Running a switch statement
Note Unlike other programming languages such as Objective-C, you do not need to use a break command to separate code stored in different “case” statements of the switch statement.
The above switch statement is equivalent to:
if whatNumber == 1 {
print ("The number is 1")
} else if whatNumber == 2 {
print ("The number is 2")
} else if whatNumber == 3 {
print ("The number is 3")
print ("Isn’t this amazing?")
} else if whatNumber == 4 {
print ("The number is 4")
} else if whatNumber == 5 {
print ("The number is 5")
} else {
print ("The number is undefined")
}
As you can see, the switch statement is cleaner and simpler to read and understand.
When creating a switch statement, you must handle all possibilities. In the above switch statement, the whatNumber variable can be any integer, so the switch statement can explicitly handle any value from 1 to 5. If the value does not fall within this 1 to 5 range, then the default portion of the switch statement handles any other value. If you fail to include a default, then Xcode will flag your switch statement as a possible error. (One exception to this rule is if you use a switch statement with an enum structure, which you’ll learn about later in this chapter.) This is to protect your code from possibly crashing if the switch statement receives data it doesn’t know how to handle.
This example of a switch statement tries to match a value/variable/expression with exactly one value. However, a switch statement can try matching more than one value. There are three ways a switch statement can check a range of values:
When a case statement lists all values separated by commas, its code will run only if the switch value/variable/expression exactly matches one of those values. For example, consider the following case statement:
switch whatNumber {
case 1, 2, 3: print ("The number is 1, 2, or 3")
default: print ("The number is undefined")
}
Only if the value of whatNumber is 1, 2, or 3 will its code print “The number is 1, 2, or 3.”
Explicitly listing all possible values may be fine for a handful of numbers, but for multiple numbers, it can get tedious to write every possible number. As a shortcut, Swift lets you specify a range. If you wanted to match a number between 4 and 20, the case portion of the switch statement might look like this:
switch whatNumber {
case 4...20: print ("The number is between 4 and 20")
default: print ("The number is undefined")
}
In this case, whatNumber could be 4, 20, or any number in between. The three dots signify a range that includes the starting and ending number of the range (4 and 20).
Swift can also check a half range that consists of two dots and a less than symbol such as:
switch whatNumber {
case 20..<49: print ("The number is between 20 and 48")
default: print ("The number is undefined")
}
This 20..<49 half range matches only if whatNumber is 20, 48, or any number in between. Notice that if whatNumber is 49, it will not match the half range but if whatNumber is 20, then it will match.
To see how all three variations of multiple values works, follow these steps:
import Cocoa
var whatNumber : Int = 49
switch whatNumber {
case 1, 2, 3: print ("The number is 1")
print ("Isn’t this amazing?")
case 4...20: print ("The number is between 4 and 20")
case 20..<49: print ("The number is between 20 and 48")
default: print ("The number is undefined")
}
When the value is 49, the switch statement matches nothing so it uses the default portion of the switch statement as shown in Figure 7-7.
Figure 7-7. Running a switch statement that checks for multiple values
Change the value of the whatNumber variable to 2, 12, and 48 to see how the switch statement matches different values.
Using the switch Statement with enum Data Structures
The switch statement is often used to check for specific values such as strings or numbers. However, one common use for switch statements is to work with a data structure called enum (which is short for “enumeration”). An enum data structure lets you create a list of different data with descriptive names such as:
enum dog {
case poodle
case collie
case terrier
case mutt
}
The above enum data structure creates a data type called “dog” that can only hold one of four values: poodle, collie, terrier, or mutt. To use an enum data structure, you need to create a variable or a constant such as:
var myPet = dog.collie
The above code defines a variable called “myPet” and assigns it a value of dog.collie. If you wanted to check the value of the mYpet variable, you could use a switch statement like this:
switch myPet {
case .poodle:
print ("Poodle")
case .collie:
print ("Collie")
case .terrier:
print ("Terrier")
case .mutt:
print ("Mutt")
}
To see how the switch statement works with the enum data structure, follow these steps:
import Cocoa
enum dog {
case poodle
case collie
case terrier
case mutt
}
var myPet = dog.collie
switch myPet {
case .poodle:
print ("Poodle")
case .collie:
print ("Collie")
case .terrier:
print ("Terrier")
case .mutt:
print ("Mutt")
}
The switch statement only looks to see which possible value, specified by the enum data structure, that the myPet variable holds. Since the value is .collie, the switch statement prints “Collie” as shown in Figure 7-8.
Figure 7-8. Running a switch statement that checks for multiple values
Making Decisions in an OS X Program
Using playground files to test out Swift code can be fun and simple because it lets you focus solely on learning how Swift works without the interference of other parts of writing a program. However, eventually you’ll need to see how Swift code works outside of your playground file.
In this sample program, the user needs to type an employee ID number and password. The employee ID number must fall within a range of 100 to 150 to be valid. The password must exactly match the string “password” (which isn’t a very secure password).
That means we’ll be using two comparison operators to check if the name and password are valid. Then we’ll use a logical operator to make sure that both are valid.
Create a new OS X project by following these steps:
Figure 7-9. Making the window of the user interface visible
Figure 7-10. Creating a basic user interface with labels, text fields, and a push button
Figure 7-11. The Attributes Inspector pane for a label
Figure 7-12. Double-clicking on a label lets you change the text without using the Attributes Inspector pane
Figure 7-13. The Size Inspector pane
Figure 7-14. Aligning a text field using the mouse
Figure 7-15. The completed user interface
With this user interface, the user will type an ID (an integer) into the ID text field, a password (string) in the Password text field, and then click the Check Password button to see if the ID and password are valid. Once we have a user interface, the next step is to connect the user interface to Swift code using IBOutlets and IBAction methods.
Remember, IBOutlets let you retrieve or display information to a user interface. IBAction methods let the user interface make your program do something.
In this example, we’ll need two IBOutlet variables to connect to each text field so we can retrieve the data the user types in. Then we’ll need one IBAction method to connect to the Check Password button. That way when the user clicks on the Change Password button, the IBAction method can verify the ID and password.
To connect Swift code to your user interface, follow these steps:
Figure 7-16. The Assistant editor displays the user interface next to the AppDelegate.swift file
Figure 7-17. Control-dragging from the top text field to the AppDelegate.swift file
Figure 7-18. The pop-up window for defining an IBOutlet
@IBOutlet weak var IDfield: NSTextField!
@IBOutlet weak var Passwordfield: NSTextField!
Figure 7-19. Control-dragging from the push button to the AppDelegate.swift file
Figure 7-20. Defining an IBAction method
@IBAction func checkPassword(sender: NSButton) {
let validPassword = "password"
var ID : Int
ID = IDfield.integerValue
var myAlert = NSAlert()
switch ID {
case 100...150: if (Passwordfield.stringValue == validPassword) {
myAlert.messageText = "Access granted"
} else {
myAlert.messageText = "No access"
}
default:
myAlert.messageText = "No access"
}
myAlert.runModal()
}
This IBAction method uses a switch statement to check if the user typed an ID that falls within the 100…150 range. If so, then it checks if the user typed “password” in the Password text field. If this is also true, then the switch statement displays “Access granted” in an Alert dialog. If the ID does not fall within the 100…150 range or the Password text field does not contain “password,” then the Alert dialog displays “No access.”
Note You may be wondering where stringValue and integerValue came from in the above code. If you look at your IBOutlets, you’ll see that each text field IBOutlet is based on the NSTextField class. Look up NSTextField in Xcode’s documentation, and you’ll see that NSTextField is based on the NSControl class. The NSControl class contains properties such as stringValue and integerValue so that means anything based on NSTextField (such as the IBOutlets that represent the text fields on your user interface) can also use these stringValue and integerValue properties.
Figure 7-21. Displaying an Alert dialog
Summary
To respond intelligently to the user, every program needs a way to make decisions. The first step to making a decision is to define a Boolean value, which is either true or false. Boolean values can be calculated through comparison operators or logical operators.
Once you can determine a Boolean value, then you can use that Boolean value to decide which code to follow in a branching statement. The simplest branch is an if statement that either runs a set of code or does nothing at all.
Another branch is the if-else statement, which offers exactly two sets of code to run. If a Boolean value is true, then the first set of code runs. Otherwise the second set of code runs.
If you want to choose between three or more sets of code to run, you can use an if-else if statement. However, it’s often simpler to use a switch statement instead. When using a switch statement, you must anticipate all possible values.
A switch statement lets you match an exact value, a range of values (that includes the beginning and ending numbers), or a half range of values (which only includes the beginning number but not the end).
Branching statements combined with Boolean values gives your program the ability to make decisions and run different code based on the data it receives. Branching statements essentially let your program respond intelligently to outside data.