Mastering Swift
上QQ阅读APP看书,第一时间看更新

Control flow

Control flow, also known as flow of control, refers to the order in which statements, instructions, or functions are executed within an application. Swift supports all of the familiar control flow statements that are in C-like languages. These include loops (that is, for, while, and do/while), conditional statements (that is, if and switch) and the transfer of control statements (that is, break and continue). In addition to the standard C control flow statements, Swift has also added additional statements, such as the for-in loop, and has enhanced some of the existing statements, such as the switch statement.

Let's begin by looking at conditional statements in Swift.

Conditional statements

A conditional statement will check a condition and execute a block of code only if the condition is met. Swift provides both the if and if-else conditional statements. Let's take a look at how to use the if and if-else conditional statements to execute blocks of code, only if a specified condition is met.

The if statement

The if statement will check the conditional statement and if it is true, it will execute the block of code. The if statement takes the following format:

if condition {
  block of code
}

Now, let's look at how to use the if statement:

let teamOneScore = 7
let teamTwoScore = 6
if teamOneScore > teamTwoScore {
    println("Team One Won")
}

In the preceding example, we begin by setting the teamOneScore and teamTwoScore variables. We then use the if statement to check if the value of teamOneScore is greater than the value of teamTwoScore. If the value is greater, we print Team One Won to the console. If we run this code, we will indeed see that Team One Won is printed to the console, but if the value of teamTwoScore were greater than the value of teamOneScore, then nothing would be printed to the console. That would not be the best way to write an application, because we would want the user to know which team actually won. The if-else statement can help us with this problem.

Executing codes with the if-else statement

The if-else statement will check the conditional statement and if it is true, it will execute a block of code. If the conditional statement is not true, then it will execute a separate block of code. The if-else statement follows this format:

if condition {
   block of code if true
} else {
   block of code if not true
}

Let's modify the preceding example to tell the user which team won:

var teamOneScore = 7
var teamTwoScore = 6
if teamOneScore > teamTwoScore {
    println("Team One Won")
} else {
    println("Team Two Won")
}

This new version will print out Team One Won, if the value of teamOneScore is greater than the value of teamTwoScore; otherwise, it will print out the message, Team Two Won. What do you think the code will do if the value of teamOneScore were equal to the value of teamTwoScore? In the real world, we will have a tie, but in the preceding code, we will print out Team Two Won; this would not be fair to team one. In cases like this, we can use multiple else if and plain else statements, as shown in the following example:

var teamOneScore = 7
var teamTwoScore = 6
if teamOneScore > teamTwoScore {
    println("Team One Won")
} else if teamTwoScore > teamOneScore {
    println("Team Two Won")
} else {
    println("We have a tie")
}

In the preceding code, if the value of teamOneScore is greater than the value of teamTwoScore, we print Team One Won to the console. We then have another if statement that checks to see whether the value of teamTwoScore is greater than the value of teamOneScore, but this if statement follows an else statement, which means the if statement is checked only if the previous conditional statement is false. Finally, if both the if statements were false, then we assume that the values are equal and print We have a tie to the console.

A conditional statement checks the condition once, and if the condition is met, it executes the block of code. What if we wanted to continuously execute the block of code until a condition is met? For this, we would use one of the looping statements that are in Swift. Let's take a look at looping statements in Swift.

The for loops

The for loop variants are probably the most widely used looping statements. Swift offers the standard C-based for loop and also an extra for-in loop. The standard C-based for loop executes a block of code until a condition is met, usually by incrementing or decrementing a counter. The for-in statement will execute a block of code for each item in a range, collection, or sequence. We usually use one of the for loop variants when we need to iterate over a collection, or have a set number of times we want to execute a block of code.

Using the for loop variant

Let's begin by looking at the standard C-based for loop and how we would use it. The format for the for statement looks like this:

for initialization; condition; update-rule {
  block of code
}

As shown in the preceding format, the for loop has three sections:

  • Initialization: This is where we initialize any variables needed; this can contain multiple initializations, separated by commas, if needed
  • Condition: This is the condition to check; when the condition is false, the loop will exit
  • Update-rule: This is the what needs to be updated at the end of each loop

It is important to understand the order in which the sections are called. When the execution of the code encounters a for loop, the initialization section of the for loop is called to initialize the variables. Next, the condition section is executed to verify whether the block of code should be executed, and, if so, it will execute the block of code. Finally, the update-rule is called to perform any updates before looping back and starting over.

The following example shows how to use the for loop to go through a range of numbers:

for var index = 1; index <= 4; index++ {
    println(index)
}

In the preceding example, the index variable is initialized to the number 1. At the beginning of each loop, we check whether the index variable is equal to or less than number 4. If the index variable is equal to or less than number 4, the block of code is executed (which prints the index variable to the console). Finally, we increment the index variable before looping back and starting over. Once the index variable is greater than 4, the for loop exits. If we run the preceding example, the numbers 1 through 4 will indeed be printed to the console.

One of the most common uses of a for loop is to iterate through a collection and perform a block of code for each item in that collection. Let's look at how to loop through an array, followed by an example of how to loop through a dictionary:

var countries = ["USA","UK", "IN"]
for var index = 0; index < countries.count; index++ {
    println(countries[index])
}

In the preceding example, we begin by initializing the countries array with the abbreviations of three countries. In the for loop, we initialize the index variable to 0 (the first index of the array), and in the condition statement of the for loop, we check whether the index variable is less than the number of elements in the countries array. Each time we loop, we retrieve and print the value from the countries array at the index specified by the index variable.

One of the biggest mistakes that new programmers make when they use a for loop to iterate through an array, is to use the less than or equal to (<=) operator, rather than the less than (<) operator. Using a less than or equal to (<=) operator would cause one, too many iterations through the loop and generate an Index out of Bounds exception when the code is run. In the preceding example, a less than or equal to operator will generate a count from 0 through 3 inclusively because there are three elements in the array; however, the elements in the array have indexes from 0 through 2 (0, 1, and 2). So, when we try to retrieve the value at index 3, the Index out of Bounds exception will be thrown.

Let's look at how we would iterate through a dictionary with a standard C-based for loop:

var dic = ["USA": "United States", "UK": "United Kingdom", "IN":"India"]

var keys  = dic.keys.array
for var index = 0; index < keys.count; index++ {
  println(dic[keys[index]])
}

In the preceding example, we begin by creating a dictionary object that contains country names as the values with their abbreviations as the keys. We then use the keys property of the dictionary to get an array of keys. In the for loop, we initialize the index variable to 0, verify whether the index variable is less than the number of elements in the countries array and then increments the index variable at the end of each loop. Each time we loop, we print the country's name to the console.

Now, let's look at how to use the for-in statement and how it can help prevent common mistakes that occur when we use the standard for statement.

Using the for-in loop variant

In the standard for loop, we provide an index and then loop until a condition is met. While this approach is very good when we want to loop through a range of numbers, it can cause bugs, as mentioned earlier, if our condition statements are not correct. The for-in loop is designed to prevent these types of exceptions.

The for-in loop iterates over a collection of items or a range of numbers and executes a block of code for each item in the collection or range. The format for the for-in statement looks like this:

for variable in Collection/Range {
  block of code
}

As we can see in the preceding code, the for-in loop has two sections:

  • Variable: This variable will change each time the for-in loop executes and will hold the current item from the collection or range
  • Collection/Range: This is the collection or range to iterate through

Let's look at how to use the for-in loop to iterate through a range of numbers:

for index in 1...5 {
    println(index)
}

In the preceding example, we iterate over a range of numbers from 1 to 5 and print each of the numbers to the console. This particular for-in statement uses the close range operator () to give the for-in loop a range to go through. Swift also provides a second range operation called the half-open range operator (..<). The half-open range operator iterates through the range of numbers but does not include the last number. Let's look at how to use the half-range operator:

for index in 1..<5 {
  println(index)
}

In the closed range operator example (), we will see the numbers 1 though 5 printed to the console. In the half-range operator example, the last number (5) will be excluded; therefore, we will see the numbers 1 though 4 printed to the console.

Now, let's see how to iterate over an array with the for-in loop:

var countries = ["USA","UK", "IN"]
for item in countries {
    println(item)
}

In the preceding example, we iterate through the countries array and print each element of the counties array to the console. As we can see, iterating though an array with the for-in loop is safer, cleaner, and a lot easier than using the standard C-based for loop.

Let's look at how to iterate over a dictionary with the for-in loop:

var dic = ["USA": "United States", "UK": "United Kingdom", "IN":"India"]

for (abbr, name) in dic {
  println("\(abbr) --  \(name)")
}

In the preceding example, we used the for-in loop to iterate through each key-value pair of a dictionary. Each item in the dictionary is returned as a (key,value) tuple. We can decompose (key,value) tuple members as named constants within the body of the for-in loop. One thing to note is that since a dictionary does not guarantee the order that items are stored in, the order that they are iterated over may not be the same as the order they were inserted in.

Now, let's look at another type of loop, the while loop.

The while loop

The while loop executes a block of code until a condition is met. Swift provides two forms of while loops, these are the while and do-while loops. We use while loops when the number of iterations to perform is not known and is usually dependent on some business logic. A while loop is used when you want to run a loop zero or more times, while a do-while loop is used when you want to run the loop one or more times.

Using the while loop

The while loop starts by evaluating a conditional statement and then repeatedly executes a block of code while the conditional statement is true. The format for the while statement is as follows:

while condition {
  block of code
}

Let's look at how to use a while loop. In the following example, the while loop will continue to loop while a randomly generated number is less than 4. In this example, we are using the arc4random() function to generate a random number between 0 and 4.

var ran = 0
while ran < 4 {
    ran = Int(arc4random() % 5)
}

In the preceding example, we begin by initializing the ran variable to 0. The while loop then checks the ran variable, and if its value is less than 4, a new random number, between 0 and 4, is generated. The while loop will continue to loop while the randomly generated number is less than 4. Once the randomly generated number is equal to or greater than 4, the while loop will exit.

In the preceding example, the while loop checks the conditional statement prior to generating a new random number. What if we did not want to check the conditional statement prior to generating a random number? We could generate a random number when we initialize the ran variable, but that means we would need to duplicate the code that generates the random numbers. That would not be an ideal solution. It would be preferable to use the do-while loop for such instances.

Using the do-while loop

The difference between the while and do-while loops is that, while loops check the conditional statement prior to executing the block of code in the loop block, that is, all variables in the conditional statements need to be initialized prior to executing the while loop. The do-while loop will run through the loop block prior to checking the conditional statement for the first time; this means that we can initialize the variables in the conditional block of code. Use of the do-while loop is preferred when the conditional statement is dependent on the code in the loop block. The do-while loop takes the following format:

do {
   block of code
} while condition

Let's take a look at this specific example by creating a do-while loop where we initialize the variable we are checking, in the conditional statement, within the loop block:

var ran: Int
do {
    ran = Int(arc4random() % 5)
} while ran < 4

In the preceding example, we define the ran variable as an Int, but we do not initialize it until we enter the loop block and generate a random number. If we try to do this with the while loop (leaving the ran variable uninitialized), we will receive a Variable used before being initialized exception.

The switch statement

The switch statement takes a value and then compares it to several possible matches, and executes the appropriate block of code based on the first successful match. The switch statement is an alternative to using the if statement when there could be several possible matches. The switch statement takes the following format:

switch value {
  case match1 :
    block of code
  case match2 :
    block of code
  …… as many cases as needed
  default :
    block of code
}

Unlike the switch statements in most other languages, in Swift, it does not fall through to the next case statement; therefore, we do not need to use a break statement to prevent the fall through. This is another safety feature that is built into Swift, since one of the most common programming mistakes, with the switch statement, made by beginner programmers is to forget the break statement at the end of the case statement. Let's look at how to use the switch statement:

var speed = 300000000
switch speed {
case 300000000:
    println("Speed of light")
case 340:
    println("Speed of sound")
default:
    println("Unknown speed")
}

In the preceding example, the switch statement takes the value of the speed variable and compares it to the two case statements, and if the value of speed matches either case, it will print out what the speed is. If the switch statement does not find a match, it will print out the unknown speed message.

Every switch statement must have a match for all possible values. This means that unless we are matching against an enum, each switch statement must have a default case. Let's look at a case where we do not have a default case:

var num = 5
switch num {
case 1 :
    println("number is one")
case 2 :
    println("Number is two")
case 3 :
    println("Number is three")
}

If we put the preceding code into a Playground and attempt to execute it, we will receive an switch must be exhaustive, consider adding a default clause error. This is a compile time error; therefore, we will not be notified until we attempt to execute the code.

It is possible to include multiple items in a single case. To set multiple items within a single case, we would need to separate the items with a comma. Let's look at how we would use the switch statement to tell us if a character was a vowel or a consonant:

var char : Character = "e"
switch char {
case "a", "e", "i", "o", "u":
    println("letter is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    println("letter is a consonant")
default:
    println("unknown letter")
}

We can see in the preceding example that each case has its multiple items. Commas separate these items and the switch statement will attempt to match the char variable to each item listed in the case statement.

It is also possible to check the value of a switch statement to see if it is included in a range. To do this, we would use a range operator in the case statement, as you can see in the following example:

var grade = 93
switch grade {
case 90...100:
    println("Grade is an A")
case 80...89:
    println("Grade is a B")
case 70...79:
    println("Grade is an C")
case 60...69:
    println("Grade is a D")
case 0...59:
    println("Grade is a F")
default:
    println("Unknown Grade")
}

In the preceding example, the switch statement takes the grade variable and compares it with the grade ranges in each case statement, and prints out the appropriate grade.

In Swift, any case statement can contain an optional guard expression that can provide an additional condition to validate. The guard expression is defined with the where keyword. Let's say, in our preceding example, we had students who were receiving special assistance in the class and we wanted to define a grade of D for them in the range of 55 to 69. The following example shows how to do this:

var studentId = 4
var grade = 57
switch grade {
case 90...100:
    println("Grade is an A")
case 80...89:
    println("Grade is a B")
case 70...79:
    println("Grade is an C")
case 55...69 where studentId == 4:
    println("Grade is a D for student 4")
case 60...69:
    println("Grade is a D")
case 0...59:
    println("Grade is a F")
default:
    println("Unknown Grade")
}

One thing to keep in mind with the guard expression is that Swift will attempt to match the value starting with the first case statement and working its way down checking each case statement in order. This means, if we put the case statement with the guard expression after the Grade F case statement, then the case statement with the guard expression would never be reached. The following example illustrates this:

var studentId = 4
var grade = 57
switch grade {
case 90...100:
    println("Grade is an A")
case 80...89:
    println("Grade is a B")
case 70...79:
    println("Grade is an C")
case 60...69:
    println("Grade is a D")
case 0...59:
    println("Grade is a F")
//The following case statement would never be reached because the
//grades would always match one of the previous two
case 55...69 where studentId == 4:
    println("Grade is a D for student 4")
default:
    println("Unknown Grade")
}
Note

A good rule of thumb is that if you are using guard expressions, always put the case statements with the guard expression before any similar case statements without guard expressions. Now, let's look at control transfer statements.

Control transfer statements

Control transfer statements are used to transfer control to another part of the code. Swift offers four control transfer statements; these are continue, break, fallthrough, and return, and then look at the return statement when we discuss Functions later in this chapter.

The continue statement

The continue statement tells a loop to stop executing the code block and go to the next iteration of the loop. The following example shows how to use a continue statement to print out only the odd numbers in a range:

for i in 1...10 {{{
    if i % 2 == 0 {
        continue
    }
    println("\(i) is odd")
}

In the preceding example, we loop through a range of 1 through 10. For each iteration of the for-in loop, we use the remainder (%) operator to see if the number is odd or even. If the number is even, the continue statement tells the loop to immediately go to the next iteration of the loop. If the number is odd, we print out the number is odd and then move ahead. The output of the preceding code is as follows:

1 is odd
3 is odd
5 is odd
7 is odd
9 is odd

Now, let's look at the break statement.

The break statement

The break statement immediately ends the execution of a code block within the control flow. The following example shows how to break out of a for loop when we encounter the first even number:

for i in 1...10 {
    if i % 2 == 0 {
        break
    }
    println("\(i) is odd")
}

In the preceding example, we loop through the range of 1 through 10. For each iteration of the for loop, we use the remainder (%) operator to see if the number is odd or even. If the number is even, we use the break statement to immediately exit the loop. If the number is odd, we print out that the number is odd and then go to the next iteration of the loop. The preceding code has the following output:

1 is odd
The fallthrough statement

In Swift, switch statements do not fall through like other languages; however, we can use the fallthrough statement to force them to fall through. The fallthrough statement can be very dangerous because once a match is found; then, the next case defaults to true and that code block is executed. The following example illustrates this:

var name = "Jon"
var sport = "Baseball"
switch sport {
case "Baseball":
    println("\(name) plays Baseball")
    fallthrough
case "Basketball":
    println("\(name) plays Basketball")
    fallthrough
default:
    println("Unknown sport")
}

In the preceding example, since the first case, Baseball, matches the code and the remaining code blocks also execute, the output looks like this:

Jon plays Baseball
Jon plays Basketball
Unknown sport

Now that we have seen how the control flow statements work in Swift, let's give an introduction to functions and classes in Swift.