Looping over a collection is a common task in most programming languages. In this post, you learn about Swift's for loop and you learn that a for loop isn't always the best solution to the problem. Swift has several other constructs that are at times a better fit. Let's start with the for loop.

Swift's For Loop

Fire up Xcode and create a playground if you want to follow along. Choose the Blank template from the iOS > Playground section.

Swift's For Loop

Clean the contents of the playground and add an import statement for the Foundation framework.

import Foundation

We first create an array of integers. Swift's for loop makes it trivial to iterate through the array. The syntax should look familiar if you have some experience programming in other languages.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

for number in numbers {
    print(number)
}

In Swift, we typically refer to a for loop as a for-in loop. The for keyword is followed by a constant whose name you choose. The in keyword is followed by the collection you loop over, the array of integers in this example. The body of the for-in loop is executed for every element of the collection. In this example, we print the element, an integer, to the console. The body of the for-in loop is executed seven times because the array contains seven elements.

0
1
1
2
3
5
8

Swift's Standard Library also defines the forEach(_:) method, which I prefer over the for-in loop. We simply invoke the forEach(_:) method on the numbers array. The method accepts a closure as its only argument. The idea is similar to a for-in loop in that the closure accepts one argument, an element of the collection.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

for number in numbers {
    print(number)
}

numbers.forEach { number in
    print(number)
}

Obtaining the Index of an Element

How do you access the index of the element in the collection in the body of the for-in loop? You can access the element's index by creating an enumerated sequence. Let me explain what that means. We revisit the first example and invoke enumerated() on the numbers array. The enumerated() method returns an enumerated sequence. Each element of the enumerated sequence is a tuple that contains the index of the element and the element itself. Take a look at the example and the output it generates.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

for (index, number) in numbers.enumerated() {
    print("\(number) at position \(index)")
}
0 at position 0
1 at position 1
1 at position 2
2 at position 3
3 at position 4
5 at position 5
8 at position 6

You can also invoke the forEach(_:) method on an enumerated sequence. The idea is similar.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

for (index, number) in numbers.enumerated() {
    print("\(number) at position \(index)")
}

numbers.enumerated().forEach { index, number in
    print("\(number) at position \(index)")
}

Transforming Collections

You can use a for-in loop or the forEach(_:) method if you need to transform a collection. In this example, we remove odd numbers from the numbers array. It is a bit clunky because we first declare a variable, evenNumbers, to hold the even numbers. In the body of the for-in loop, we append the number to the array of even number if it is an even number.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

var evenNumbers: [Int] = []

for number in numbers {
    if number % 2 == 0 {
        evenNumbers.append(number)
    }
}

This works fine, but it isn't the most elegant solution. The filter(_:) method is much simpler and easier to read. Like the forEach(_:) method, the filter(_:) method accepts a closure as its only argument and it returns a collection of the same type.

The closure we pass to the filter(_:) method accepts an element of the collection and it returns a boolean. If the closure returns true, then the element is included in the returned collection. If the closure returns false, then the element is not included in the returned collection. Let's rewrite the previous example using the filter(_:) method. I hope you agree that the result is easier to read and understand. Right?

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

let evenNumbers = numbers.filter { number in
    number % 2 == 0
}

We can further simplify the implementation by using shorthand argument syntax.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

let evenNumbers = numbers.filter { $0 % 2 == 0 }

Swift's Standard Library defines a range of convenient methods to operate on or transform collections. We can use the reversed() method to reverse the numbers array before we filter out odd numbers.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

let evenNumbers = numbers
    .reversed()
    .filter { $0 % 2 == 0 }

We can also filter the array of numbers first and then reverse the array of even numbers.

import Foundation

let numbers = [0, 1, 1, 2, 3, 5, 8]

let evenNumbers = numbers
    .filter { $0 % 2 == 0 }
    .reversed()

Choose the Right Solution

I rarely use Swift's for-in loop. It's a fine solution, but it's a coarse solution. The Standard Library defines dozens of APIs that are often a better fit. Check out How to Map an Array to a Dictionary to learn about the versatility of the map(_:) method. I use map(_:) and compactMap(_:) much more often than Swift's for-in loop. The choice is up to you, though. If you prefer Swift's for-in loop, then that is fine too.