Swift forEach vs for-in: Differences, Use Cases, and Errors

Swift gives you two common ways to iterate over collections: the for-in loop and the forEach method. They can look similar in simple examples, but they behave differently in important ways, especially around control flow, readability, and how you structure your code. This article explains exactly how each one works, when to use each option, and the mistakes that often confuse beginners.

Quick answer: Use for-in when you need full loop control, especially break, continue, or early exit from the loop body. Use forEach when you want to apply the same action to every element and do not need traditional loop control.

Difficulty: Beginner

Helpful to know first: You will understand this better if you already know basic Swift syntax, arrays and dictionaries, and how closures work at a simple level.

1. What Are the Options?

This comparison matters because both for-in and forEach let you go through items in a collection, but they are not interchangeable in every situation.

In other words, for-in is a loop construct, while forEach is a function-style operation.

2. Side-by-Side Comparison

Featurefor-inforEach
TypeLanguage loop syntaxMethod on sequences/collections
Basic styleImperative loopClosure-based iteration
Can use breakYesNo
Can use continueYesNo
Can use returnReturns from surrounding function if written therereturn exits only the current closure call
Readability for simple actionsGoodOften concise
Readability for complex conditionsUsually betterOften worse
Best use caseGeneral-purpose iterationApply one action to all elements

3. Key Differences Explained

Loop syntax vs method call

for-in is part of the Swift language. It is designed specifically for iteration and integrates naturally with control-flow keywords.

forEach is a method that accepts a closure. That means the body you write is closure code, not a traditional loop body. This difference explains many of the surprising behaviors people run into.

Control flow behavior

The biggest practical difference is control flow.

Readability

If your code simply prints values, logs them, or applies one short operation to each element, forEach can be clean and expressive.

If your code needs branching, skipping, searching, nested conditions, or early termination, for-in is usually easier to read and maintain.

Intent

forEach communicates, “run this action for each element.”

for-in communicates, “I am looping, and I may use loop logic.”

That makes for-in the safer default when you are not sure which one to choose.

4. When to Use Each

Here are practical rules you can rely on.

Use for-in when:

Use forEach when:

A good rule for beginners is simple: start with for-in. Switch to forEach only when it clearly improves readability.

5. Code Examples

Example 1: Printing every value

Both approaches work well when you want to process every element with the same simple action.

let scores = [85, 90, 78]

Using for-in:

for score in scores {
print(score)
}

Using forEach:

scores.forEach { score in
print(score)
}

These examples produce the same output. In a simple case like this, choosing between them is mostly a style decision.

Example 2: Skipping values

This is where the difference becomes important. Suppose you want to print only even numbers.

With for-in, continue makes the intent obvious.

let numbers = [1, 2, 3, 4, 5]
for number in numbers {
if number % 2 != 0 {
continue
}

print(number)
}

With forEach, you cannot use continue, but you can simulate a skip by returning from the closure early.

numbers.forEach { number in
if number % 2 != 0 {
return
}

print(number)
}

This works, but many developers find the for-in version easier to understand because continue clearly says “skip this iteration.”

Example 3: Stopping early when a match is found

If you want to stop after finding the first value greater than 100, for-in is the better tool.

let values = [40, 75, 130, 160]
for value in values {
if value > 100 {
print("Found: \(value)")
break
}
}

Trying to do the same thing with forEach does not provide a true early exit.

values.forEach { value in
if value > 100 {
print("Found: \(value)")
return
}
}

This closure returns only for the current element. The method still continues with the remaining elements. That is the most important behavioral difference in everyday Swift code.

Example 4: Iterating over a dictionary

Both approaches also work with dictionaries.

let stock: [String: Int] = [
"Apples": 10,
"Bananas": 5
]

Using for-in:

for (item, count) in stock {
print("\(item): \(count)")
}

Using forEach:

stock.forEach { entry in
print("\(entry.key): \(entry.value)")
}

The for-in version often reads more naturally because tuple destructuring happens directly in the loop header.

6. Common Mistakes

Mistake 1: Expecting break to work inside forEach

A very common beginner mistake is treating forEach like a normal loop and trying to stop it with break.

Problem: break is only valid in loop or switch contexts. A forEach body is a closure, so Swift reports an error because there is no loop statement to break out of.

let numbers = [1, 2, 3, 4]

numbers.forEach { number in
if number == 3 {
break
}
print(number)
}

Fix: Use for-in when you need to stop early.

for number in [1, 2, 3, 4] {
if number == 3 {
break
}
print(number)
}

The corrected version works because for-in is a real loop that supports break.

Mistake 2: Expecting continue to work inside forEach

Another common assumption is that continue can skip the current forEach iteration.

Problem: continue only works in loop statements. Inside a closure passed to forEach, Swift cannot apply loop control in the usual way.

numbers.forEach { number in
if number % 2 != 0 {
continue
}
print(number)
}

Fix: Either switch to for-in or use return to exit the current closure call early.

numbers.forEach { number in
if number % 2 != 0 {
return
}
print(number)
}

The corrected version works because return exits just that closure execution, effectively skipping the rest of the code for the current element.

Mistake 3: Thinking return exits the entire iteration process

Developers sometimes expect return inside forEach to behave like a loop-wide early exit.

Problem: In a forEach closure, return exits only the closure call for the current item. The remaining elements are still processed, which can lead to unexpected extra output or logic running longer than intended.

let values = [10, 20, 30]

values.forEach { value in
if value == 20 {
return
}
print("Processing \(value)")
}

Fix: Use for-in with break if you need true early termination.

for value in [10, 20, 30] {
if value == 20 {
break
}
print("Processing \(value)")
}

The corrected version works because break stops the loop completely as soon as the condition is met.

7. Tradeoffs and Edge Cases

8. Decision Guide

If you want a fast practical rule, use this:

A useful mental model is this: forEach is for “do this to every item,” while for-in is for “run loop logic over items.”

9. Key Points

10. Final Summary

forEach and for-in both help you iterate through Swift collections, but they serve different styles of code. for-in is a full loop construct with direct support for control flow, which makes it the better option for most real-world looping tasks. forEach is best when your intent is simply to perform the same action for every element and you do not need to stop or skip in the traditional loop sense.

The most important distinction to remember is that forEach uses a closure, so keywords like break and continue do not behave as they do in a loop. If you keep that one rule in mind, choosing between them becomes much easier. A good next step is to compare iteration tools such as map, filter, and compactMap so you can choose the right approach for each collection task.