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

Using stride(from:to:by:)

This form starts at one value, moves by a fixed amount, and stops before reaching the end value.

Swift
for number in stride(from: 0, to: 10, by: 2) {
    print(number)
}
Output✓ Tested · Swift 6.4
0
2
4
6
8

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.

Swift
for number in stride(from: 0, through: 10, by: 2) {
    print(number)
}
Output✓ Tested · Swift 6.4
0
2
4
6
8
10

This prints 0, 2, 4, 6, 8, and 10.

Using a negative step

To count backward, the step must be negative.

Swift
for countdown in stride(from: 5, through: 1, by: -1) {
    print(countdown)
}
Output✓ Tested · Swift 6.4
5
4
3
2
1

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.

Swift
for value in stride(from: 0, to: 10, by: 2) {
    print(value)
}
Output✓ Tested · Swift 6.4
0
2
4
6
8

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.

Swift
for value in stride(from: 0, through: 10, by: 5) {
    print("Checkpoint: \(value)")
}
Output✓ Tested · Swift 6.4
Checkpoint: 0
Checkpoint: 5
Checkpoint: 10

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.

Swift
for seconds in stride(from: 10, through: 0, by: -1) {
    print("\(seconds)...")
}
print("Go!")
Output✓ Tested · Swift 6.4
10...
9...
8...
7...
6...
5...
4...
3...
2...
1...
0...
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.

Swift
for temperature in stride(from: 18.0, through: 20.0, by: 0.5) {
    print("Set to \(temperature)°C")
}
Output✓ Tested · Swift 6.4
Set to 18.0°C
Set to 18.5°C
Set to 19.0°C
Set to 19.5°C
Set to 20.0°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.

Swift
let names = ["Ana", "Ben", "Chris", "Dina", "Eli"]

for index in stride(from: 0, to: names.count, by: 2) {
    print(names[index])
}
Output✓ Tested · Swift 6.4
Ana
Chris
Eli

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.

Swift
for number in stride(from: 0, to: 10, by: 2) {
    print(number)
}
Output✓ Tested · Swift 6.4
0
2
4
6
8

Fix: Use through when the ending value should be included if the sequence lands on it.

Swift
for number in stride(from: 0, through: 10, by: 2) {
    print(number)
}
Output✓ Tested · Swift 6.4
0
2
4
6
8
10

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.

Swift
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.

Swift
for number in stride(from: 10, through: 0, by: -1) {
    print(number)
}
Output✓ Tested · Swift 6.4
10
9
8
7
6
5
4
3
2
1
0

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.

Swift
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.

Swift
let items = ["A", "B", "C"]

for index in stride(from: 0, to: items.count, by: 1) {
    print(items[index])
}
Output✓ Tested · Swift 6.4
A
B
C

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.

Swift
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.

Swift
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")
    }
}
Output✓ Tested · Swift 6.4
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.

Swift
// 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)
}
Output✓ Tested · Swift 6.4
0
3
6
9
0
3
6
9
12

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.

Swift
// 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)
}
Output✓ Tested · Swift 6.4
0
20
40
60
80
100
100
80
60
40
20
0

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.

Swift
// 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)
}
Output✓ Tested · Swift 6.4
0
2
4
6
8
10
12
14
16
18
20
0
2
4
6
8
10
12
14
16
18
20

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.

Swift
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!")
Output✓ Tested · Swift 6.4
Quarter-hour marks:
Minute 0
Minute 15
Minute 30
Minute 45
Minute 60

Countdown:
5
4
3
2
1
0
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.

Show solution
Swift
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])
}
Output✓ Tested · Swift 6.4
Multiples of 3:
3
6
9
12
15
Countdown:
4
3
2
1
0
Every second word:
red
green
purple

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.