When writing code in Swift, it is not uncommon to want to repeat the execution of a set of statements. Controlling the position of different characters in a game, looping through the photos in a library, looping over a list of tweets – they all require re-executing the same set of statements multiple times. In Swift, we achieve this through loop statements.
Table of Contents
The Swift Loop Statements
In Swift there are four main types of loop statement that we have at our disposal:
- For-Condition-Increment
- For-In
- While
- Repeat-While
In the next few sections, we’ll look at each of these in turn. We’ll start with the for-condition-increment loops.
For-Condition-Increment Loops
Similar to the loops you may have encountered in other programming languages such as C, C++ or Java, the for-condition-increment
loop (or for
loop for short), will potentially be the most familiar type of Swift loop we’ll look at in this article.
As with the most of the other loops we’re going to look at later, the for-condition-increment
loop repeatedly executes one or more statements until a boolean condition evaluates to true
.
In Swift, the general structure for a for-condition-increment loop is as follows:
for <initialization>; <condition>; <increment> {
<statements>
}
The declaration consists of the for
keyword followed by three loop parameters (separated by semi-colons) and a set of mandatory curly braces ({}
) that denote the body of the loop and unlike in other languages, the parameters to the for-condition-increment loop in Swift do not have to be surrounded by a set of parentheses.
The first parameter to the for-loop is the initialisation expression.
Inititialisation Expressions
The initialisation expression is evaluated once, when the loop is first entered, and is used to setup any variables or constants that are needed for the loop. The initialisation expression actually looks very similar to the declaration of any type of variables in Swift with the declarations being prefixed with the keyword var
and being used to declare one or more variables. Where multiple variables are declared each declaration is separated from the next by a comma just like we saw when declaring multiple variables on a single line of code (see this article for more detail).
Whether it’s one variable or many, any variables declared within the initialisation expression can then be referenced within the remainder of the loops parameters or within the body of the loop itself.
We’re not quite done though. As an alternative to the initialisation expression it’s also worth pointing out that we don’t have to use the initialisation expression at all if we don’t want to. One option is to declare the variables used in the loop before the loop declaration itself and use the initialisation expression to simply initialise those variables. The primary reason for doing this would be if we wanted to access the final value of the loop variables after the loop has finished. For example:
var index : Int
for index = 0; index < 5; index++ {
// statements...
}
print(index)
In this example if we had declared index
within the initialisation expression itself, index
would only be available within the remainder of the loop declaration and within the body of the loop itself. It would therefore have gone out of scope when the loop exited and would result in an error when we tried to print it out.
By declaring index
before the loop, (effectively at the same level of scope as the print
statement), we retain access to the variable, even after the loop has exited and can therefore access its value.
If we so wish, we can also take this idea one stage further and both declare and initialise the loop variables before entering the loop. In this case, we can completely omit the initialisation expression though we do have to retain the semi-colon to separate the three parameters:
var otherIndex : Int = 3
for ; otherIndex < 5; otherIndex++ {
// statements...
}
Ok, that’s the initialisation expression. The next parameter to the loop declaration is the loop condition.
Loop Conditions
The loop condition is a boolean expression that is evaluated at the start of each iteration of the loop. If the loop condition evaluates to false
the loop ends and code execution continues immediately after the closing curly brace (}
) of the loop. If the expression evaluates to true
, the body of the loop and all the statements within the loops curly braces ({}
), are executed.
The final step of the loop iteration is the evaluation of the increment expression.
Increment Expressions
The increment expression may increase or decrease the values of, or set the state of, one or more of the other initialised variables within the for loop based on the outcome of the statements that were executed.
For example, in the following loop, the increment expression increments the the loop variable x
using the postfix increment operator. Once incremented, the next iteration of the loop starts with a re-evaluation of the loop condition. In our example below, due to the fact that the increment expression increments the value of x
by 1
each time around the loop, we will eventually reach a point where the loop condition evaluates to false
. At this point the loop will exit:
for var x = 0; x < 5; x++ {
print("\(x)")
}
// 0, 1, 2, 3, 4
As with the initialisation expression though, we do not have to use the increment expression. Instead, we can choose to increment the loop variables from within the body of the loop instead of in the increment expression. If we do this, we also have the option of omitting the increment expression in its entirety but again we must retain the semi-colon:
for var x = 0; x < 5; {
print("\(x)")
}
// 0, 1, 2, 3, 4
So now we understand the basics of a for
loop, what can we do with them?
For-Condition-Increment Loops in Practice
There are actually a whole range of examples to choose from when it comes to for-condition-increment loops in practice and I’m not going to go into them all here but I will give you a couple of illustrations just so you get some idea of what you can achieve. First, we could use a for-condition-increment loop to add random numbers to an array:
var anArray = Array<Int>()
for var i = 0; i < 10; i++ {
// Generate random number in range 0 <= randomNumber < 100
let randomNumber = Int(arc4random() % 100)
anArray.append(randomNumber)
}
// Note: Your output will undoubtedly be different to the this
// [14, 38, 14, 3, 55]
Or we could also use them to iterate over an array and print out the arrays contents:
for var i = 0; i < anArray.count; i++ {
print("\(anArray[i])")
}
// Note: Your output will undoubtedly be different to the this.
// 14, 38, 14, 3, 55
As I said, there’s a whole range of examples to choose from but before you get too excited about the possibilities there are few important things you should know about the future of the for-condition-increment loop in Swift. We’ll look at that next.
The Future of For-Condition-Increment Loops
As you may know, on 3rd December, 2015, Apple made version 2.2 of the Swift language open source under an Apache 2.0 license and as part of the infrastructure supporting the new open source language they also establishment of the Swift Evolution Process, a community driven process that manages changes to the Swift language, it’s public interface, standard library and APIs. This process is used to review any proposals from the open source community for changes to the language and if accepted, also plans the incorporation of those changes into upcoming releases.
In the case of loops, one such proposal, proposal SE-0007 is particularly applicable.
In proposal SE-0007, Erica Sadun (@ericasadun), proposed the removal of the for-condition-increment loop from the language and after review, this proposal has been accepted by the open-source community. The result is that as of Swift 3.0, this style of loop will be removed from the language and instead, you will have to use some of the other loop statements we’ll look at shortly. For this reason then, my advice would be to avoid using the for-condition-increment loops if possible and instead use one of the loop types we’re going to look at later in this article. Before we look at these alternatives though, there is something we need to look at first. This is the concept of ranges.
Ranges
In Swift, the Range
data type is a generic data type that represents a sequence of numbers. It’s like an array of Int
or Float
values that stretch from one specified number to another.
There are two types of range that are available in Swift:
- The closed range
- The half-open range
Closed-Ranges
The closed range in Swift represents a range of values stretching from one value up to and including a second value.
Closed ranges are declared using the closed-range operator which is represented by three dots (...
). When using the closed-range operator we supply the start value for the range immediately before, and the end value for the range immediately after, the operator itself. For example:
let closedRange = 0...4
In this example the resulting range is a range of 5 integer values that start at 0
and range up to and include the value of 4
.
The Half-Closed Range Operator
In addition to closed-ranges, the Swift language also supports half-closed ranges.
Half-closed ranges are similar to the closed-range but represent a range of values stretching from one value up to but not including a second value. It’s this ‘but not including’ bit that makes them different.
In Swift, half-closed ranges are declared using the half-closed range operator. The half-closed range operator is represented by two periods followed immediately by a less than character (e.g. ..<
).
The syntax for using the half-closed range operator is identical to that of the closed-range operator in that we supply the start value for the range immediately before the operator and the end value for the range immediately after.
This time though the end value will NOT be included within the range itself. You can use the less than character in the operator as a way of remembering it. The range will start at the first value and will range up to a value less than the second value.
For example:
let halfClosedRange = 0..<4
In this case this example our range contains 4 values, the integer values from 0
up to 3
.
Ok, that covers ranges. Now that we understand them, we can now take a look at the next type of loop in Swift, the for-in loop.
For-In Loops
With the future removal of the for-condition-increment loop in Swift, the for-in loop, is likely to be the most common form of loop used in Swift. As such, the for-in loop allows you to iterate over each of the elements in a given sequence such as the numbers in a range, items in an array or characters in a string.
The syntax of the for-in loop is much more succinct than that of the for-condition-increment loop and is much more akin to iterators that you may have seen in languages such as Python or Ruby.
In it’s most basic form, the syntax for a for-in loop is as follows:
for <incrementValue> in <sequence> {
// statements...
}
As with the for-condition-increment loop, the declaration of a for-in loop starts with the for
keyword. This is then followed by the name of the increment value, a constant whose value will be automatically set to the next item from loops sequence for each iteration of the loop. After that we have the in
keyword and the sequence of elements that the loop will iterate over. This sequence can be a range of values, an array, a dictionary or any other type of collection that conforms to the SequenceType
protocol.
Using For-In Loops With Ranges
Ok, let’s have a look at a real example. Say we wanted to print out the numbers from 0
through 4
inclusive. We could write this as follows:
for index in 0...4 {
print("\(index)")
}
// 0, 1, 2, 3, 4
As required, we start with the for
keyword and then provide a name for our increment value, in this case index
.
We don’t have to call it index
though we could have called it anything we like as long as it follows the rules for naming constants and variables in Swift. However, it is a a good idea to give the increment value an expressive name, something that gives readers of your code an idea of what you are using it for.
Notice also, that we don’t have to declare the increment value using the let
keyword or specify its type as we would normally have to when declaring a constant. In Swift, the inclusion of the name within the loops declaration is enough to have Swift implicitly declare the constant and infer its type for us. If you’re interested, the type is inferred to be the same type as each element within the loops sequence.
After we’ve specified the name for the constant we then have the in
keyword and then the sequence itself. In this case we make use of the closed range operator we looked at in the previous section and have used it to define a range of values ranging from 0
through 4
inclusive.
So with the declaration done, how exactly does the loop execution work?
Well, the first time around the loop, the constant index
is automatically set to the first value in the range (0
). Once set, the statements within the body of the loop are then executed. In this case it’s just a single print
statement but it could be as many statements as you like. Once execution of all the statements within the body of the loop is complete execution continues with the next iteration of the loop.
This involves setting the constant index
to the next element from the sequence (1
) and then re-executing the statements within the body of the loop. This process continues until all elements in the sequence have been evaluated at which point the loop terminates and execution continues immediately after the closing curly brace of the loops body. Get the idea?
Modifying the Increment Value within the Body of the Loop
Now, as I mentioned before, the first parameter to the for-in loop is by default a constant. But what if we wanted to modify that value within the body of the loop?
There are actually two approaches we could use to do this.
As of Swift 2.2, one option would be to use the var
keyword just before the name of the increment value. This would tell Swift to declare the increment value as a variable instead of a constant which in turn would allow us to modify the increment value within the body of the loop:
for var index in 0...4 {
index = index + 1
print("\(index)")
}
// 1, 2, 3, 4, 5
The are however, some caveats.
Firstly, as I’ve mentioned, at the start of each iteration, the new increment value is automatically set to the next value from the sequence. This means that any changes that we make to the increment value within the body of the loop, are automatically overwritten at the start of the next iteration. It’s not a big thing, it’s just something we have to watch out for.
The second caveat though, and probably more important one, is that this approach will only work for versions of Swift up to and including the 2.x release of Swift. In Swift 3.0, this trick is due to be removed under proposal SE-0003 meaning we will no longer be able to include the var
keyword within the loops declaration. Instead, we will have to declare a local variable within the body of the loop and set its initial value to the value of the increment value. The local value can then be modified within the body of the loop. To make things a little easier, this local variable can have the same name as the increment value though:
for index in 0...4 {
var index = index + 1
print(index)
}
// 1, 2, 3, 4, 5
Arguably, this is cleaner and more expressive than modifying the increment value itself. I’ll let you decided. Either way, given the fact that we will no longer be able to modify the increment value as of Swift 3.0, it’s an approach you should try to adopt going forwards.
Using a For-In Loop to Iterate In Reverse
Ok, let’s move on. We now know how to iterate over a range of values but what if we wanted to iterate over them in reverse?
In Swift this is super easy with the reverse()
function:
for index in (0...4).reverse() {
print(index)
}
// 4, 3, 2, 1, 0
Note here that unlike when we iterate over the range in the forward direction, in the reverse direction we have to wrap the range itself in a set of parentheses so that Swift doesn’t interpret the period before reverse()
as a decimal point. As you can see though, it’s pretty easy.
Ignoring the Increment Value
There are also situations when we use a for-in loop where we don’t use the increment value at all. This is usually when we want to ensure that the loop executes a certain number of times but don’t really care about the increment value on each iteration. In these situations, we can supply a wildcard pattern (an underscore character) in place of the increment value. This wildcard pattern indicates to Swift that we simply want to ignore the increment value and discard it.
For example, say we wanted to calculate the result of raising one number to the power of another. In this case we don’t care about the increment value itself, but do want to ensure that the loop runs a certain number of times:
var base = 10
let power = 3
var result = 1
for _ in 1...power {
result = result * base
}
print("\(base) to the power of \(power) equals \(result)")
// 10 to the power of 3 equals 10000
Using For-In Loop With Non-Contiguous Ranges
Now in all our examples of iterating over ranges so far, we’ve always iterated over a contiguous sequence; 0
, 1
, 2
etc but the reality is, we don’t have to.
The numeric types in Swift all adopt Swifts Strideable
protocol. Without going into protocols in depth, this simply means that they all implement a specified set of methods two of which are the stride(through:by:)
and stride(to:by:)
functions.
The stride(through:by:)
function is similar to the closed range operator we look at earlier. On calling the function we provide two arguments. The first is for the through:
parameter which represents the upper limit of our sequence. The numbers in the sequence will continue up to and include this value. You can think of this as equivalent to the upper value in the closed range operator we looked at earlier.
We also have the second parameter, the by:
parameter. This represents the increment between each successive element in the sequence. For example if we supplied a value of 2
to the by parameter each element of the sequence would be offset from the next element by 2
.
Let’s look at a simple example of using these in practice. Say we wanted to print out all the even numbers between 0
and 8
inclusive we could write:
for index in 0.stride(through: 8, by: 2) {
print(index)
}
// 0, 2, 4, 6, 8
In this case the sequence start at 0
(the initial numeric value we called the function on) and continues up to and includes the value 8
. Each element in the sequence is offset from the previous one by a value of 2
.
Ok, here is a slightly more tricky example:
for index in 0.stride(through: 9, by: 2) {
print(index)
}
// 0, 2, 4, 6, 8
It’s essentially the same as our previous example but this time I’ve changed the value of the through:
parameter to 9
instead of 8
. As you can see, we still get the same output though. Surprised?
What I wanted to illustrate here is that the sequence continues up to and including the through:
value but does not exceed it. Because the next increment in the sequence would be a value of 10
(which obviously exceeds the upper limit of our stride), the sequence ends before that value is reached. Get the idea?
Ok. Now that we’ve got the idea of the stride(through:by:)
function, let’s look at it’s sibling the stride(to:by:)
function.
Essentially the syntax and mechanics for the stride(to:by:)
function is identical to what we just looked at. This time though, the sequence will continue up to but not include the value we provide for the to:
parameter:
for index in 0.stride(to: 8, by: 2) {
print(index)
}
// 0, 2, 4, 6
The other thing to point out here is that both these functions can also be used with floating point numbers. This means that we can use non-integer increment values and non-integer increments. The only thing to watch out for when using floating-point numbers though is that we have to supply the initial value for the sequence within a set of parentheses to ensure that Swift separates the decimal point from the function call. In this example I do just that with the stride(through:by:)
function:
for index in (3.1).stride(through: 4.5, by: 0.3) {
print(index)
}
// 3.1, 3.4, 3.7, 4.0, 4.3
The stride(to:by:)
function works pretty much the same way.
We can also use the stride(to:by:)
and stride(through:by:)
functions to iterate in reverse if we provide a negative value for the by:
parameter and ensure that the through:
or to:
parameters are less than the starting value:
for x in 10.stride(through: 0, by: -2) {
print(x)
}
// 10, 8, 6, 4, 2, 0
Now, we don’t just have to use the for-in loop to iterate over numbers. In the next few sections, we’re going to look at how to iterate over collections of items instead. Let’s first start by looking at how we print out the characters in a string.
Using For-In Loops To Iterate Over The Characters in a String
As you may know, in Swift we can retrieve the characters from a string using the characters
property. This returns a value of type CharacterView
which conforms to the CollectionType
protocol. The CollectionType
protocol itself then inherits from the SequenceType
protocol we saw earlier. The bottom line is that because of this protocol conformance we can iterate over the characters in a string just as if they were a range of numbers:
for character in "Hello“.characters {
print("\(character)")
}
// H, e, l, l, o
Also by appending the reverse()
function we can also iterate over the characters in reverse:
for character in "Hello“.characters.reverse() {
print("\(character)")
}
// o, l, l, e, H
Using For-In Loops To Iterate Over the Contents of an Array
When it comes to iterating over an array of items (ok, in this example I’m going to use an array of numbers but it could be an array of anything), things aren’t much different.
In the case of arrays, we simply provide the array in place of the sequence parameter in the loop declaration and as the loop executes the loop increment will be sequentially set to each element from the array. In this example, we use it to total the scores from a team of players:
let individualScores = [10, 11, 3, 5, 9, 2, 17]
var teamScore = 0
for score in individualScores {
teamScore += score
}
print("The Team Scored: \(teamScore)")
// The Team Scored: 57
Using a For-In Loop to Iterate Over the Contents of a Dictionary
The syntax for iterating over a dictionary in Swift is much the same as with Arrays. This time though we supply the dictionary in place of the sequence parameter to the loop.
When iterating over dictionaries each key/value pair is extracted from the dictionary as a two-element tuple and being a tuple we can automatically decompose it into its constituent parts:
var dict = ["key1" : "value1", "key2" : "value2"]
for (key, value) in dict {
print("Key: \(key) and Value: \(value)")
}
// Key: key1 and Value: value1
// Key: key2 and Value: value2
The only thing to watch out for with dictionaries is that due to their key/value nature, there is no guarantee to the order in which individual elements are returned from the dictionary.
Using enumerate() with a For-In Loop
Now here is a question. What if I was iterating over an array and wanted to track both the index of the current increment value in the array and the increment value itself?
Well, we can achieve this using the enumerate()
function.
The enumerate()
function returns a sequence of tuples with each tuple containing the index of the element in the original sequence plus the element itself and being tuples, we can again decompose them into their constituent elements:
let planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
for (index, planet) in planets.enumerate {
print("\(index + 1): \(planet)")
}
// 1: Mercury
// 2: Venus
// 3: Earth
// 4: Mars
// 5: Jupiter
// 6: Saturn
// 7: Uranus
// 8: Neptune
This same technique works with any of the sequences we’ve looked at so far; ranges, arrays, characters in a string. All of them.
We can also use the enumerate()
function with dictionaries as well but if we do, we need to be a little careful as the enumerate()
function will return a two element tuple with the first element being the index and the second element being a two-element tuple containing the key/value pair.
Ok, let’s park for-in loops for a while and look at the remaining types of loop statement that are available in Swift. We’ll start with the while loop.
The While Loop
While loops have a slightly different structure to the for-condition-increment or for-in loops we’ve looked at so far. They’re actually much simpler and continue to execute their statements whilst a boolean condition evaluates to true
.
The general syntax for a while loop is as follows and as you can see we don’t need parentheses around the condition:
while <condition> {
// statements...
}
Ok. That’s the syntax, but how do they actually work?
Well at the start of each iteration of the loop the condition is first evaluated. As I mentioned above, this condition must evaluate to true
for the body of the loop to be executed, otherwise execution continues immediately after the closing curly brace. If the condition is true
, the statements within the body of the loop are then executed and upon completion, execution returns to the start of the loop with re-evaluation of the condition. Execution continues in this manner until the loop condition evaluates to false
and the loop exits.
Ok, let’s have a look at an example of this in practice:
var y = 1
while y < 100 {
print(y)
y = y * 2
}
print("Final: \(y)")
// 1, 2, 4, 8, 16, 32, 64, Final: 128
In this example we start by declaring a variable y
outside of the loop and set its initial value to 1
. We then enter the loop itself.
The first thing that occurs is evaluation of the loops condition (y < 100
). If true
(which it is in this case), the statements within the body of the loop are executed. In our case, this means printing the value of y
before multiplying it’s value by 2
and storing the result back into y
. At that point, execution returns to the start of the loop and the boolean condition is re-evaluated. This pattern continues until at some point the expression y < 100
evaluates to false
at which point the loop ends and execution continues immediately after the closing curly brace of the loop where we then print out the the final value of y
.
Now, there is one subtlety with while loops. Due to the fact that their boolean expression is evaluated at the start of the loop it can, in certain situations, mean that the body of the loop isn’t executed at all. For example, if I had the following loop immediately after the example above the value of y
would never be printed:
while y < 100 {
print("y: \(y)")
}
// No output.
This is because as we enter the while loop y
is already set to a value of 128
. As we’ve seen, the first thing the loop does is to evaluate it’s boolean condition (y < 100
) which would evaluate to false
and execution would therefore continue after the closing brace of the loop without ever executing the statements within the loops body.
The key thing to remember then is that while loops can execute zero or more times. Just keep that in mind as we look at our next loop statement the repeat-while loop.
The Repeat-While Loop
The general structure for a repeat-while loop is as follows:
repeat {
// statements...
} while <condition>
The repeat-while loop is similar to the while
loop in that it continues to execute the statements within the body of the loop whilst a boolean expression evaluates to true
. The difference though is that the repeat-while loop evaluates its boolean condition at the end of the loop rather than the start. We’ll talk about the implications of this in a second. For now let’s take our previous example this time written as a repeat-while loop:
var x = 1
repeat {
print(x)
} while x < 100
print("Final: \(x)")
// 1, 2, 4, 8, 16, 32, 64, Final: 128
Ok, similar output. Good. But here is the subtlety. Check out what happens when we also run our second loop:
repeat {
print("\(x)")
} while x < 100
// x: 128
Ok, we get the value of x
printed. Maybe not what you were expecting? Let’s think about it.
Remember what I said about the repeat-while loop? The boolean condition is evaluated at the end of the loop rather than the start. This means that although x
is set to 128
as we enter the loop, the statements within the body of the repeat-while loop are executed before we check the boolean condition. The effect is that with repeat-while loops their statements will always execute one or more times before loop exits. Contrast this to the zero or more times we saw with the while loop. It’s a subtle difference but one that can be extremely useful at times.
Ok, a couple more things to look at before we dive into some advanced loops. The first is infinite loops.
Infinite Loops
Infinite loops are loops that repeat endlessly, either because the loop does not have a condition that can cause termination of the loop or because the loop has a termination condition that can never be met. Either way, infinite loops are generally a bad thing in our code. There biggest issues is that they can cause an app to become less responsive as they tend to burns excessive CPU cycles and the reality is that most of the time, they are a sign of a bug.
The simplest form of an infinite loop is as follows:
// An inifinite loop with a condition that can never be met.
while true {
// Do something forever...
}
Pretty obvious. But what about this one? It’s more subtle and more like the bugs you will encounter in your own code.
// An infinite loop with a condition that can never cause termination.
var z = 0
while z != 5 {
z += 2
}
In this case, the loop is an infinite loop because the value of z
can never be set to five and therefore the loop will never terminate. As I said, be careful with infinite loops.
Nested Loops
Now, there is one other thing to mention that you may not have picked up so far.
All of the loops we’ve talked about up until now, can be nested, with one loop inside the body of the other.
When nested in this fashion, the outer loop only moves on to its next iteration once the inner loop and any other statements within the body of the inner loop have finished executing in their entirety.
For example, say we wanted to print out our multiplication tables, we could do that with two loops, one nested inside the other as follows:
for i in 1...12 {
for j in 1...12 {
print("\(i) * \(j) = \(i * j)")
}
}
// 1 * 1 = 1
// 2 * 2 = 2
// ...
// 12 * 12 = 144
In this case, the value of i
only increments after the for-loop with the loop increment j
has completed.
Advanced Loops
Ok, by this point, we’ve pretty much covered the basics of the loops in Swift and if this were all you knew, you’d probably get along just fine. However, there are a number of other more advanced techniques that we can also use in conjunction with Swifts loop statements that take the power of these relatively simple statements to the next level. The first of these is iteration requirements.
Using a For-In Loop in Conjunction With Iteration Requirements
In Swift, iteration requirements allow us to add some additional requirement to the standard for-in loop that must be satisfied in order for that particular iteration of the loop to execute.
In Swift, iteration requirements are specified using a where
clause. The where
clause consists of the where
keyword followed by either a single or a compound boolean expression. Whichever you choose, the expression must evaluate to true
for the particular loop iteration to execute. If not, execution continues with the next iteration of the loop (or the loop exits if the loop condition now evaluates to false
). Maybe its easier to explain with an example.
Imagine that we had a list of numbers and we only wanted to print out those that had even values. We’ve seen how we could do it using the stride
functions but we could also do it using an iteration requirement:
for index in 0...4 where index % 2 == 0 {
print(index)
}
// 0, 2, 4
In this example, the first part of the for-in loop is much as we’ve seen previously; we simply loop over each of the values from 0
through 4
inclusive. However, in this case we’ve also attached an iteration requirement to the loop. This where
clause consists of a single boolean expression (index % 2 == 0
) which will only evaluate to true if the value of index
is an even number. As a result, each time this requirement evaluates to false
, the loop is skipped and the next iteration to be executed. This results in the body of the loop only being executed for even numbers. We can see this in the values that are printed.
We can also make these iteration requirements more complicated. A more complicated example may be to only print out a string if it has more then 3 characters and starts with either an H
or a Y
:
let words = ["Hello", "World", "How", "Are", "You?"]
for word in words where (word.characters.count > 3) && (word.characters.first == "H" || word.characters.first == "Y") {
print(word)
}
// "Hello", "You?"
Notice here that the word How
is not printed despite the fact that it starts with an H
. This is because it does not also exceed the 3-character requirement we placed on the loop resulting in the overall iteration requirement evaluating to false
.
So that’s loop requirements, but we can also take things a step further with pattern matching.
Using For-In Loop in Conjunction With Pattern Matching
Swifts pattern matching syntax allows us to extend our loop declarations by supplying patterns to match the loop value against. These patterns represent the structure of a value rather than any single value and can therefore be used to match match either single values or a composite values such as with tuples. In addition to simply pattern matching though, these patterns also allow us to bind each part of any matched value to a new constant or variable name and this can be seriously useful as we’ll see shortly.
When specifying a pattern within a loop declaration, we first use the case
keyword. The case
keyword indicates to Swift that we are about to supply a pattern to match against. This is followed by either the var
or the let
keyword. These indicate whether the matched values should be bound as either new variables or new constants.
Note: One thing to note though here is that the ability bind to new variables will be removed under SE-0003 so try to avoid using this if you can. Instead, use the same technique we saw earlier and bind to constants in the loop declaration and declare new variables within the body of the loop should you wish to change the values.
Ok, let’s keep going. After the var
or let
keywords comes the pattern itself. Pattern matching in Swift is actually a much more complicated topic than you might expect so I’m not going to go into it in depth here. For now I’ll just give you a couple of simple examples and we can save a detailed look at pattern matching for a future post.
Anyway, let’s start with a simple example. Say I had an array of String
values and wanted to determine how many times a given string existed within the array. We could use the pattern matching technique in conjunction with an identifier pattern (essentially a concrete value against which to match) in order to filter the values from the array:
let names = ["Steve", "Scott", "Paul", "James", "Steve"]
var steveCount = 0
for case "Steve" in names {
steveCount = steveCount + 1
}
print("There were \(steveCount) Steve's")
// There were 2 Steve's
In this example, we match against the string literal Steve
. Each time around the loop, the value is compared to this string literal and if it matches, the body of the loop is executed. In our case, this means incrementing the value of the steveCount
variable which we eventually print out once the loop has exited.
Using For-In to Filter Tuple Values
This technique can also be used with tuples as well. With tuples, we can supply a comma separated list of zero or more patterns enclosed within parentheses to match against.
In this example, we use two types of pattern within the parentheses. First we use a value-binding pattern. This is the name of a constant (due to the use of the keyword let
) to which the value from the tuple will be bound should it match. We also supply an identifier pattern similar to what we saw in the last example. This time it is the literal string United Kingdom
:
let cities = [("London", "United Kingdom"), ("Paris", "France"), ("Washington DC", "United States of America")]
for case let (city, "United Kingdom") in countries {
print("City: \(city) Country: \(country)")
}
// City: London Country: United Kingdom
The effect is that only those tuples from the array who have a second element of United Kingdom
will be printed out. Any tuple that matches has their first element bound to the constant city
which we can then access (and print out) within the body of the loop.
Using For-In To Filter Enum Values
This same technique can also be extended to enumerations as well.
We can use the same case let <pattern>
approach to extract, and bind, any associated value of an enumeration. In this form we supply an enumeration case pattern which consists of the enumeration case and a tuple continuing zero or more identifier patterns:
enum Direction {
case North(Int)
case South(Int)
case East(Int)
case West(Int)
}
let directions : [Direction] = [.North(10), .South(5), .East(3), .South(13), .West(12)]
for case let .South(value) in directions {
print(value)
}
// 5, 13
In this example I’ve used compass headings with an associated distance (bad example but go with it).
Here we filter the array of enumeration values to only those values with the enumeration .South
will be printed. However, we also combine this with a value-binding pattern to extract the associated value. For each enumeration value that matches the identifier pattern the associated value is then bound to the constant value
which we then print out.
Iterating Over Collections Containing Optional Items
We can also use a similar syntax for iterating over collections that contain optional values. In this case we provide an optional pattern. The optional pattern matches values that are wrapped as optionals and allows us to execute the body of the loop only if the values are not nil
.
We’ve talked in previous posts about how optionals under the hood are actually an enumeration with two values either .Some(Value)
or None
and in the last section we saw how we could optionally match and bind an emulation value. Using that knowledge we can write:
let items:[String?] = [nil, nil, "Hello", nil, "World!"]
for case let .Some(message) in items {
print(message)
}
// Hello
// World!
However, we have got some syntactic sugar at our disposal and can make use of another pattern available to us in Swift the optional pattern. The optional pattern consists of an identifier pattern which we’ve looked at earlier followed immediately by a question mark:
for case let message? in items {
print(message)
}
Semantically this is identical to the example above, but it provides us with a short-hand notation for binding any matching value to the given constant, if and only if, the value is not nil
at which point the body of the loop will execute.
Iterating Over Collections Containing Heterogeneous Content
And as a final thing for today, we can also use a similar technique to iterate over collections with heterogenous content through the use of a type casting pattern.
There are actually two type casting patterns available in Swift, but only one of them, the as
pattern, can be used with a loop.
The as
pattern matches a value, if the type of that value at runtime, is either the same, or a subclass of, the type specified to the right-hand side of the as
. If the value matches, value is then bound to a local constant for use within the body of the loop.
For example, say we had a collection of objects of different types all held within an array. We could print only those objects that were of type NSString
by doing the following:
let myString : NSString = "Hello world!"
let otherString = "AnotherString"
let myNumber : NSNumber = 10
let myURL : NSURL = NSURL(fileURLWithPath: "http://example.com/")
let myDate = NSDate()
let moreItems : [NSObject] = [otherString, myString, myNumber, myURL, myDate]
for case let item as NSString in moreItems {
print(item)
}
// AnotherString
// Hello world!
Only if the loop value is of type NSString
is it bound to the constant item
.
This same technique also works with protocols. In the following example, we only print out the isEmpty
property for those objects that conform to the CustomProtocol
protocol:
protocol CustomProtocol {
var isEmpty : Bool { get }
}
struct Foo{}
struct Bar : CustomProtocol {
var isEmpty : Bool = true
}
extension String : CustomProtocol {}
let protocolItems : [Any] = [Foo(), Bar(), String()]
for case let item as CustomProtocol in protocolItems {
print(item.isEmpty)
}
// true
// true
Summary
Ok. That’s enough. We’ve done loops to death and I’ve just realised this is another extremely long post so I’m going to leave it there for today.
As you’ve seen, loops are one of the fundamental programming constructs used in Swift. Whichever style you choose whether it be the, the if-condition-increment, the for-in, the while or the repeat-while, loops in Swift allows us to executed code blocks repeatedly without the need to write a chunk more code. Whichever form you choose to use, having a good understanding of what is available and what there capabilities are is critical if you are going to be the best from the Swift language and I hope this article has given that to you. I’ll see you next time.