Skip to content

6. Loops

What & why

Loops repeat work: process every item in a list, retry until input is valid, count from 1 to 100. In Swift you'll mostly use for-in over a sequence; while and repeat-while handle the "until something happens" cases.

for-in over a range

for i in 1...5 {
    print(i, terminator: " ")
}
// 1 2 3 4 5

Ranges:

  • 1...5 — closed: 1, 2, 3, 4, 5
  • 0..<5 — half-open: 0, 1, 2, 3, 4 (the upper bound is excluded; ideal for indices)
  • 5... and ..<5 — one-sided (used for slicing and switch, not for looping forever/backwards)

If you don't need the counter, use _:

for _ in 1...3 {
    print("hello")
}

for-in over collections

let scores = [90, 72, 85]
for s in scores {
    print(s)
}

for ch in "hey" {
    print(ch)
}

let ages = ["Ada": 36, "Linus": 54]
for (name, age) in ages {          // order is not guaranteed
    print("\(name) is \(age)")
}

The loop constant is immutable — you can't assign to s. To modify elements in place, loop over indices:

var values = [1, 2, 3]
for i in values.indices {
    values[i] *= 10
}
print(values)    // [10, 20, 30]

Index and element: enumerated()

let fruits = ["apple", "banana", "cherry"]
for (index, fruit) in fruits.enumerated() {
    print("\(index + 1). \(fruit)")
}

Counting backwards and by steps

for i in (1...5).reversed() { print(i) }          // 5 4 3 2 1

for i in stride(from: 0, to: 10, by: 3) { print(i) }       // 0 3 6 9 (excludes 10)
for i in stride(from: 10, through: 0, by: -5) { print(i) } // 10 5 0 (includes 0)

while

Repeat while a condition holds — when you don't know the count in advance:

var n = 27
var steps = 0
while n != 1 {
    n = n.isMultiple(of: 2) ? n / 2 : 3 * n + 1
    steps += 1
}
print("Collatz steps: \(steps)")

repeat-while

Like while, but the body runs at least once. Perfect for input validation:

var age: Int?
repeat {
    print("Enter age (1-120): ", terminator: "")
    age = Int(readLine() ?? "")
} while age == nil || !(1...120).contains(age!)
print("Age: \(age!)")

(The ?? "" and ! bits are covered in Optionals. Here, age! is safe because the loop only ends once age is non-nil.)

break, continue, and labels

for i in 1...10 {
    if i.isMultiple(of: 2) { continue }    // skip the rest of this iteration
    if i > 7 { break }                     // leave the loop entirely
    print(i, terminator: " ")
}
// 1 3 5 7

break and continue affect the innermost loop. To control an outer one, give it a label:

outer: for row in 1...3 {
    for col in 1...3 {
        if row * col == 4 { break outer }   // leaves both loops
        print(row * col, terminator: " ")
    }
}

Reading input until it runs out

var total = 0
while let line = readLine() {              // returns nil at end of input
    if let value = Int(line) {
        total += value
    }
}
print("sum: \(total)")

Try it with printf '1\n2\n3\n' | swift run, or type numbers and finish with Ctrl+D (Ctrl+Z then Enter on Windows).

where filters

for n in 1...20 where n % 3 == 0 {
    print(n, terminator: " ")
}
// 3 6 9 12 15 18

Nested loops

for row in 1...3 {
    for col in 1...3 {
        print(row * col, terminator: "\t")
    }
    print()
}

Functional alternatives (a preview)

Often a loop that builds a new collection is clearer as map / filter / reduce:

let squares = (1...5).map { $0 * $0 }              // [1, 4, 9, 16, 25]
let evens = (1...10).filter { $0.isMultiple(of: 2) }   // [2, 4, 6, 8, 10]
let sum = (1...100).reduce(0, +)                   // 5050

You'll learn closures properly in Intermediate. Use whichever reads best.

Common mistakes

  • Using 1...n when n might be 0: 1...0 crashes ("Range requires lowerBound <= upperBound"). Use 0..<n, or stride.
  • Trying to modify the loop variable: it's a constant.
  • Modifying a collection while iterating over it — the loop iterates over a copy, so changes won't affect it, which may not be what you expected.
  • Infinite while loops from forgetting to update the condition.
  • Off-by-one with ... vs ..<. Prefer ..< with array.count, or better, iterate the array directly.
  • Reversed ranges: for i in 10...1 crashes. Use (1...10).reversed() or stride.

Exercises

  1. Print 1–100, but print "Fizz" for multiples of 3, "Buzz" for multiples of 5, and "FizzBuzz" for both.
  2. Read integers until end-of-input and print the count, sum, minimum, maximum, and average. Skip lines that aren't numbers.
  3. Number guessing game: pick Int.random(in: 1...100), let the user guess with "higher"/"lower" hints, and count the guesses. Handle non-numeric input.
  4. Print a right triangle of * of height N using nested loops.
  5. Print all primes below 100.
  6. Print a multiplication table (1–10) with aligned columns.
  7. Use a labeled break to find the first pair (a, b) with 1 <= a, b <= 20 and a * b == 91.

Recap

  • for-in over ranges (1...5, 0..<n), collections, strings, and dictionaries.
  • enumerated() for index + element; stride for custom steps; .reversed() to count down.
  • while for unknown counts; repeat-while runs at least once.
  • break / continue act on the innermost loop; labels reach outer loops.
  • while let line = readLine() reads until input ends.

Next: Arrays →