Picking between a for loop and forEach
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).