project focustimer

Step 3: Countdown

System Text-to-Speech Ready
Slide: 0:00 / 0:00
Slide 1 of 0Interactive Deck

Full Lesson Reference

Implement countdown logic with start, pause, and reset.

Where We Are

You have a good-looking timer UI. Now let's make it count down.

Add Timer State

terminal
Add state variables to track:
- Total duration in seconds (default: 25 * 60)
- Remaining seconds
- Whether the timer is running

Calculate the progress value (0.0 to 1.0) from remaining / total.
Use this to drive the ring's .trim(from:to:) value.

Start the Countdown

terminal
When the user taps Start:
- Create a Timer that fires every second
- Each tick, decrease remaining seconds by 1
- Update the progress ring with animation
- When remaining hits 0, stop the timer

Use Timer.publish(every: 1, on: .main, in: .common) and .onReceive.

The Start button should change to a Pause button (SF Symbol: "pause.fill") when the timer is running.

Animate the Ring

terminal
Add a smooth animation to the ring progress so it doesn't jump.
Use .animation(.linear(duration: 1)) on the trim value
so the ring smoothly decreases each second.

Pause and Reset

terminal
Pause: Stop the timer but keep the remaining time. The button
should switch back to "play.fill".

Reset: Stop the timer and set remaining time back to the full duration.
The ring should animate back to full.

Format the Time

terminal
Display the remaining time as "MM:SS" format.
- 25 minutes = "25:00"
- 1 minute 5 seconds = "01:05"
- 0 seconds = "00:00"

Always show two digits for both minutes and seconds.

Test It

  1. Run the app
  2. Tap Start — the ring should shrink smoothly, time counts down
  3. Tap Pause — it stops
  4. Tap Start again — it resumes
  5. Tap Reset — ring goes back to full, time resets to 25:00
  6. Let it run to 00:00 — it should stop automatically

Checkpoint

By now you should have:

  • Timer counts down from 25:00
  • Ring animates smoothly as time decreases
  • Start/Pause toggle works
  • Reset returns to full duration
  • Timer stops at 00:00

What You Learned

  • Timer.publish for periodic updates
  • SwiftUI animation with .animation()
  • State management for play/pause/reset
  • Time formatting with String(format:)

Exercises

  1. Add the timer state and countdown logic, then run the app and watch the ring shrink as the time counts down — confirm it stops automatically at 00:00.
  2. Test the full control flow: Start, Pause, Start again, Reset. Verify the play/pause icon toggles correctly each time.
  3. Temporarily change the default duration to a short value (e.g., 10 seconds) to watch the MM:SS formatting hit "00:09", "00:08" cleanly, then set it back to 25 minutes.