Swift offers many different built-in ways to iterate over collections (such as arrays, sets, and dictionaries) — the most basic of which being for
loops, which let us run a piece of code for each element that was found within a given collection. For example, here we’re looping through an array of names
, and we’re then outputting each name by printing it into the console:
let names = ["John", "Emma", "Robert", "Julia"]
for name in names {
print(name)
}
An alternative way of accomplishing the same thing would be to instead call the forEach
method on our array of names
, which lets us pass a closure that’ll be run for each element:
names.forEach { name in
print(name)
}
One key difference between a for
loop and forEach
, though, is that the latter doesn’t enable us to break
the iteration in order to stop it once a given condition has been met. For example, when going back to using a for
loop again, we could decide to stop the iteration once the name Robert was encountered:
let names = ["John", "Emma", "Robert", "Julia"]
for name in names {
if name == "Robert" {
break
}
print(name)
}
There are also many other ways to use for
loops in Swift. For example, if we’d like to gain access to what index
that we’re currently handling as part of our iteration, then we could instead choose to base our loop on a range that goes from zero to the number of elements within our collection. We could then use the Array
type’s subscripting feature to retrieve the current element for that index — like this:
for index in 0..<names.count {
print(index, names[index])
}
Another way to write the exact same loop would be to instead iterate over our array’s indicies
, rather than constructing a range manually:
for index in names.indices {
print(index, names[index])
}
Yet another approach would be to use the enumerated
method to convert our array into a sequence containing tuples that pair each index with its associated element:
for (index, name) in names.enumerated() {
print(index, name)
}
Note that the enumerated
method always uses Int
-based offsets, which in the case of Array
is a perfect match, since that collection also uses Int
values as its indices.
Next, let’s take a look at while
loops, which offer a way for us to repeatedly run a block of code as long as a given boolean condition remains true
. For example, here’s how we could use a while
loop to keep appending each name within our names
array to a string, as long as that string contains less than 8 characters:
let names = ["John", "Emma", "Robert", "Julia"]
var index = 0
var string = ""
while string.count < 8 {
string.append(names[index])
index += 1
}
print(string)
Another way to construct a while
loop (which perhaps isn’t as commonly used in Swift as in other languages) is by using a separate repeat
block, which will also get repeatedly run as long as our while
condition evaluates to true
:
let names = ["John", "Emma", "Robert", "Julia"]
var index = 0
var string = ""
repeat {
string.append(names[index])
index += 1
} while string.count < 8
print(string)
The key difference between repeat
and a stand-alone while
loop is that a repeat
block will always be run at least once, even if the attached while
condition initially evaluates to false
.
One important thing to keep in mind when using while
loops, though, is that it’s up to us to make sure that each loop is ended at an appropriate time — either by manually using break
(like we did earlier when using a for
loop), or by ensuring that our loop’s boolean condition is met once the iteration should be terminated.
For example, when constructing our name-based string
value, we probably want to make sure that the current index
won’t go out of the bounds of our names
array — since otherwise our app would crash when subscripting into that array. One way to do that would be to attach a second boolean condition to our while
statement — like this:
while string.count < 8, index < names.count {
string.append(names[index])
index += 1
}
Another approach would be to instead perform the above index-check inline within the loop itself, for example by using a guard
statement that’ll break our loop within its else
clause:
while string.count < 8 {
guard index < names.count else {
break
}
string.append(names[index])
index += 1
}
When working with loops, there’s often many different ways to model the same logic, and it can usually be quite useful to prototype a few different approaches in order to figure out which one that’ll work best within each situation. As an example, we could actually have chosen to implement the above iteration using a for
loop instead — since we’d be able to attach our string.count
-based condition to such a loop by using the where
keyword:
let names = ["John", "Emma", "Robert", "Julia"]
var string = ""
for name in names where string.count < 8 {
string.append(name)
}
print(string)
That doesn’t mean that the above for
-based version is objectively better than the while
-based one. Picking what type of loop to use is often a matter of taste and code structure, although I’d personally argue that whenever a loop is based on an actual collection of elements, using a for
loop is most often the simplest way to go.
Finally, let’s turn our attention to dictionaries, which can also be iterated over using the exact same tools that we’ve been using to loop through arrays and ranges. When iterating over a dictionary, though, we don’t just get access to single elements, but rather (key, value)
tuple pairs. For example, here’s how we could iterate through a dictionary that contains a category-based version of our names
dataset:
let namesByCategory = [
"friends": ["John", "Emma"],
"family": ["Robert", "Julia"]
]
for (category, names) in namesByCategory {
print(category, names)
}
Sometimes, though, we might not need access to both the keys and values that a given dictionary contains, so it’s also possible to use the _
symbol to ignore a given tuple member within our iteration. Here’s how we could do just that to ignore our dictionary’s values, and only handle its keys (or categories) within our loop:
for (category, _) in namesByCategory {
print(category)
}
However, while the above is perfectly valid Swift code, there are actually two purpose-built APIs that enable us to perform either a key-only or value-only dictionary iteration simply by accessing either the keys
or values
property on the dictionary that we’d like to iterate over:
for category in namesByCategory.keys {
print(category)
}
for names in namesByCategory.values {
print(names)
}
When working with dictionaries, sets, or other collections that don’t offer a guaranteed element order, it’s important to remember that our loops also won’t be performed in a predictable order. So, for example, if we’d like to ensure that the above category
iteration always happens in the same order, then we could make that happen by first sorting our dictionary’s keys
into an array — like this:
for category in namesByCategory.keys.sorted() {
print(category)
}
Our categories will now always be iterated over in alphabetical order. Of course, performing the above kind of sorting operation is only required if the order of elements matters, which will likely only be true for certain iterations.
I hope that you found this Basics article useful, and that you learned at least one new way of constructing a loop in Swift. For more Basics articles, check out this page, and if you have any questions, comments, or feedback (even if it’s positive!), then feel free to reach out via either Twitter or email.
Thanks for reading!