Loops in Swift: `for-in`, `while`, and `repeat-while` Explained


Imagine you’re Woody from Toy Story, and you need to check every toy in Andy’s room before the big move. Would you write a separate instruction for each toy? Of course not — you’d say “check the next toy” and repeat until they’re all accounted for. That’s exactly what a loop does.

In this guide, you’ll learn every looping tool Swift offers — from for-in ranges to stride, enumerated(), and zip. We won’t cover higher-order functions like map or filter — those get their own dedicated post.

What You’ll Learn

What Is a Loop?

A loop tells Swift: “repeat this block of code multiple times.” Instead of writing the same instruction over and over, you write it once and let the loop handle the repetition.

Think of the conveyor belt on the Monsters, Inc. scare floor. Each door comes down the line one at a time, and the same scaring routine happens for every door. The conveyor belt is the loop — it keeps delivering the next door until there are none left.

Swift gives you three kinds of loops:

  • for-in — loop over a known sequence (a range, array, or dictionary)
  • while — keep looping as long as a condition is true
  • repeat-while — loop at least once, then check the condition

for-in with Ranges

The simplest loop repeats code a specific number of times using a range. A range is a sequence of numbers with a defined start and end.

for number in 1...5 {
    print("Take \(number)! Action!")
}
Take 1! Action!
Take 2! Action!
Take 3! Action!
Take 4! Action!
Take 5! Action!

The 1...5 is a closed range — it includes both 1 and 5. Swift also has a half-open range that excludes the upper bound:

for frame in 0..<4 {
    print("Rendering frame \(frame)")
}
Rendering frame 0
Rendering frame 1
Rendering frame 2
Rendering frame 3

Half-open ranges (0..<4) are ideal when working with zero-based indexes. The range 0..<4 gives you indexes 0, 1, 2, and 3 — exactly four elements.

Tip: If you don’t need the loop variable, use an underscore (_) to ignore it:

for _ in 1...3 {
    print("To infinity and beyond!")
}

for-in with Collections

In Post 6: Collections, you learned about arrays, dictionaries, and sets. You can loop over all of them with for-in.

Arrays give you each element in order:

let toys = ["Woody", "Buzz", "Jessie", "Rex"]
for toy in toys {
    print("\(toy) is ready for adventure!")
}
Woody is ready for adventure!
Buzz is ready for adventure!
Jessie is ready for adventure!
Rex is ready for adventure!

Dictionaries give you each key-value pair as a tuple — a small group of values bundled together:

let movieYears = [
    "Toy Story": 1995,
    "Finding Nemo": 2003,
    "Up": 2009
]
for (title, year) in movieYears {
    print("\(title) came out in \(year)")
}

Note: Dictionaries are unordered, so the print order may differ each time you run this code.

while and repeat-while

A while loop checks its condition before each iteration. If the condition starts out false, the body never runs at all.

var balloonCount = 3
while balloonCount > 0 {
    print("\(balloonCount) balloons left!")
    balloonCount -= 1
}
print("Carl's house is floating!")
3 balloons left!
2 balloons left!
1 balloons left!
Carl's house is floating!

A repeat-while loop runs the body at least once, then checks the condition. Think of Dory in Finding Nemo — she tries a path first, then checks if it was the right one.

var attempts = 0
repeat {
    attempts += 1
    print("Attempt \(attempts) to find Nemo")
} while attempts < 3
Attempt 1 to find Nemo
Attempt 2 to find Nemo
Attempt 3 to find Nemo

Tip: Use while when the loop might not need to run at all. Use repeat-while when you always need at least one iteration.

stride, enumerated(), and zip

stride — Custom Step Sizes

Sometimes you need to count by twos, go backwards, or skip values. The stride function creates a sequence with a custom step size.

stride(from:to:by:) excludes the end value (half-open):

for i in stride(from: 0, to: 10, by: 2) {
    print(i, terminator: " ")
}
0 2 4 6 8

stride(from:through:by:) includes the end value (closed), and you can count backwards with a negative step:

for i in stride(from: 10, through: 0, by: -2) {
    print(i, terminator: " ")
}
10 8 6 4 2 0

Apple Docs: stride(from:to:by:) — Swift Standard Library

enumerated() — Index + Value

When you need both the position and the value while looping over a collection, use enumerated():

let monsters = ["Sulley", "Mike", "Boo", "Randall"]
for (index, name) in monsters.enumerated() {
    print("\(index + 1). \(name)")
}
1. Sulley
2. Mike
3. Boo
4. Randall

enumerated() returns a sequence of (offset, element) tuples. We add 1 to the index so our numbered list starts at 1 instead of 0.

zip — Pairing Two Collections

zip iterates two collections at the same time, pairing up elements by position:

let characters = ["Woody", "Buzz", "Jessie"]
let phrases = [
    "There's a snake in my boot!",
    "To infinity and beyond!",
    "Yodel-ay-hee-hoo!"
]
for (name, phrase) in zip(characters, phrases) {
    print("\(name) says: \(phrase)")
}
Woody says: There's a snake in my boot!
Buzz says: To infinity and beyond!
Jessie says: Yodel-ay-hee-hoo!

Note: If the two collections have different lengths, zip stops at the shorter one — no crash, no error.

Controlling Loop Flow

The where Clause

Add a where clause to a for-in loop to filter which elements get processed:

let scores = [85, 42, 91, 67, 55, 95]
for score in scores where score >= 80 {
    print("Passing score: \(score)")
}
Passing score: 85
Passing score: 91
Passing score: 95

break and continue

Two keywords let you alter the normal flow inside a loop:

  • continue — skip the rest of this iteration and move to the next one
  • break — exit the loop entirely
let movies = [
    "Toy Story", "Cars", "Up",
    "Finding Nemo", "Brave"
]
for movie in movies {
    if movie == "Cars" { continue }
    if movie == "Finding Nemo" { break }
    print("Watching \(movie)")
}
Watching Toy Story
Watching Up

continue skipped “Cars” and kept going. break stopped the loop at “Finding Nemo,” so neither it nor “Brave” were printed.

Labeled Statements

When you have nested loops, break and continue only affect the innermost loop. Use a label to target an outer loop:

let sections = ["A", "B"]
let seats = [1, 2, 3]

sectionLoop: for section in sections {
    for seat in seats {
        if section == "A" && seat == 2 {
            continue sectionLoop
        }
        print("Section \(section), Seat \(seat)")
    }
}
Section A, Seat 1
Section B, Seat 1
Section B, Seat 2
Section B, Seat 3

The label sectionLoop: lets continue jump to the outer loop, skipping the remaining seats in section A.

Common Mistakes

Forgetting to Update the Condition in while

// ❌ This loop never ends — count never changes
var count = 5
while count > 0 {
    print(count)
    // Forgot to decrement count!
}
// ✅ Always update the condition variable
var count = 5
while count > 0 {
    print(count)
    count -= 1
}

Warning: An infinite loop will freeze your Playground or app. Always double-check that while loops have a way to end.

Off-by-One with Ranges

// ❌ Fatal error — index 3 is out of range
let toys = ["Woody", "Buzz", "Jessie"]
for i in 0...toys.count {
    print(toys[i])
}
// ✅ Use half-open range to match array indexes
let toys = ["Woody", "Buzz", "Jessie"]
for i in 0..<toys.count {
    print(toys[i])
}

Tip: Even better — loop directly over the array (for toy in toys) whenever you don’t need the index.

What’s Next?

  • Loops repeat code: for-in for known sequences, while for condition-based repetition, repeat-while for at-least-once.
  • Use stride for custom step sizes and reverse counting.
  • enumerated() pairs indexes with values; zip pairs two collections together.
  • where filters iterations; break exits a loop; continue skips to the next iteration.

Now that you can repeat actions efficiently, it’s time to learn about values that might not exist. Head to Post 9: Optionals to discover Swift’s built-in safety net for handling missing data.