Articles, podcasts and news about Swift development, by John Sundell.

Picking between a for loop and forEach

Published on 04 Feb 2020

When iterating over a collection, such an Array or a Set, it may at first seem like a matter of preference and style whether to use a classic for loop, or the closure-based forEach method. While it’s definitely true that those two mechanisms are highly similar, they do differ in a few distinct ways.

One of those differences is that a for loop’s iteration is performed directly within our code’s control flow, which gives us much more precise control over those iterations. For example, we can choose to skip to the next element at any point using the continue keyword, or to break the iteration completely using break:

func recentArticles(among articles: [Article]) -> [Article] {
    var results = [Article]()
    
    for article in articles {
        guard !article.isDraft else {
            // Immediately skip to the next element.
            continue
        }
    
        results.append(article)
        
        guard results.count < 5 else {
            // Break the iteration. In this case we could've also
            // returned here directly.
            break
        }
    }
    
    return results
}

When a for loop’s iteration starts with a guard (or if) statement that’s used for filtering (like above), we can also chose to implement that logic using where-based pattern matching instead — like this:

for article in articles where !article.isDraft {
    results.append(article)
    
    guard results.count < 5 else {
        break
    }
}

The above kind of control flow-related features are a big part of what makes for loops so powerful, but if we don’t need that level of control, using a call to forEach might give us slightly simpler-looking code. For example, here we’re adding a new view for each of an array’s elements, which can be done really elegantly using forEach:

func addArticleViews(for articles: [Article]) {
    articles.forEach { article in
        let articleView = ArticleView()
        
        // Seting up our new view using the closure's article.
        ...
        
        view.addSubview(articleView)
    }
}

However, it would perhaps be even more elegant to use Swift’s first class functions capabilities — by refactoring the above method to only accept a single Article, and then directly injecting that method into a call to forEach, like this:

class ArticleGroupViewController: UIViewController {
    private let articles: [Article]
    
    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        articles.forEach(addArticleView)
    }

    private func addArticleView(for article: Article) {
        let articleView = ArticleView()
        ...
        view.addSubview(articleView)
    }
}

To sum up: Using a for loop gives us a much greater degree of control over an iteration, while using forEach enables us to take advantage of the power of closures and first class functions, even though we won’t be able to stop an iteration once it was started (apart from throwing an error, that is).