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?
for-inwith Rangesfor-inwith Collectionswhileandrepeat-whilestride,enumerated(), andzip- Controlling Loop Flow
- Common Mistakes
- What’s Next?
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 truerepeat-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
whilewhen the loop might not need to run at all. Userepeat-whilewhen 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,
zipstops 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 onebreak— 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
whileloops 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-infor known sequences,whilefor condition-based repetition,repeat-whilefor at-least-once. - Use
stridefor custom step sizes and reverse counting. enumerated()pairs indexes with values;zippairs two collections together.wherefilters iterations;breakexits a loop;continueskips 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.