Swift Iterating with stride: Forward and Reverse Looping
Swift ranges like 0...10 are great for counting one step at a time, but many real loops need a different step size. You might want to count by 2, loop backward, generate time intervals, or stop just before a final value. This article explains how to iterate with stride in Swift, when to use to versus through, and how to avoid the mistakes that often confuse beginners.
Quick answer: Use stride(from:to:by:) when the end value should be excluded, and use stride(from:through:by:) when the end value should be included if the step reaches it. stride is the standard Swift way to loop with a custom step size, including reverse loops.
Difficulty: Beginner
Helpful to know first: You'll understand this better if you know basic Swift syntax, how for-in loops work, and how numeric types like Int and Double are used.
1. What Is Iterating with stride?
Iterating with stride means producing a sequence of values that moves from a starting value toward an ending value using a fixed step size. Instead of advancing by 1 automatically, you choose the amount to move each time.
- stride is useful when a normal range is not enough.
- It can move forward with a positive step, such as 0, 2, 4, 6.
- It can move backward with a negative step, such as 10, 8, 6, 4.
- Swift provides two common forms: stride(from:to:by:) and stride(from:through:by:).
- The difference between them is whether the ending value is excluded or included.
A regular range like 1...5 works well when you want every number in order. But if you need every second number, every fifth index, or a reverse countdown, stride is usually the right tool.
A common comparison is range versus stride. Ranges are simpler for step-by-1 iteration, while stride is designed for custom increments or decrements.
2. Why Iterating with stride Matters
Custom-step loops appear often in real Swift code. Even if beginner examples focus on counting from 1 to 10, production code frequently needs more control.
- Processing every nth item in a collection index sequence.
- Creating countdowns and reverse traversals.
- Generating chart axis values or measurement intervals.
- Running simulations at fixed time steps.
- Skipping unnecessary iterations for performance or clarity.
Using stride also makes intent clearer. A loop that says “move by 5” is easier to understand than a loop that counts every value and manually ignores most of them.
You should not use stride when a simple range already expresses the logic naturally. If you just need every integer from 1 to 5, a normal range is shorter and easier to read.
3. Basic Syntax or Core Idea
Using stride(from:to:by:)
This form starts at one value, moves by a fixed amount, and stops before reaching the end value.
for number in stride(from: 0, to: 10, by: 2) {
print(number)
}This prints 0, 2, 4, 6, and 8. It does not print 10 because to is exclusive.
Using stride(from:through:by:)
This form includes the end value if the stepping lands on it exactly.
for number in stride(from: 0, through: 10, by: 2) {
print(number)
}This prints 0, 2, 4, 6, 8, and 10.
Using a negative step
To count backward, the step must be negative.
for countdown in stride(from: 5, through: 1, by: -1) {
print(countdown)
}This produces a reverse loop from 5 down to 1.
So the core idea is simple:
- from is the starting value.
- to or through is the target boundary.
- by is the step size.
4. Step-by-Step Examples
Example 1: Looping through even numbers
This example prints only even numbers from 0 up to, but not including, 10.
for value in stride(from: 0, to: 10, by: 2) {
print(value)
}This is clearer than looping through every number and checking whether it is even.
Example 2: Inclusive counting with through
If you need the ending value included, use through instead of to.
for value in stride(from: 0, through: 10, by: 5) {
print("Checkpoint: \(value)")
}This prints checkpoints at 0, 5, and 10. It shows the inclusive behavior clearly.
Example 3: Reverse countdown
A negative step lets you count down instead of up.
for seconds in stride(from: 10, through: 0, by: -1) {
print("\(seconds)...")
}
print("Go!")This is a common pattern for timers, countdown displays, and reverse traversal.
Example 4: Working with decimal values
stride can also be used with floating-point values such as Double.
for temperature in stride(from: 18.0, through: 20.0, by: 0.5) {
print("Set to \(temperature)°C")
}This creates values 18.0, 18.5, 19.0, 19.5, and 20.0. It is useful for measurement-based iteration, though floating-point precision needs care, which we will cover later.
Example 5: Iterating over array indexes with a step
Sometimes you want every second item in an array by index.
let names = ["Ana", "Ben", "Chris", "Dina", "Eli"]
for index in stride(from: 0, to: names.count, by: 2) {
print(names[index])
}This prints the values at indexes 0, 2, and 4. Using to: names.count is important because array indexes stop before the count.
5. Practical Use Cases
- Showing every 5th minute in a scheduling or calendar tool.
- Building a countdown before a game starts.
- Sampling every nth element from a data set.
- Generating tick marks for a graph or progress scale.
- Looping backward through indexes when removing items carefully.
- Testing logic at fixed intervals, such as 0.0, 0.25, 0.5, and 0.75.
6. Common Mistakes
Mistake 1: Using to when you meant through
This is one of the most common beginner problems. The loop seems correct, but the final value never appears.
Problem: to excludes the ending value, so this code stops before 10 even though the step size would otherwise reach it.
for number in stride(from: 0, to: 10, by: 2) {
print(number)
}Fix: Use through when the ending value should be included if the sequence lands on it.
for number in stride(from: 0, through: 10, by: 2) {
print(number)
}The corrected version works because through uses an inclusive boundary.
Mistake 2: Using a positive step in a reverse loop
When counting down, the step must move toward the ending value. A positive step cannot do that.
Problem: This loop tries to go from 10 down to 0 with a positive step, so it does not move in the correct direction and will not produce the intended sequence.
for number in stride(from: 10, through: 0, by: 1) {
print(number)
}Fix: Use a negative step when the start is greater than the end.
for number in stride(from: 10, through: 0, by: -1) {
print(number)
}The corrected version works because each step moves closer to the lower boundary.
Mistake 3: Going out of bounds when using array indexes
Array loops often fail when the end value is treated as a valid index. In Swift, the final valid index is one less than count.
Problem: This code includes items.count as an index by using through, which can cause a runtime crash with an index out of range error.
let items = ["A", "B", "C"]
for index in stride(from: 0, through: items.count, by: 1) {
print(items[index])
}Fix: Use to: items.count so the loop stops before the invalid final index.
let items = ["A", "B", "C"]
for index in stride(from: 0, to: items.count, by: 1) {
print(items[index])
}The corrected version works because array indexes run from 0 up to, but not including, count.
Mistake 4: Expecting floating-point stride values to behave perfectly
Floating-point numbers like 0.1 cannot always be represented exactly in binary. That can produce surprising output in some calculations.
Problem: This code may produce values with tiny precision differences, so direct equality checks can fail or printed results can look unexpected.
for value in stride(from: 0.0, through: 1.0, by: 0.1) {
if value == 0.3 {
print("Found exactly 0.3")
}
}Fix: Avoid relying on exact equality with floating-point stride values. Compare within a small tolerance or use integer steps and convert when needed.
for step in stride(from: 0, through: 10, by: 1) {
let value = Double(step) / 10.0
if abs(value - 0.3) < 0.0001 {
print("Close to 0.3")
}
}The corrected version works because it avoids depending on exact binary representation of decimal fractions.
7. Best Practices
Practice 1: Choose to or through deliberately
Beginners often pick one form without thinking about the boundary. A better approach is to decide whether the ending value belongs in the sequence.
// Excludes 12
for hour in stride(from: 0, to: 12, by: 3) {
print(hour)
}
// Includes 12
for hour in stride(from: 0, through: 12, by: 3) {
print(hour)
}This practice makes loops easier to reason about and prevents off-by-one errors.
Practice 2: Match the step direction to the loop direction
Make the step clearly positive when moving upward and clearly negative when moving downward.
// Preferred forward loop
for score in stride(from: 0, through: 100, by: 20) {
print(score)
}
// Preferred reverse loop
for score in stride(from: 100, through: 0, by: -20) {
print(score)
}This keeps the loop behavior obvious and avoids empty or incorrect iteration.
Practice 3: Prefer stride over filtering every value manually
If the loop itself should skip values, express that in the iteration rather than inside the body.
// Less preferred
for number in 0...20 {
if number % 2 == 0 {
print(number)
}
}
// Preferred
for number in stride(from: 0, through: 20, by: 2) {
print(number)
}The preferred version better communicates that only even numbers are part of the loop.
8. Limitations and Edge Cases
- stride(from:through:by:) includes the end only if the stepping lands on it exactly.
- Floating-point iteration can show precision surprises, especially with decimal step sizes like 0.1.
- Using through: array.count with array indexing can cause an index out of range crash.
- If the step direction does not match the start and end values, the loop may produce no values.
- stride is best for evenly spaced steps. If the next value depends on changing logic, a while loop may be a better fit.
- For simple step-by-1 integer loops, ranges are usually shorter and easier to read than stride.
9. Practical Mini Project
This small program prints time marks every 15 minutes across one hour, then performs a reverse countdown. It combines inclusive and exclusive boundaries in a realistic way.
let interval = 15
print("Quarter-hour marks:")
for minute in stride(from: 0, through: 60, by: interval) {
print("Minute \(minute)")
}
print("")
print("Countdown:")
for seconds in stride(from: 5, through: 0, by: -1) {
print("\(seconds)")
}
print("Start!")The first loop uses through because 60 should appear as the final quarter-hour mark. The second loop shows reverse iteration with a negative step. Together they demonstrate how stride handles both forward and backward loops cleanly.
10. Key Points
- Use stride when you need a custom step size in a loop.
- stride(from:to:by:) excludes the ending value.
- stride(from:through:by:) includes the ending value if the step reaches it.
- Use a negative by value for reverse loops.
- When indexing arrays, use to: array.count to avoid out-of-range access.
- Be careful with floating-point stride values because decimal precision can be surprising.
11. Practice Exercise
Write a Swift program that does all of the following:
- Prints the multiples of 3 from 3 through 15.
- Prints a reverse countdown from 4 to 0.
- Stores an array of five strings and prints every second item using stride.
Expected output: Multiples of 3, then the countdown, then array elements at indexes 0, 2, and 4.
Hint: You will need one forward loop with through, one reverse loop with a negative step, and one array-index loop with to: items.count.
let words = ["red", "blue", "green", "yellow", "purple"]
print("Multiples of 3:")
for number in stride(from: 3, through: 15, by: 3) {
print(number)
}
print("Countdown:")
for number in stride(from: 4, through: 0, by: -1) {
print(number)
}
print("Every second word:")
for index in stride(from: 0, to: words.count, by: 2) {
print(words[index])
}12. Final Summary
Swift's stride functions give you precise control over iteration. They let you move forward or backward by a chosen amount, which makes them ideal for custom counting, sampling values, reverse loops, and index-based stepping. The most important distinction is that to excludes the end value, while through includes it when the step lands on it.
You also saw the most common pitfalls: choosing the wrong boundary, using the wrong step direction, indexing arrays incorrectly, and expecting floating-point values to behave exactly like decimal math on paper. If you practice reading loop boundaries carefully, stride becomes a simple and powerful part of everyday Swift. A good next step is to compare stride with standard ranges and while loops so you can choose the clearest loop style for each task.