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.

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.

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:

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

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

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

11. Practice Exercise

Write a Swift program that does all of the following:

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.