App 23 — 24 Apps You Can Make Today
Tortoise and Hare
Animate a race between two characters — Timer-driven progress, withAnimation, and offset.
- ios
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.