Chapters

Hide chapters

Swift Apprentice: Fundamentals

First Edition · iOS 16 · Swift 5.7 · Xcode 14.2

Section III: Building Your Own Types

Section 3: 9 chapters
Show chapters Hide chapters

4. Advanced Control Flow
Written by Matt Galloway

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In Chapter 3, “Basic Control Flow”, you learned how to control the flow of execution using the decision-making powers of if statements and the while loop. In this chapter, you’ll continue to learn how to control the flow of execution. You’ll learn about another loop known as the for loop.

Loops may not sound very interesting, but they’re very common in computer programs. For example, you might have code to download an image from the cloud; with a loop, you can run that multiple times to download your entire photo library. Or, if you have a game with multiple computer-controlled characters, you might need a loop to go through each one and make sure it knows what to do next.

You’ll also learn about switch statements, which are particularly powerful in Swift. They let you inspect a value and decide what to do based on that value, and they’re incredibly powerful when used with some advanced Swift features such as pattern matching.

Countable Ranges

Before you dive into the for loop statement, you need to know about the Countable Range data types that let you represent a sequence of countable integers. Let’s look at two types of ranges.

First, there’s countable closed range, which you represent like so:

let closedRange = 0...5

The three dots (...) indicate that this range is closed, which means the range goes from 0 to 5 inclusive. That’s the numbers (0, 1, 2, 3, 4, 5).

Second, there’s countable half-open range, which you represent like so:

let halfOpenRange = 0..<5

Here, you replace the three dots with two dots and a less-than sign (..<). Half-open means the range goes from 0 up to, but not including, 5. That’s the numbers (0, 1, 2, 3, 4).

Both open and half-open ranges must always be increasing. In other words, the second number must always be greater than or equal to the first. Countable ranges are commonly used in both for loops and switch statements, which means that throughout the rest of the chapter, you’ll use ranges as well!

A Random Interlude

A common need in programming is to be able to generate random numbers. And Swift provides the functionality built into the language, which is pretty handy!

while Int.random(in: 1...6) != 6 {
  print("Not a six")
}

For Loops

In Chapter 3, “Basic Control Flow”, you looked at while loops. Now that you know about ranges, it’s time to look at another type of loop: the for loop. This is probably the most common loop you’ll see, and you’ll use it to run code a certain number of times.

for <CONSTANT> in <COUNTABLE RANGE> {
  <LOOP CODE>
}
let count = 10
var sum = 0
for i in 1...count {
  sum += i
}

sum = 1
var lastSum = 0

for _ in 0..<count {
  let temp = sum
  sum = sum + lastSum
  lastSum = temp
}
sum = 0
for i in 1...count where i % 2 == 1 {
  sum += i
}

Continue and Labeled Statements

Sometimes you’d like to skip a loop iteration for a particular case without breaking out of the loop entirely. You can do this with the continue statement, which immediately ends the current iteration of the loop and starts the next iteration.

6 7 4 9 2 4 3 1 9 2 4 5 6 4 6 7 4 7 2 9 9 34 58 81 2 7 5 2 09 84 88 59 8 7 2 16 74 53 80 77 5 2 19 93 11 79 50 48 8 6 70 46 18 90 23 09 0 2 84 27 85 01 79 08 4 1 9 5 0 8 8 1 7 7 1 6 0 2 0 1

5 4 7 8 5 6 2 3 5 4 1 6 77 73 59 94 4 5 41 06 12 06 76 61 5 0 51 93 01 85 35 50 9 1 5 4 7 0 1 1 9 0 7 7 5 8 6 1

sum = 0

for row in 0..<8 {
  if row % 2 == 0 {
    continue
  }

  for column in 0..<8 {
    sum += row * column
  }
}
5 6 0 4 9 8 0 5 6 82 3 4 12 99 53 0 8 27 59 85 66 8 7 67 97 28 53 12 7 3 7 6 2 7 0 3 6 0 8 8 8 6 8 3

sum = 0

rowLoop: for row in 0..<8 {
  columnLoop: for column in 0..<8 {
    if row == column {
      continue rowLoop
    }
    sum += row * column
  }
}

Mini-Exercises

sum = 0
for row in 0..<8 {
  if row % 2 == 0 {
    continue
  }
  for column in 0..<8 {
    sum += row * column
  }
}

Switch Statements

You can also control flow via the switch statement. It executes different code depending on the value of a variable or constant. Here’s a switch statement that acts on an integer:

let number = 10

switch number {
case 0:
  print("Zero")
default:
  print("Non-zero")
}
switch number {
case 10:
  print("It’s ten!")
default:
  break
}
let string = "Dog"

switch string {
case "Cat", "Dog":
  print("Animal is a house pet.")
default:
  print("Animal is not a house pet.")
}

Advanced switch Statements

You can also give your switch statements more than one case. In Chapter 3, “Basic Control Flow”, you saw an if statement that used multiple else clauses to convert an hour of the day to a string describing that part of the day.

let hourOfDay = 12
var timeOfDay: String

switch hourOfDay {
case 0, 1, 2, 3, 4, 5:
  timeOfDay = "Early morning"
case 6, 7, 8, 9, 10, 11:
  timeOfDay = "Morning"
case 12, 13, 14, 15, 16:
  timeOfDay = "Afternoon"
case 17, 18, 19:
  timeOfDay = "Evening"
case 20, 21, 22, 23:
  timeOfDay = "Late evening"
default:
  timeOfDay = "INVALID HOUR!"
}

print(timeOfDay)
switch hourOfDay {
case 0...5:
  timeOfDay = "Early morning"
case 6...11:
  timeOfDay = "Morning"
case 12...16:
  timeOfDay = "Afternoon"
case 17...19:
  timeOfDay = "Evening"
case 20..<24:
  timeOfDay = "Late evening"
default:
  timeOfDay = "INVALID HOUR!"
}
switch number {
case let x where x % 2 == 0:
  print("Even")
default:
  print("Odd")
}
switch number {
case _ where number % 2 == 0:
  print("Even")
default:
  print("Odd")
}

Partial Matching

Another way you can use switch statements with matching to great effect is as follows:

let coordinates = (x: 3, y: 2, z: 5)

switch coordinates {
case (0, 0, 0): // 1
  print("Origin")
case (_, 0, 0): // 2
  print("On the x-axis.")
case (0, _, 0): // 3
  print("On the y-axis.")
case (0, 0, _): // 4
  print("On the z-axis.")
default:        // 5
  print("Somewhere in space")
}
switch coordinates {
case (0, 0, 0):
  print("Origin")
case (let x, 0, 0):
  print("On the x-axis at x = \(x)")
case (0, let y, 0):
  print("On the y-axis at y = \(y)")
case (0, 0, let z):
  print("On the z-axis at z = \(z)")
case let (x, y, z):
  print("Somewhere in space at x = \(x), y = \(y), z = \(z)")
}
switch coordinates {
case let (x, y, _) where y == x:
  print("Along the y = x line.")
case let (x, y, _) where y == x * x:
  print("Along the y = x^2 line.")
default:
  break
}

Mini-Exercises

Challenges

Before moving on, here are some challenges to test your knowledge of advanced 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: How Many Times

In the following for loop, what will be the value of sum, and how many iterations will happen?

var sum = 0
for i in 0...5 {
  sum += i
}

Challenge 2: Count the Letter

In the while loop below, how many instances of “a” will there be in aLotOfAs? Hint: aLotOfAs.count tells you how many characters are in the string aLotOfAs.

var aLotOfAs = ""
while aLotOfAs.count < 10 {
  aLotOfAs += "a"
}

Challenge 3: What Will Print

Consider the following switch statement:

switch coordinates {
case let (x, y, z) where x == y && y == z:
  print("x = y = z")
case (_, _, 0):
  print("On the x/y plane")
case (_, 0, _):
  print("On the x/z plane")
case (0, _, _):
  print("On the y/z plane")
default:
  print("Nothing special")
}
let coordinates = (1, 5, 0)
let coordinates = (2, 2, 2)
let coordinates = (3, 0, 1)
let coordinates = (3, 2, 5)
let coordinates = (0, 2, 4)

Challenge 4: Closed Range Size

A closed range can never be empty. Why?

Challenge 5: The Final Countdown

Print a countdown from 10 to 0. (Note: do not use the reversed() method, which will be introduced later.)

Challenge 6: Print a Sequence

Print 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0. (Note: do not use the stride(from:by:to:) function, which will be introduced later.)

Key Points

  • You can use countable ranges to create a sequence of integers, incrementing to move from one value to the next.
  • Closed ranges include both the start and end values.
  • Half-open ranges include the start value and stop one before the end value.
  • For loops allow you to iterate over a range.
  • The continue statement lets you finish the current loop iteration and begin the next iteration.
  • Labeled statements let you use break and continue on an outer loop.
  • You use switch statements to decide which code to run depending on the value of a variable or constant.
  • The power of a switch statement comes from leveraging pattern matching to compare values using complex rules.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now