App 23 — 24 Apps You Can Make Today

Tortoise and Hare

Animate a race between two characters — Timer-driven progress, withAnimation, and offset.

  • ios
↓ Download Playgrounds File

The tortoise is slow and steady. The hare is fast but unreliable. This app races them to the finish — and the result is always surprising. It's a beautiful showcase of animation driving directly from state.

What you'll build

Two racers on a track, each advancing at their own pace. A start button kicks off the race. Whoever reaches the end first wins. The hare might nap; the tortoise keeps going.

Key concepts

Progress as state — Each racer's position is just a Double from 0.0 to 1.0.

@State var tortoiseProgress: Double = 0
@State var hareProgress: Double = 0

withAnimation — Wrap state changes in withAnimation to let SwiftUI smoothly interpolate between old and new values.

Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
    withAnimation(.linear(duration: 0.5)) {
        tortoiseProgress += 0.04
        hareProgress += Double.random(in: -0.02...0.12)  // the hare is erratic
    }
    if tortoiseProgress >= 1 || hareProgress >= 1 { timer.invalidate() }
}

.offset() driven by progress — Map each racer's 0–1 progress value to a horizontal position using GeometryReader or a fixed track width.

Text("🐢")
    .offset(x: tortoiseProgress * trackWidth)

Win detection — Check progress values each tick. The first to reach 1.0 wins.

Animations are interpolation

When you change a value inside withAnimation, SwiftUI doesn't jump to the new value — it smoothly moves from the old value to the new one over the specified duration. That's all animation is: smooth interpolation between states.

Download and run it

Open the Playgrounds file from GitHub, hit Run, and watch the race.

← All apps in this book