App 21 — 24 Apps You Can Make Today

StopWatch

A working stopwatch with start, stop, and reset — Timer, elapsed time, and formatted display.

  • ios
↓ Download Playgrounds File

A stopwatch needs to count up on its own while displaying the time precisely. It also needs three buttons that work correctly regardless of the current state. This is a surprisingly rich little app.

What you'll build

A large elapsed time display and three buttons: Start, Stop, Reset. The display counts up while running, freezes when stopped, and resets to zero on demand.

Key concepts

Timer for steady updates — Fire a timer 100 times per second to track hundredths of a second.

@State var elapsed: TimeInterval = 0
@State var timer: Timer?

func start() {
    timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in
        elapsed += 0.01
    }
}

func stop() {
    timer?.invalidate()
    timer = nil
}

func reset() {
    stop()
    elapsed = 0
}

TimeInterval is Doubleelapsed is just a Double representing seconds. Hundredths of a second are decimals.

Formatted display — Convert elapsed into MM:SS.hh format for display.

var display: String {
    let minutes = Int(elapsed) / 60
    let seconds = Int(elapsed) % 60
    let hundredths = Int((elapsed * 100).truncatingRemainder(dividingBy: 100))
    return String(format: "%02d:%02d.%02d", minutes, seconds, hundredths)
}

Button state — Disable Start when running, disable Stop when not running, so the interface always makes sense.

Button("Start") { start() }
    .disabled(timer != nil)

Managing timer state

The timer variable being nil vs non-nil is itself state — it tells you whether the stopwatch is running. That's a cleaner signal than a separate isRunning: Bool because it's impossible for the timer to be running while timer == nil.

Download and run it

Open the Playgrounds file from GitHub, hit Run, and time something.

← All apps in this book