3.
Basic Control Flow
Written by Matt Galloway
When writing a computer program, you must tell the computer what to do in different scenarios. For example, a calculator app would need to do one thing if the user taps the addition button, another if the user taps the subtraction button, another if the user taps the multiplication button, and so on.
In computer programming terms, this concept is known as control flow, named so because various methods control the flow of the program. This chapter will teach you how to make decisions and repeat tasks in your programs using syntax to control the flow. You’ll also learn about Booleans, which represent true and false values, and how you can use these to compare data.
Comparison Operators
You’ve seen a few types, such as Int, Double and String. Here you’ll learn about another type that lets you compare values through the comparison operators.
When you perform a comparison, such as looking for the greater of two numbers, the answer is either true or false. Swift has a data type just for this! It’s called a Bool, which is short for Boolean, after a rather clever man named George Boole who invented an entire field of mathematics around the concept of true and false.
Here is how you use a Boolean in Swift:
let yes: Bool = true
let no: Bool = false
And because of Swift’s type inference, you can leave off the type annotation:
let yes = true
let no = false
A Boolean can only be true or false, denoted by the keywords true and false. In the code above, you use the keywords to set the state of each constant.
Boolean Operators
You commonly use Booleans to compare values. For example, you may have two values and want to know if they’re equal: either they are (true), or they aren’t (false).
In Swift, you do this using the == operator which is called the equality operator:
let doesOneEqualTwo = (1 == 2)
Swift infers that doesOneEqualTwo is a Bool. Clearly, 1 does not equal 2, so doesOneEqualTwo will be false.
Similarly, you can find out if two values are not equal using the != operator:
let doesOneNotEqualTwo = (1 != 2)
This time, the comparison is true because 1 does not equal 2, so doesOneNotEqualTwo will be true.
The prefix ! operator, also called the not-operator, toggles true to false and false to true. Another way to write the above is:
let alsoTrue = !(1 == 2)
Because 1 does not equal 2, (1 == 2) is false, and then ! flips it to true.
Two more operators let you determine if a value is greater than (>) or less than (<) another value. You’ll likely know these from mathematics:
let isOneGreaterThanTwo = (1 > 2)
let isOneLessThanTwo = (1 < 2)
And it’s not rocket science to prove that isOneGreaterThanTwo will equal false and isOneLessThanTwo will equal true.
There’s also an operator that lets you test if a value is less than or equal to another value: <=. It’s a combination of < and == and will return true if the first value is either less than the second value or equal to it.
Similarly, there’s an operator that lets you test if a value is greater than or equal to another — you may have guessed that it’s >=.
Boolean Logic
Each of the examples above tests just one condition. When George Boole invented the Boolean, he had much more planned for it than these humble beginnings. He invented Boolean logic, which lets you combine multiple conditions to form a result.
One way to combine conditions is by using AND. When you AND two Booleans together, the result is another Boolean. If both input Booleans are true, then the result is true. Otherwise, the result is false.
In Swift, the operator for Boolean AND is &&, used like so:
let and = true && true
In this case, and will be true. If either of the values on the right were false, then and would be false.
Another way to combine conditions is by using OR. When you OR together two Booleans, the result is true if either of the input Booleans is true. Only if both input Booleans are false will the result be false.
In Swift, the operator for Boolean OR is ||, used like so:
let or = true || false
In this case, or will be true. If both values on the right were false, then or would be false. If both were true, then or would still be true.
Swift uses Boolean logic to evaluate multiple conditions. Maybe you want to determine if two conditions are true; in that case, you’d use AND. If you only care whether one of two conditions is true, then you’d use OR.
For example, consider the following code:
let andTrue = 1 < 2 && 4 > 3 // true
let andFalse = 1 < 2 && 3 > 4 // false
let orTrue = 1 < 2 || 3 > 4 // true
let orFalse = 1 == 2 || 3 == 4 // false
Each of these tests two separate conditions, combining them with either AND or OR.
It’s also possible to use Boolean logic to combine more than two comparisons. For example, you can form a complex comparison like so:
let andOr = (1 < 2 && 3 > 4) || 1 < 4 // true
The parentheses disambiguates the expression. First, Swift evaluates the sub-expression inside the parentheses, and then it evaluates the entire expression, following these steps:
1. (1 < 2 && 3 > 4) || 1 < 4
2. (true && false) || true
3. false || true
4. true
String Equality
Sometimes you want to determine if two strings are equal. For example, a children’s game of naming an animal in a photo would need to determine if the player answered correctly.
In Swift, you can compare strings using the standard equality operator, ==, in the same way you compare numbers. For example:
let guess = "dog"
let dogEqualsCat = guess == "cat"
Here, dogEqualsCat is a Boolean that, in this case, equals false because "dog" does not equal "cat". Simple!
Just as with numbers, you can compare not just for equality but also to determine if one value is greater than or less than another value. For example:
let order = "cat" < "dog"
This syntax checks if one string comes before another alphabetically. In this case, order equals true because "cat" comes before "dog".
Note: You will learn more about string equality in Chapter 9, “Strings”. Some interesting things crop up when strings contain special characters and capitalization.
Toggling a Bool
A Bool often represents the state of something being “on” or “off”. In those cases, it’s common to toggle between states. For example, you could use a Bool to represent the state of a light switch in your application and toggle between the states of “on” and “off”.
For these situations, Swift provides a handy way to flip a Bool from true to false and back again. Like so:
var switchState = true
switchState.toggle() // switchState = false
switchState.toggle() // switchState = true
Here, the variable called switchState starts as true. Then, after one toggle, it becomes false. After another toggle, it’s set to true again.
Note: The
toggle()here is a call to a function. You’ll learn more about these in Chapter 5, “Functions”, and how they apply to types in Chapter 13, “Methods”.
Mini-Exercises
- Create a constant called
myAgeand set it to your age. Then, create a constant namedisTeenagerthat uses Boolean logic to determine if the age denotes someone in the age range of 13 to 19 inclusive. - Create another constant named
theirAgeand set it to 30. Then, create a constant namedbothTeenagersthat uses Boolean logic to determine if both ages denote teenagers. Also create a constant namedeitherTeenagersthat uses Boolean logic to determine if either age denotes a teenager. - Create a constant named
readerand set it to your name as aString. Create a constant namedauthorand set it to my name, Matt Galloway. Create a constant namedauthorIsReaderthat uses string equality to determine ifreaderandauthorare equal. - Create a constant named
readerBeforeAuthorwhich uses string comparison to determine ifreadercomes beforeauthor.
The if Statement
The first and most common way of controlling the flow of a program is through the use of an if statement, which allows the program to do something only if a certain condition is true. For example, consider the following:
if 2 > 1 {
print("Yes, 2 is greater than 1.")
}
This is a simple if statement. If the condition is true, the statement will execute the code between the braces. The statement won’t execute the code between the braces if the condition is false. It’s as simple as that!
At the heart of the if statement is the condition. The condition is the thing being checked, and then the code in the braces either runs or doesn’t. Therefore, an if statement is a form of conditional statement. You’ll see that term crop up again in this chapter.
You can extend an if statement to provide code to run if the condition is false. This is known as the else clause. Here’s an example:
let animal = "Zebra"
if animal == "Cat" || animal == "Dog" {
print("Animal is a house pet.")
} else {
print("Animal is not a house pet.")
}
Here, if animal equals either "Cat" or "Dog", the statement will run the first code block. If animal does not equal either "Cat" or "Dog", then the statement will run the block inside the else part of the if statement, printing the following to the debug area:
Animal is not a house pet.
But you can go even further than that with if statements. Sometimes you want to check one condition, then another. This is where else-if comes into play, nesting another if statement in the else clause of a previous if statement.
You can use it like so:
let hourOfDay = 12
var timeOfDay: String
if hourOfDay < 6 {
timeOfDay = "Early morning"
} else if hourOfDay < 12 {
timeOfDay = "Morning"
} else if hourOfDay < 17 {
timeOfDay = "Afternoon"
} else if hourOfDay < 20 {
timeOfDay = "Evening"
} else if hourOfDay < 24 {
timeOfDay = "Late evening"
} else {
timeOfDay = "INVALID HOUR!"
}
print(timeOfDay)
These nested if statements test multiple conditions one by one until a true condition is found. Only the code associated with that first true condition is executed, regardless of whether subsequent else-if conditions are also true. In other words, the order of your conditions matters!
You can add an else clause at the end to handle the case where none of the conditions are true. This else clause is optional if you don’t need it; here, you do need it to ensure that timeOfDay has a valid value by the time you print it out.
In the example above, the if statement takes a number representing an hour of the day and converts it to a string representing the part of the day to which the hour belongs. Working with a 24-hour clock, the statements are checked in order, one at a time:
- The first check is to see if the hour is less than 6. If so, that means it’s early morning.
- If the hour is not less than 6, the statement continues to the first
else-if, where it checks the hour to see if it’s less than 12. - Then, as conditions prove false, the statement checks the hour to see if it’s less than 17, then less than 20, then less than 24.
- The last part of the statement executes if the hour is not in the range of values.
- Finally, after all the conditions have been checked, the code prints the value of
timeOfDayto the console.
Since the hourOfDay constant is 12 in the example, the code will print the following:
Afternoon
Notice that even though both the hourOfDay < 20 and hourOfDay < 24 conditions are also true, the statement only executes the first block whose condition is true; in this case, the block with the hourOfDay < 17 condition.
Note: In the code above, the order of the comparisons is important. Consider what happens if
hourOfDayis 15. If the if-statement checked for “less than 20” before it checked for “less than 17”, then 15 would be considered “less than 20” when it should be “less than 17”. This is because the if-statement works top-to-bottom and stops when it finds a condition that is true.
Short-Circuiting
An important fact about if statements is what happens when there are multiple Boolean conditions separated by ANDs (&&) or ORs (||).
Consider the following code:
if 1 > 2 && name == "Matt Galloway" {
// ...
}
The first condition of the if statement, 1 > 2 is false. Therefore the whole expression can never be true.
So Swift will not even bother to check the second part of the expression, namely the check of name. Similarly, consider the following code:
if 1 < 2 || name == "Matt Galloway" {
// ...
}
Since 1 < 2 is true, the whole expression must be true no matter the value of name. Therefore, once again, the check of name is not executed. This short-circuiting behavior will come in handy when dealing with more complex conditions.
Constant and Variable Scope
if statements introduce a new concept scope, which controls the visibility and lifetime of constants and variables through the use of braces. Imagine you want to calculate the fee to charge your client. Here’s the deal you’ve made:
You earn $25 for every hour up to 40 hours and $50 for every hour after that.
Using Swift, you can calculate your fee in this way:
var hoursWorked = 45
var price = 0
if hoursWorked > 40 {
let hoursOver40 = hoursWorked - 40 // hoursOver40 = 5
price += hoursOver40 * 50 // price = 250
hoursWorked -= hoursOver40 // hoursWorked = 40
}
price += hoursWorked * 25 // price = 1250
print(price)
This code takes the number of hours and checks if it’s over 40. If so, the code calculates the number of hours over 40, multiplies that by $50 and then adds the result to the price. The code then subtracts the number of hours over 40 from the hours worked. It multiplies the remaining hours worked by $25 and adds that to the total price.
In the example above, the result is as follows:
1250
The interesting thing here is the code inside the if statement. There is a declaration of a new constant, hoursOver40, to store the number of hours over 40. Clearly, you can use it inside the if statement. But what happens if you try to use it after the print statement, for example?
...
print(price)
print(hoursOver40)
This would result in the following error:
This error informs you that you’re only allowed to use the hoursOver40 constant within the scope it was created. In this case, the if statement introduced a new scope, so you can no longer use the constant when that scope finishes.
However, each scope can use variables and constants from its parent scope. In the example above, the scope inside the if statement uses the price and hoursWorked variables from the parent scope.
The Ternary Conditional Operator
Now I want to introduce a new operator, one you didn’t see in Chapter 2, “Types & Operations”. It’s called the ternary conditional operator and it’s related to if statements.
If you wanted to determine the minimum and maximum of two variables, you could use if statements like so:
let a = 5
let b = 10
let min: Int
if a < b {
min = a
} else {
min = b
}
let max: Int
if a > b {
max = a
} else {
max = b
}
You know how this works by now, but it’s a lot of code. Wouldn’t it be nice if you could shrink this to just a couple of lines? Well, you can, thanks to the ternary conditional operator!
The ternary conditional operator takes a condition and returns one of two values, depending on whether the condition is true or false. The syntax is as follows:
(<CONDITION>) ? <TRUE VALUE> : <FALSE VALUE>
You can use this operator to rewrite your long code block above like so:
let a = 5
let b = 10
let min = a < b ? a : b
let max = a > b ? a : b
In the first example, the condition is a < b. If this is true, the result assigned back to min will be the value of a; if it’s false, the result will be the value of b.
I’m sure you’ll agree that it’s much simpler! This is a useful operator that you’ll find yourself using regularly. Whether it’s better to use the ternary conditional operator or an if and else block in a particular case comes down to what is easier to read and what you prefer.
Note: Because finding the greater or smaller of two numbers is such a common operation, the Swift standard library provides two functions for this purpose:
maxandmin. If you were paying attention earlier in the book, then you’ll recall you’ve already seen these.
Mini-Exercises
- Create a constant named
myAgeand initialize it with your age. Write anifstatement to print outTeenagerif your age is between 13 and 19 andNot a teenagerif your age is not between 13 and 19. - Create a constant named
answerand use a ternary condition to set it equal to the result you print out for the same cases in the above exercise. Then print outanswer.
Loops
Loops are Swift’s way of executing code multiple times. In this section, you’ll learn about one type of loop: the while loop. If you know another programming language, you’ll find the concepts and maybe even the syntax familiar.
While Loops
A while loop repeats a code block while a condition is true. You create a while loop this way:
while <CONDITION> {
<LOOP CODE>
}
The loop checks the condition before every iteration. If the condition is true, then the loop executes and moves on to another iteration. If the condition is false, then the loop stops. Like if statements, while loops introduce a scope.
The simplest while loop takes this form:
while true { }
This while loop never ends because the condition is always true. Of course, you would never write such a while loop because your program would spin forever! This situation is known as an infinite loop, and while it might not cause your program to crash, it will likely cause your computer to freeze.
Here’s a more useful example of a while loop:
var sum = 1
while sum < 1000 {
sum = sum + (sum + 1)
}
This code calculates a mathematical sequence to the point where the value of sum is greater than 1000.
The loop executes as follows:
-
Before iteration 1:
sum= 1, loop condition = true -
Before iteration 2:
sum= 3, loop condition = true -
Before iteration 3:
sum= 7, loop condition = true -
Before iteration 4:
sum= 15, loop condition = true -
Before iteration 5:
sum= 31, loop condition = true -
Before iteration 6:
sum= 63, loop condition = true -
Before iteration 7:
sum= 127, loop condition = true -
Before iteration 8:
sum= 255, loop condition = true -
Before iteration 9:
sum= 511, loop condition = true -
Before iteration 10:
sum= 1023, loop condition = false
Before the tenth iteration, the sum variable is 1023, and therefore the loop condition of sum < 1000 becomes false. At this point, the loop stops.
repeat-while Loops
A variant of the while loop is called the repeat-while loop. It differs from the while loop in that the condition is evaluated at the end of the loop rather than at the beginning. You construct a repeat-while loop like this:
repeat {
<LOOP CODE>
} while <CONDITION>
Here’s the example from the last section, but using a repeat-while loop:
sum = 1
repeat {
sum = sum + (sum + 1)
} while sum < 1000
In this example, the outcome is the same as before. However, that isn’t always the case — you might get a different result with a different condition.
Consider the following while loop:
sum = 1
while sum < 1 {
sum = sum + (sum + 1)
}
Consider the corresponding repeat-while loop, which uses the same condition:
sum = 1
repeat {
sum = sum + (sum + 1)
} while sum < 1
In the case of the regular while loop, the condition sum < 1 is false right from the start. That means the body of the loop won’t be reached! The value of sum will equal 1 because the loop won’t execute any iterations.
In the case of the repeat-while loop, sum will equal 3 because the loop executes once.
Breaking Out of a Loop
Sometimes you want to break out of a loop early. You can do this using the break statement, which immediately stops the loop’s execution and continues on to the code after the loop.
For example, consider the following code:
sum = 1
while true {
sum = sum + (sum + 1)
if sum >= 1000 {
break
}
}
Here, the loop condition is true, so the loop normally iterates forever. However, the break means the while loop will exit once the sum is greater than or equal to 1000.
You’ve seen how to write the same loop in different ways, demonstrating that there are often many ways to achieve the same result in computer programming.
You should choose the method that’s easiest to read and conveys your intent in the best way possible. This skill is one you’ll internalize with enough time and practice.
Mini-Exercises
- Create a variable named
counterand set it equal to0. Create a while loop with the conditioncounter < 10, which prints outcounter is X(whereXis replaced withcountervalue) and then incrementscounterby1. - Create a variable named
counterand set it equal to0. Create another variable namedrolland set it equal to0. Create arepeat-whileloop. Inside the loop, setrollequal toInt.random(in: 1...6), which means to pick a random number between1and6. Then incrementcounterby1. Finally, printAfter X rolls, roll is YwhereXis the value ofcounterandYis the value ofroll. Set the loop condition so the loop finishes when the first1is rolled.
Challenges
Before moving on, here are some challenges to test your knowledge of basic control flow. It is best to try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Find the Error
What’s wrong with the following code?
let firstName = "Matt"
if firstName == "Matt" {
let lastName = "Galloway"
} else if firstName == "Ray" {
let lastName = "Wenderlich"
}
let fullName = firstName + " " + lastName
Challenge 2: Boolean Challenge
In each of the following statements, what is the value of the Boolean answer constant?
let answer = true && true
let answer = false || false
let answer = (true && 1 != 2) || (4 > 3 && 100 < 1)
let answer = ((10 / 2) > 3) && ((10 % 2) == 0)
Challenge 3: Snakes and Ladders
Imagine you’re playing a game of snakes & ladders that goes from position 1 to position 20. On it, there are ladders at positions 3 and 7, which take you to 15 and 12, respectively. Then there are snakes at positions 11 and 17, which take you to 2 and 9, respectively.
Create a constant called currentPosition, which you can set to whatever position between 1 and 20 you like. Then create a constant called diceRoll, which you can set to whatever roll of the dice you want. Finally, calculate the final position considering the ladders and snakes, calling it nextPosition.
Challenge 4: Number of Days in a Month
Given a month (represented with a String in all lowercase) and the current year (represented with an Int), calculate the number of days in the month. Remember that because of leap years, February has 29 days when the year is a multiple of 4 but not a multiple of 100. February also has 29 days when the year is a multiple of 400.
Challenge 5: Next Power of Two
Given a number, determine the next power of two greater than or equal to that number.
Challenge 6: Triangular Number
Given a number, print the triangular number of that depth. You can get a refresher on triangular numbers here: https://en.wikipedia.org/wiki/Triangular_number
Challenge 7: Fibonacci
Calculate the nth Fibonacci number. Remember that Fibonacci numbers start their sequence with 1 and 1, and then subsequent numbers in the sequence are equal to the previous two values added together. You can get a refresher here: https://en.wikipedia.org/wiki/Fibonacci_number
Challenge 8: Make a Loop
Use a loop to print out the multiplication or times table up to 12 of a given factor.
Challenge 9: Dice Roll Table
Print a table showing the number of combinations to create each number from 2 to 12, given two six-sided dice rolls. You should not use a formula but compute the number of combinations exhaustively by considering each possible dice roll.
Key Points
- You use the Boolean data type
Boolto represent true and false. - The comparison operators, all of which return a Boolean, are:
- You can use Boolean logic (
&&and||) to combine comparison conditions. - You use
ifstatements to make simple decisions based on a condition. - You use
elseandelse-ifwithin anifstatement to extend the decision-making beyond a single condition. - Short-circuiting ensures that only the minimal required parts of a Boolean expression are evaluated.
- You can use the ternary operator
(a ? b : c)instead of a simpleifstatement. - Variables and constants belong to a scope beyond which you cannot use them. A scope inherits variables and constants from its parent.
-
whileloops allow you to perform a particular task zero or more times until a condition is met. -
repeat-whileloops always execute the loop at least once. - The
breakstatement lets you break out of a loop.