# Swift forEach: Prefer forEach Only for Side Effects

Source: https://devdocs10.com/articles/swift/swift-foreach-side-effects  
By Murat Bo · Published 2026-06-21 · DevDocs10 (https://devdocs10.com)

Swift's `forEach` method is a convenient way to run the same closure for every element in a collection. It is useful when you want to perform an action for its side effects, but it is usually the wrong choice when you need control flow, early exit, or a transformed result.

> **Quick answer:** Use `forEach` when you want to do something for each element, such as printing, logging, or updating external state. Prefer a `for-in` loop when you need `break`, `continue`, or clearer iteration.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic Swift collections, closures, and the difference between returning a value and performing an action.

## 1. What Is forEach?

`forEach` is a method on Swift sequences and collections that calls a closure once for every element. The closure receives each element in order, and the method itself returns `Void`.

- It iterates through items one at a time.
- It is designed for actions, not results.
- It does not support loop control statements like `break` or `continue`.
- It is often less flexible than a `for-in` loop.

That last point is why the rule of thumb matters: if you are using `forEach` to build a new collection or to search for a value, there is usually a better tool.

## 2. Why forEach Matters

`forEach` can make code read like a list of actions applied to each element. That can be nice when the intent is simple, such as printing, writing to a file, or notifying another object.

However, many beginners reach for it when they really want a loop with control flow. In those cases, `forEach` can make code harder to understand because it hides the fact that you are not returning a result and you cannot stop early in the usual way.

Prefering `forEach` only for side effects helps keep code readable and prevents subtle bugs where a developer expects loop behavior that `forEach` does not provide.

## 3. Basic Syntax

The minimal form of `forEach` uses a closure that receives each element:

```swift
numbers.forEach { number in
    print(number)
}
```

This means: take every value in `numbers`, bind it to `number`, and run the closure body.

### How the closure works

The closure you pass to `forEach` can use the element, update outside state, or call other functions. Because `forEach` ignores any return value from the closure, it is not a good fit for producing a new array.

### What the method does not do

`forEach` does not change the collection by itself. It also does not let you leave the loop early with `break`. If you need those behaviors, use `for-in` instead.

## 4. Step-by-Step Examples

### Example 1: Printing values

A straightforward side effect is printing each element. This is one of the clearest uses of `forEach`.

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

names.forEach { name in
    print("Hello, \(name)")
}
```

Output (tested with Swift 6.4):

```text
Hello, Ava
Hello, Noah
Hello, Mia
```

This code performs an action for each name. It does not create a new array or change the original one.

### Example 2: Logging items conditionally

You can use `forEach` when the main purpose is to send each item to a logger or debugging output.

```swift
let tasks = ["download", "parse", "save"]

tasks.forEach { task in
    if task.hasPrefix("s") {
        print("Important task: \(task)")
    }
}
```

Output (tested with Swift 6.4):

```text
Important task: save
```

The closure still runs for every item, but only some items trigger the side effect.

### Example 3: Updating external state

Sometimes a closure updates a value outside itself, such as a running total. This is a side effect, so `forEach` can be reasonable.

```swift
let prices = [12, 8, 5]
var total = 0

prices.forEach { price in
    total += price
}

print(total)
```

Output (tested with Swift 6.4):

```text
25
```

This works because the goal is to update `total`, not to create a transformed collection.

### Example 4: Comparing with a for-in loop

Here is the same iteration written as a `for-in` loop. Notice how the loop gives you more control.

```swift
for price in prices {
    if price < 10 {
        continue
    }
    print(price)
}
```

If you need to skip values or stop early, the `for-in` loop is clearer and more capable.

## 5. Practical Use Cases

- Printing debug information for every element in a collection.
- Sending each item to a logger, analytics event, or notification service.
- Applying external updates such as accumulating totals or building a report string.
- Triggering one-off side effects where the result is stored elsewhere.
- Running cleanup or bookkeeping logic over a small collection.

These are all cases where the important outcome is the action, not a returned collection.

## 6. Common Mistakes

### Mistake 1: Using forEach to build a new array

Many beginners try to use `forEach` when they want to transform values. That often leads to extra mutable state and less readable code.

**Problem:** The code below uses `forEach` to append transformed values into another array. It works, but it is harder to read than a transformation method designed for that job.

```swift
let numbers = [1, 2, 3]
var doubles: [Int] = []

numbers.forEach { number in
    doubles.append(number * 2)
}
```

**Fix:** Use `map` when the goal is to create a new array from each element.

```swift
let numbers = [1, 2, 3]
let doubles = numbers.map { number in
    number * 2
}
```

The corrected version is better because it expresses transformation directly instead of relying on side effects.

### Mistake 2: Expecting break to stop forEach

`forEach` is not a full loop statement, so it does not support `break` in the way a `for-in` loop does.

**Problem:** Trying to use `break` inside `forEach` causes a compile-time error because the closure is not a loop body.

```swift
let scores = [10, 22, 31]

scores.forEach { score in
    if score > 20 {
        break
    }
    print(score)
}
```

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

```swift
let scores = [10, 22, 31]

for score in scores {
    if score > 20 {
        break
    }
    print(score)
}
```

Output (tested with Swift 6.4):

```text
10
```

The `for-in` version works because `break` is part of the language's loop syntax.

### Mistake 3: Using return to mean “return from the loop”

Inside `forEach`, `return` exits only the closure for the current element. It does not stop the whole iteration.

**Problem:** This code looks like it should stop when it finds an even number, but it keeps processing later elements because `return` only leaves the closure body for that one item.

```swift
let values = [1, 3, 4, 6]

values.forEach { value in
    if value % 2 == 0 {
        return
    }
    print(value)
}
```

Output (tested with Swift 6.4):

```text
1
3
```

**Fix:** Use a loop with `break`, or use a searching method if your real goal is to find an element.

```swift
for value in values {
    if value % 2 == 0 {
        break
    }
    print(value)
}
```

The corrected version matches the intended control flow, so later elements are not processed once the condition is met.

## 7. Best Practices

### Practice 1: Use forEach when the result is intentionally discarded

If the output of the iteration is not needed, `forEach` can communicate that the purpose is the side effect itself.

```swift
let ids = ["A12", "B45", "C98"]

ids.forEach { id in
    print("Processing \(id)")
}
```

Output (tested with Swift 6.4):

```text
Processing A12
Processing B45
Processing C98
```

This is a good fit because the code is about doing something to each item, not producing a transformed value.

### Practice 2: Prefer for-in when you need control flow

When the iteration needs to stop, skip values, or nest more complex logic, a `for-in` loop is usually clearer.

```swift
for id in ids {
    if id == "B45" {
        break
    }
    print(id)
}
```

This version is better because it makes the control flow explicit.

### Practice 3: Avoid side effects when a return value would be better

If you are computing a new collection, count, or derived value, use the collection method that returns that result instead of writing to external mutable variables.

```swift
let amounts = [5, 10, 15]

let filtered = amounts.filter { amount in
    amount > 8
}
```

This keeps the data flow easier to test and reason about than mutating an external array inside `forEach`.

## 8. Limitations and Edge Cases

- `forEach` cannot use `break` or `continue` to control iteration.
- `return` inside the closure returns from that closure call only, not from the containing function.
- It is usually a poor choice when you need to build a new array or search for a matching value.
- On lazy sequences, `forEach` still evaluates items one by one, but it does not change the fact that the result is `Void`.
- Using `forEach` for large or complex workflows can hide the actual intent of the code.

These limitations are not bugs; they are part of the method's design. The method is intentionally simple.

## 9. Practical Mini Project

Let's build a tiny command-line report that prints each product name and calculates the total inventory value as a side effect.

```swift
struct Product {
    let name: String
    let price: Int
    let quantity: Int
}

let inventory = [
    Product(name: "Notebook", price: 3, quantity: 10),
    Product(name: "Pen", price: 2, quantity: 25),
    Product(name: "Folder", price: 4, quantity: 8)
]

var totalValue = 0

inventory.forEach { product in
    let lineValue = product.price * product.quantity
    totalValue += lineValue
    print("\(product.name): $\(lineValue)")
}

print("Total inventory value: $\(totalValue)")
```

Output (tested with Swift 6.4):

```text
Notebook: $30
Pen: $50
Folder: $32
Total inventory value: $112
```

This example is appropriate for `forEach` because the side effects are the real output: printing each line and updating a total.

## 10. Key Points

- `forEach` is best when the purpose is an action, not a returned value.
- It is a method on sequences and collections that runs a closure for each element.
- It does not support `break` or `continue`.
- Use `map` for transformations and `for-in` for flexible loops.
- If your code depends on side effects, make that intent obvious.

## 11. Practice Exercise

- Create an array of five city names.
- Use `forEach` to print each city in uppercase.
- Keep a separate counter for how many city names contain the letter `a`.
- Do not build a new array.

**Expected output:** Five uppercase city names printed, followed by the count of cities containing `a`.

**Hint:** Use `print` and a mutable counter, but do not try to return values from `forEach`.

```swift
let cities = ["Paris", "Tokyo", "Lima", "Oslo", "Cairo"]
var countWithA = 0

cities.forEach { city in
    let uppercased = city.uppercased()
    print(uppercased)

    if city.lowercased().contains("a") {
        countWithA += 1
    }
}

print("Cities containing a: \(countWithA)")
```

Output (tested with Swift 6.4):

```text
PARIS
TOKYO
LIMA
OSLO
CAIRO
Cities containing a: 3
```

## 12. Final Summary

`forEach` is a clear and useful tool when you want to perform an action for each element in a collection. Its strength is simplicity: it reads well when the code is doing side effects such as printing, logging, or updating external state.

Its weakness is control flow. If you need to stop early, skip values, or produce a transformed result, a `for-in` loop, `map`, or another collection method is usually a better fit. Keeping that distinction in mind will make your Swift code easier to read and less error-prone.

If you want to continue learning, compare `forEach` with `map`, `filter`, and `for-in` next, since those are the methods and statements most often confused with it.
