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

Source: https://devdocs10.com/articles/swift/swift-foreach-vs-forin  
By Murat Bo · Updated 2026-06-05 · DevDocs10 (https://devdocs10.com)

Swift gives you two common ways to iterate over collections: the `for-in` loop and the `forEach` method. They can look similar at first, but they behave differently in important ways, especially when you need control flow like `break`, `continue`, or early exit. In this article, you will learn how each option works, when to choose one over the other, and which mistakes to avoid in real Swift code.

> **Quick answer:** Use `for-in` for most loops, especially when you need `break`, `continue`, or clearer control flow. Use `forEach` when you want to apply the same action to every element and do not need to stop or skip the loop in the usual way.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic Swift syntax, arrays and dictionaries, and how functions and closures work at a simple level.

## 1. What Are the Options?

The comparison matters because both `for-in` and `forEach` let you process items in a collection, but they are not interchangeable in every situation.

- `for-in` is a language loop construct.
- `forEach` is a method on sequences and collections that takes a closure.
- `for-in` gives you normal loop control such as `break` and `continue`.
- `forEach` is often concise, but it changes how control flow works.
- Both can read elements one by one, but readability and behavior differ.

In practice, many Swift developers prefer `for-in` as the default choice because it is easier to understand and more flexible. `forEach` is useful when you want to clearly express, “run this operation for every element.”

## 2. Side-by-Side Comparison

| Feature | `for-in` | `forEach` |
| --- | --- | --- |
| Type | Language loop syntax | Method that takes a closure |
| Works with sequences | Yes | Yes |
| Supports `break` | Yes | No |
| Supports `continue` | Yes | No direct support |
| Early exit from surrounding function | Yes, with `return` | No, `return` exits only the closure call |
| Closure syntax required | No | Yes |
| Readability for simple looping | Usually very clear | Can be concise |
| Best when control flow matters | Excellent | Poor fit |

## 3. Key Differences Explained

### Loop construct vs method call

`for-in` is built into the Swift language. It reads naturally and is designed specifically for iteration.

`forEach` is a method. That means you call it on a sequence and pass a closure that runs once for each element.

### Control flow behavior

This is the most important difference. In a `for-in` loop, you can stop the loop with `break`, skip the current iteration with `continue`, or leave the surrounding function with `return`.

Inside `forEach`, those usual loop controls do not work the same way because you are inside a closure, not a traditional loop body.

### Readability and intent

`for-in` often communicates “I am looping and may make decisions while looping.”

`forEach` often communicates “apply this action to every element.” That can be nice when the action is short and there is no branching logic.

### Performance expectations

In normal app code, the performance difference is usually not the deciding factor. You should choose based on clarity and control flow needs first. Swift can optimize both forms well in many cases, but `for-in` is generally the safer readability choice.

## 4. When to Use Each

Choose based on what the loop needs to do, not just on which syntax looks shorter.

### Use `for-in` when

- You need `break` to stop early.
- You need `continue` to skip some elements.
- You want clear, familiar loop control.
- You are writing logic with conditions, nested loops, or early returns.
- You want the most beginner-friendly and maintainable option.

### Use `forEach` when

- You want to perform the same simple action for every element.
- You do not need to stop early.
- You are already working in a closure-heavy style.
- The body is short and readable.

> A good rule of thumb is: if you are even wondering whether you might need `break` or `continue`, start with `for-in`.

## 5. Code Examples

### Example 1: Basic array iteration with `for-in`

This example uses a normal loop to print each name in an array.

```swift
let names = ["Ava", "Noah", "Mia"]

for name in names {
    print(name)
}
```

Output (tested with Swift 6.4):

```text
Ava
Noah
Mia
```

This is the most direct and readable form for basic iteration.

### Example 2: Basic array iteration with `forEach`

This version does the same job, but uses a closure passed to `forEach`.

```swift
let names = ["Ava", "Noah", "Mia"]

names.forEach { name in
    print(name)
}
```

Output (tested with Swift 6.4):

```text
Ava
Noah
Mia
```

This is concise and works well when the loop body is small and simple.

### Example 3: Stopping early with `for-in`

If you need to stop when you find a match, `for-in` is the right tool.

```swift
let scores = [72, 88, 95, 67]

for score in scores {
    if score >= 90 {
        print("Found an A-grade score: \(score)")
        break
    }
}
```

Output (tested with Swift 6.4):

```text
Found an A-grade score: 95
```

As soon as Swift finds the first score that matches, the loop stops.

### Example 4: Skipping values with `for-in`

When you want to ignore some elements, `continue` keeps the loop readable.

```swift
let numbers = [1, 2, 3, 4, 5]

for number in numbers {
    if number % 2 == 0 {
        continue
    }

    print(number)
}
```

Output (tested with Swift 6.4):

```text
1
3
5
```

This prints only odd numbers because even numbers are skipped.

### Example 5: Simulating a skip in `forEach`

You cannot use `continue` directly in `forEach`, but you can return from the closure for that element.

```swift
let numbers = [1, 2, 3, 4, 5]

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

    print(number)
}
```

Output (tested with Swift 6.4):

```text
1
3
5
```

This skips the current closure body for even numbers, but it does not stop the full iteration.

### Example 6: Iterating with index and value

Both styles can work with `enumerated()` when you need positions.

```swift
let tasks = ["Plan", "Build", "Test"]

for (index, task) in tasks.enumerated() {
    print("\(index): \(task)")
}
```

Output (tested with Swift 6.4):

```text
0: Plan
1: Build
2: Test
```

This is a common and readable pattern when you need both the index and the element.

```swift
let tasks = ["Plan", "Build", "Test"]

tasks.enumerated().forEach { index, task in
    print("\(index): \(task)")
}
```

Output (tested with Swift 6.4):

```text
0: Plan
1: Build
2: Test
```

This also works, though some developers find the `for-in` version easier to scan.

## 6. Common Mistakes

### Mistake 1: Trying to use `break` inside `forEach`

A very common beginner mistake is assuming that `forEach` behaves exactly like a normal loop.

**Problem:** `break` is only valid in a loop or switch statement. A `forEach` body is a closure, so this code does not compile.

```swift
let scores = [72, 88, 95]

scores.forEach { score in
    if score >= 90 {
        break
    }
}
```

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

```swift
let scores = [72, 88, 95]

for score in scores {
    if score >= 90 {
        print("Stopping at \(score)")
        break
    }
}
```

Output (tested with Swift 6.4):

```text
Stopping at 95
```

The corrected version works because `for-in` supports normal loop control statements.

### Mistake 2: Expecting `return` in `forEach` to exit the surrounding function

Inside `forEach`, `return` returns from the current closure call, not from the outer function in the same way a loop body might suggest.

**Problem:** This code looks like it should stop the whole search as soon as it finds a negative value, but it only exits that one closure execution and the iteration continues.

```swift
func containsNegative(in numbers: [Int]) -> Bool {
    var found = false

    numbers.forEach { number in
        if number < 0 {
            found = true
            return
        }
    }

    return found
}
```

**Fix:** Use `for-in` if you want to return immediately from the surrounding function.

```swift
func containsNegative(in numbers: [Int]) -> Bool {
    for number in numbers {
        if number < 0 {
            return true
        }
    }

    return false
}
```

The corrected version works because the `return` belongs to the function itself, not just to a closure call.

### Mistake 3: Choosing `forEach` for complex logic

`forEach` can become harder to read when you add multiple conditions, nested checks, or state changes.

**Problem:** This style hides loop intent inside a closure and makes the iteration harder to maintain, especially when later changes need skipping or early exit.

```swift
let orders = [120, 0, 85, -1]
var validTotal = 0

orders.forEach { order in
    if order < 0 {
        print("Invalid order found")
        return
    }

    if order == 0 {
        return
    }

    validTotal += order
}
```

Output (tested with Swift 6.4):

```text
Invalid order found
```

**Fix:** Rewrite complex iteration as a `for-in` loop so the control flow is explicit.

```swift
let orders = [120, 0, 85, -1]
var validTotal = 0

for order in orders {
    if order < 0 {
        print("Invalid order found")
        continue
    }

    if order == 0 {
        continue
    }

    validTotal += order
}
```

Output (tested with Swift 6.4):

```text
Invalid order found
```

The corrected version works better because the loop rules are visible and easy to extend.

## 7. Tradeoffs and Edge Cases

- `forEach` always visits every element unless the program throws an error or stops for another reason outside normal loop control.
- `return` inside `forEach` acts more like skipping the current iteration than ending the whole loop.
- If you need to search for a matching element, methods like `contains(where:)`, `first(where:)`, or `allSatisfy(_:)` may express your intent better than either `for-in` or `forEach`.
- When mutating an external variable inside `forEach`, the code can become less functional and less clear than expected.
- For dictionaries, both forms work, but remember that dictionary iteration order should not be relied on unless your logic explicitly handles ordering.

> If your goal is not “loop through everything,” consider whether a more specific sequence method describes the job better than either of these iteration styles.

## 8. Decision Guide

- If you need `break`, use `for-in`.
- If you need `continue`, use `for-in`.
- If you need to return early from the surrounding function, use `for-in`.
- If you just want to perform one short action for every element, `forEach` is fine.
- If the loop body is growing in complexity, switch to `for-in`.
- If you are teaching, learning, or prioritizing readability, start with `for-in`.

## 9. Key Points

- `for-in` is a language loop, while `forEach` is a method that takes a closure.
- `for-in` supports `break`, `continue`, and straightforward early returns.
- `forEach` is best for short, simple actions applied to every element.
- `return` inside `forEach` does not behave like `break`.
- For most real-world loops, `for-in` is the clearer default choice.

## 10. Final Summary

`forEach` and `for-in` both iterate over Swift collections, but they solve slightly different problems. `forEach` is compact and expressive when you want to run one simple action for every element. `for-in` is more flexible and usually easier to read when your loop needs conditions, skipping, stopping, or early return behavior.

The safest habit for most Swift developers is to default to `for-in` and use `forEach` only when its closure-based style genuinely makes the code clearer. As a next step, you may want to compare `forEach` with other higher-order functions like `map`, `filter`, and `compactMap` so you can choose the right tool for each iteration task.
