Swift Tutorial 5 – How to Use Control Flow in Swift

In this section we’re going to look at the various looping constructs and decision statements that Swift supports. We’ll be learning about while, repeat-while, and the variations of for loops. Next, we’ll discuss decision statements such as the if, if-else, and switch case statements. Finally, we’ll finish this section by talking about the branching statements we can use to control the program flow.

BUILD GAMES

FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.

Looping Constructs

While Loops

The simplest kind of loop is called the while loop. This loop will simply run until the given condition is false. Let’s look at the following code snippet.

var counter = 0
while counter < 5 {
    print("Counter is at \(counter)")
    counter += 1
}

There are a few things to understand about this loop here. In the case of the while loop, the loop’s condition is check before the loop’s body is executed at every iteration, including the first iteration! For example, if the loop’s condition was counter < -1 , the loop wouldn’t execute at all. Also note that the loop does iterate 5 times, but the last value of the counter is 4. Outside of the loop, the counter will be 5, but the last valid iteration, just before the loop is terminated, the counter is at 4. Simply put, this while loop will execute forever until the condition is false.

Repeat-While

A repeat-while loop is almost exactly like a while loop, except with one key addition: the loop’s condition is checked at the end! This guarantees the loop will iterate at least once! Take the following code snippet.

var counter = 0
repeat {
    print("Counter is at \(counter)")
    counter += 1
} while counter < -1

The condition is not met, but the loop will still execute once, then terminate. This type of loop is particularly useful when we need to do any sort of input validation such as the following.

var input = -1
repeat {
    print("Please enter a valid input (1 - 100): ")
    input = getInputFromProgramSomehow()
} while input <= 0 || input > 100

The above loop will run the prompt until the user enters a valid input.

For-in Loops

Swift offers a sophisticated for-in loop that can be used to iterate through a range, an array, a set, a dictionary, a collection of tuples, and many other structures. In previous sections, particularly the Collection Types section, we’ve seen how to use for-in loops to iterate over an array, set, and dictionary so look back at that section for example on how to do that. Let’s see how we can iterate through the numbers 1 to 5 as a for-in loop.

for counter in 1...5 {
    print("Counter: \(counter)")
}

This loop will run 5 times, but, as we can see from the use of the closed range operator, the counter values will be 1, 2, 3, 4, 5 and not 0, 1, 2, 3, 4. We could easily fix this by changing the in clause to say 0..<5  instead. Also note how we don’t need to declare our counter variable before it’s used like we did with the for loops. The reason for this is that Swift will implicitly declare the the counter to be a constant that iterates through the values of the range, so we don’t need to declare the counter with an explicit var  or let .

There might be some cases where the counter variable itself is unnecessary and so we don’t want to create a variable name for it.

for _ in 1...5 {
    print("Hi!")
}

This is easily fixed, as shown in the above snippet, by replacing the variable name with an underscore (_).

To recap this subsection, we discussed the 3 major types of loops that Swift offers: while, repeat-while, and for-in. The while loop will execute until the condition is false. The repeat-while loop will do the same, except check the condition at the end. The versatile for-in loop can be applied to ranges, arrays, sets, dictionaries, and other structures.

Decision Statements

If and If-Else

The simplest decision statement is the if statement. It will simply execute a block of code if the condition is true.

var heartRate = 75
if heartRate <= 80 {
    print("Relaxing")
}

The above code will print “Relaxing” since the condition is met. Keep in mind that the condition can be any boolean expression that evaluates to true. We can use any of our conditional operators to create a resulting boolean. In addition, we can add an else block that executes if the condition is false.

var heartRate = 100
if heartRate <= 80 {
    print("Relaxing")
} else {
    print("Exercising")
}

Now the else block will execute since our condition was not met. We can have multiple cases by using else-if blocks in case we have several bands of values we have separate code for.

var heartRate = 100
if heartRate <= 80 {
    print("Relaxing")
} else if heartRate > 120 {
    print("Get help!")
} else {
    print("Exercising")
}

One important thing to note is that Swift will only execute the first block of code whose condition is met. This means that the order in which you put the else-if blocks does matter in some cases.

Switch

A switch statement operates on a value and executes code if it matches a pattern. We can match a single possible value or multiple values. However, the switch statement must be exhaustive, meaning that every possible value of the operand must be a case. In the event of any number type, we obviously can’t account for every possible value, so we can use a default case that’s execute if none of the other cases match.

let number:UInt = 1;
switch number {
case 0:
    print("Number is zero!")
case 1, 3, 5, 7, 9:
    print("Number is odd!")
case 2, 4, 6, 8, 10:
    print("Number is even!")
default:
    print("Number is bigger than 10!")
}

It’s important to note that only the code in the matching case is executed, no other cases are executed. In addition to matching specific values, we can also use the switch statement to match intervals.

let number:UInt = 1234;
switch number {
case 0..<10:
    print("Number is less than 10!")
case 10..<100:
    print("Number is less than 100!")
case 100..<1000:
    print("Number is less than 1000!")
default:
    print("Number is bigger than 1000!")
}

We’re using the range operators to define intervals that we can match if the number falls in one of those intervals. In addition to intervals, we can also match tuples. However, since tuples can have many different values, we can use the underscore (_)  as a wildcard to match anything.

let point = (0, 0)
switch point {
case (0, 0):
    print("Point is the origin")
case (_, 0):
    print("\(point.0) is on the x-axis")
case (0, _):
    print("\(point.1) is on the y-axis")
case (0...1, 0...1):
    print("(\(point.0), \(point.1)) is inside the unit box")
default:
    print("Point is outside the unit box")
}

In the above code, we’re using the underscore to tell Swift that this part of the tuple can take on any value. In the final case, we’re using the range operators inside the tuple. We can refer to the coordinates using the dot notation and it’s position in the tuple, but we can use value binding in a switch case to assign the underscore to an actual value we can use.

let point = (0, 0)
switch point {
case (0, 0):
    print("Point is the origin")
case (let x, 0):
    print("\(x) is on the x-axis")
case (0, let y):
    print("\(y) is on the y-axis")
case (0...1, 0...1):
    print("(\(point.0), \(point.1)) is inside the unit box")
case let(x, y):
    print("(\(x), \(y)) is outside the unit box")
}

In the above code, we’re using value binding to assign values to those underscores so we can use them in the case statement. To make switch statements even more powerful, we can use a where clause to add additional constraints.

let point = (0, 0)
switch point {
case let (x, y) where x == y:
    print("(\(x),\(y)) is on the line y = x")
case let (x, y):
    print("(\(x),\(y)) is some point in 2D space")
}

In the above code snippet, we’re adding the constraint that x must be equal to y in order to execute the code in this case.

To recap this subsection, we learned about the elementary decision structures we can use to execute code conditionally. If and else-if will execute code only if a particular condition is met. The same is true for the switch case, except switch case is more powerful with its value bindings and and where clause.

Branching Statements

Continue

This branching statement will skip over the current iteration of the loop. Any code after the statement will be skipped for this iteration. The loop will “continue” at the next iteration.

for index in 0...100 {
    if index % 3 == 0 {
        continue
    }
    print("\(index)")
}

The above code will skip over all numbers that are divisible by 3.

Break

The break statement, when used in a loop, will terminate the closest loop to it. If we had two nested loops and the break statement was in the innermost one, then that would be the loop that would terminate, but the outermost loop would still run. In a switch statement, we can use it to disregard a particular case.

var num:UInt = 3
switch num {
case 1:
    print("One")
case 2:
    print("Two")
default:
    break
}
print("Outside switch")

In the above code, we don’t want anything to happen in the default case, so let’s break out of the switch case. Leaving the default case blank would be a syntax error, so we use the break statement.

Fallthrough

Remember that only one case in a switch statement is executed. Suppose we wanted to fall through to the next case, we can do just this by using the fallthrough statement as the final statement in the case we’re initiating the falling through.

var num:UInt = 1
switch num {
case 1:
    print("One")
    fallthrough
case 2:
    print("Two")
default:
    break
}
print("Outside switch")

In the above example, case 1 will execute and so will case 2 since we have the fallthrough statement at the end of the first case.

In this section, we’ve covered all of the different looping constructs and decision control statements. We also learned how we can manipulate program control flow using the branching statements.