for-in
loops in Swift tutorialIn layman’s terms, when something runs in a loop, it repeats the same things again and again. For example, a loop would be iterating through the number of blog posts and displaying them on the main page.
There are different types of loops for control flow in Swift. These are for-in
, forEach
, while
, and repeat-while
loops. In this article, we’ll go through a basic overview of for-in
loops in Swift. Then, we’ll demonstrate how to work with them using examples and use cases with different data types.
We’ll focus on the following:
for-in
loopsTo follow along, you should have basic knowledge of the Swift language.
for-in
loopsThe syntax starts with the word for
, followed by the particular element in a loop that is created as a constant. We follow it by the word in
and, finally, the sequence you want to loop over:
for element in elements { // do something with the element }
For example, we have a list of stocks, each including its price, at a particular date:
struct Stock { var name: String var price: Double var date = Date() }
We want to loop over the array and print the data for each stock. The syntax for the loop will look like this:
// MARK: - EXAMPLE func printDetails(for stocks: [Stock]) { for stock in stocks { print(stock.name) print(stock.price) print(stock.date) } } // MARK: - USAGE let stocks = [Stock(name: "Banana", price: 125), Stock(name: "TapeBook", price: 320), Stock(name: "Ramalon", price: 3200)] printDetails(for: stocks) // MARK: - OUTPUT Banana 125.0 2021-05-21 22:40:42 +0000 TapeBook 320.0 2021-05-21 22:40:42 +0000 Ramalon 3200.0 2021-05-21 22:40:42 +0000
With knowledge of the basic syntax, let’s move on to looping the fundamental data structure: Array
!
From the official Swift documentation, “An array stores values of the same type in an ordered list. The same value can appear in an array multiple times at different positions.”
We use for-in
loops to iterate over the stored values and then access each value in the array.
Assume an app where we’re tracking a user jogging. At every location, we want to track their speed. Thus, in the app, we receive an array of locations:
let locations: [CLLocation] = []
We loop through the array, and for each location, we print the speed at that particular location:
for location in locations { print("The speed at location (\(location.coordinate.latitude), \(location.coordinate.longitude) is \(location.speed)") }
Taking another illustration, we create a two-dimensional 10Ă—10 array and print the value at each point:
var board: [[Int]] = Array(repeating: Array(repeating: 0, count: 10), count: 10) for row in board { for number in row { // prints 0, hundred times print(number) } }
where
clauseThere are cases where we want to restrict the sequence only to elements that match a particular condition. In this scenario, we use the where
keyword.
In a to-do app, we need the subset of completed goals out of all goals. Assume a model like this:
struct Goal: Identifiable, Hashable { var id = UUID() var name: String = "Goal Name" var date = Date() var goalCompleted: Bool = false }
And our app has an array for Goal
. We want to loop through the array and access only those goals that are completed:
// MARK: - EXAMPLE func getCompletedGoals(for goals: [Goal]) { for goal in goals where goal.goalCompleted == true { /// Access to completed goals only. print(goal) } } // MARK: - USAGE let goals = [Goal(name: "Learn basic syntax of for-in loops", goalCompleted: true), Goal(name: "Read about for-in loops and dictionaries"), Goal(name: "Read about for-in loops and enums")] getCompletedGoals(for: goals) // MARK: - OUTPUT Goal(id: B7B148D6-853B-486A-8407-CD03A904B348, name: "Learn basic syntax of for-in loops", date: 2021-05-21 22:50:38 +0000, goalCompleted: true)
enumerated()
To access each index of the element simultaneously, we can use the instance method enumerated()
. It returns a sequence of pairs that contain the index as well as the value of the element. Taking the previous example, if we want to list the index of the location in the array, we can write this:
for (index, location) in locations.enumerated() { print("The speed at location (\(location.coordinate.latitude), \(location.coordinate.longitude) is \(location.speed)") print("The index for this location is \(index)") }
indices
If we only want the index of the element in the array, we can use indices
. This represents the valid indices in an array in ascending order. It loops from 0 to the last element in the array, i.e., array.count
:
for index in array.indices { // Access the index }
Using the two-dimensional array we created earlier, we iterate through each point and assign it a random integer value:
// MARK: - EXAMPLE func updateValues(of board: inout [[Int]]) { for rowIndex in board.indices { for columnIndex in board[0].indices { board\[rowIndex\][columnIndex] = Int.random(in: 0..<10) } print(board[rowIndex]) } } // MARK: - USAGE var board: [[Int]] = Array(repeating: Array(repeating: 0, count: 10), count: 10) updateValues(of: &board) // MARK: - OUTPUT [9, 4, 1, 7, 5, 2, 6, 4, 7, 4] [1, 0, 1, 0, 5, 4, 5, 6, 7, 9] [4, 7, 6, 3, 8, 9, 3, 5, 9, 5] [8, 0, 9, 9, 6, 1, 2, 0, 2, 7] [3, 7, 4, 1, 3, 4, 9, 9, 5, 6] [5, 2, 5, 1, 8, 1, 8, 0, 0, 1] [0, 4, 3, 4, 0, 6, 1, 8, 7, 5] [7, 7, 7, 9, 1, 3, 6, 4, 0, 1] [9, 5, 6, 5, 3, 8, 0, 1, 3, 4] [1, 7, 7, 3, 1, 0, 7, 4, 5, 6]
In a case where the sequence contains optional values, we can filter out the nil values using for case let
, executing the loop for non-nil elements only.
From the previous example of the to-do app, let’s assume some of our goals have no value. The getCompletedGoals(for goals:)
now accepts an array of the optional Goal
:
// MARK: - EXAMPLE func getCompletedGoals(for goals: [Goal?]) { for case let goal? in goals where goal.goalCompleted == false { /// Access to completed goals only. print(goal) } } // MARK: - USAGE let goals: [Goal?] = [Goal(name: "Learn something new!", goalCompleted: true), Goal(name: "Read about for-in loops and dictionaries"), nil, Goal(name: "Read about for-in loops and enums"), nil] getCompletedGoals(for: goals) // MARK: - OUTPUT Goal(id: F6CB6D77-9047-4155-99F9-24F6D178AC2B, name: "Read about for-in loops and dictionaries", date: 2021-05-21 23:04:58 +0000, goalCompleted: false) Goal(id: 822CB7C6-301C-47CE-AFEE-4B17A10EE5DC, name: "Read about for-in loops and enums", date: 2021-05-21 23:04:58 +0000, goalCompleted: false)
We can also use for-in
loops for looping through hardcoded numeric ranges. They can be divided into two parts:
…
)..<
)A closed range operator creates a range including both the end elements. A basic example of working with this operator is printing 10 numbers. Here, both 1 and 10 will be printed as well:
for number in 1...10 { print("The number is \(number)") }
FizzBuzz is a simple programming exercise where we can use for for-in
loops. The prompt is along these lines:
Write a program that prints numbers from 1 to n. Multiples of 3 print “Fizz” instead of the number and multiples of 5 print “Buzz.” For numbers that are multiples of both 3 and 5, print “FizzBuzz” instead of the number.
We loop through numbers 1 to n using the closed range operator to create a ClosedRange<Int>
constant. Then, we again loop through the tuple in mapping
and check for each element in the tuple. If the number is a multiple of 3, we append Fizz
to the string
.
As we check for each element in mapping
, if it is also a multiple of 5, we append Buzz
to the string with the result being FizzBuzz
:
// MARK: - EXAMPLE func fizzBuzz(for lastNumber: Int) { var result = [String]() let mapping = [(number: 3, value: "Fizz"), (number: 5, value: "Buzz")] for number in 1...lastNumber { var string = "" for tuple in mapping { if number % tuple.number == 0 { string += tuple.value } } if string == "" { string += "\(number)" } print(result) } return result } // MARK: - USAGE fizzBuzz(for: 10) // MARK: - OUTPUT ["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz"]
A half-open range operator creates a range excluding the last element. A basic example of working with this operator is accessing the indices of an array:
for index in 0..<array.count { // Access the index }
stride
For cases where you want to skip elements in a loop by a particular number, you can use stride
. We can also use this to go backward in a loop, starting from the last element and going to the first one.
Coming back to the example where we created a two-dimensional matrix of size 10Ă—10 with random values, we want to print every alternate element in the first row:
// MARK: - EXAMPLE func printFirstRow(for board: [[Int]]) { for rowIndex in stride(from: board.count - 1, through: 0, by: -2) { print(board\[rowIndex\][0]) } } // MARK: - USAGE printFirstRow(for: board) // MARK: - OUTPUT 7 4 4 4 8
Now, we want to print every alternate element in the first column, but in the reverse order:
// MARK: - EXAMPLE func printFirstColumn(for board: [[Int]]) { for rowIndex in stride(from: board.count - 1, through: 0, by: -2) { print(board\[rowIndex\][0]) } } // MARK: - USAGE printFirstColumn(for: board) // MARK: - OUTPUT 8 6 0 6 5
We can also iterate through a Dictionary
using for-in
loops, although the result will be unordered. The syntax is similar to arrays, with each element having its key and its value:
// MARK: - EXAMPLE func printDictionary(for numbers: [Int: Int]) { for number in numbers { // number is a Dictionary<Int, Int>.Element print("The value for key \(number.key) is \(number.value)") } } // MARK: - USAGE let numbers: [Int: Int] = [1: 2, 2: 3, 3: 4] printDictionary(for: numbers) // MARK: - OUTPUT The value for key 1 is 2 The value for key 2 is 3 The value for key 3 is 4
We can also explicitly use our own keywords instead:
// MARK: - EXAMPLE func printStockPrices(for stocks: [String: Int]) { for (name, price) in stocks { print("\(name) is currently valued at $\(price).") } } // MARK: - USAGE let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200] printStockPrices(for: stocks) // MARK: - OUTPUT Banana is currently valued at $125. Ramalon is currently valued at $3200. TapeBook is currently valued at $320.
We can use where
in dictionaries as well:
// MARK: - EXAMPLE func printStockPrices(for stocks: [String: Int]) { for (name, price) in stocks where name == "Banana" { print("\(name) is currently valued at $\(price).") } } // MARK: - USAGE let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200] printStockPrices(for: stocks) // MARK: - OUTPUT Banana is currently valued at $125.
If you want the highest price in this dictionary, you can sort the dictionary using sorted(by:)
:
// MARK: - EXAMPLE func printStockPrices(for stocks: [String: Int]) { for (name, price) in stocks.sorted(by: { $0.value > $1.value }) { print("\(name) is currently valued at $\(price).") } } // MARK: - USAGE let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200] printStockPrices(for: stocks) // MARK: - OUTPUT Ramalon is currently valued at $3200. TapeBook is currently valued at $320. Banana is currently valued at $125.
KeyValuePairs
As mentioned earlier, the Dictionary
doesn’t have defined ordering. If you want ordered key-value pairs, you can use KeyValuePairs
. This is useful in cases where you’re willing to sacrifice the fast, constant look-up time for linear time:
// MARK: - EXAMPLE func printStockPrices(for stocks: KeyValuePairs<String, Int>) { for (name, price) in stocks { print("\(name) is currently valued at $\(price).") } } // MARK: - USAGE let stocks: KeyValuePairs = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200] printStockPrices(for: stocks) // MARK: - OUTPUT Banana is currently valued at $125. TapeBook is currently valued at $320. Ramalon is currently valued at $3200.
You can even iterate over an enum in Swift by conforming to a specific protocol named CaseIterable
. This type provides a collection of all its values. In our case, it gives all the cases in Enum
. To access them, we use the allCases
property.
With yet another example, we are working on a hyper-casual game. We need to set different game modes on the main screen. We’ll create an enum
and iterate over it to access the mode name and the image name:
enum GameModes: String { case arcade case challenge case casual case timed } extension GameModes { var name: String { self.rawValue.capitalized } var image: String { switch self { case .arcade: return "🕹" case .casual: return "🎮" case .challenge: return "🎖" case .timed: return "⏳" } } } extension GameModes: CaseIterable {} // Usage for mode in GameModes.allCases { let gameOptionsView = GameOptionsView() gameOptionsStackView.addArrangedSubview(gameOptionsView) gameOptionsView.set(name: mode.name, image: mode.image) }
Loops are fundamental knowledge that helps you become better at Swift. In this article, we covered an overview of for-in
loops with different examples and use cases.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]