.task(id:) and built-in debouncing

SwiftUI Mid-level Swift 5.7+ iOS 16+ Reviewed By Alok Singh

Predict the output

The user types s, sw, swi about 100 ms apart. How many API searches start?

.task(id: query) {
    try? await Task.sleep(for: .milliseconds(300))
    guard !Task.isCancelled else { return }
    results = await API.search(query)
}
Options
Hint 1 of 1

What happens to the old task when the id changes?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: B 1, for swi

Correct. The first two tasks are cancelled during their sleep and return at the guard.

Why not the others?
  • A 3. Each new id cancels the previous task while it is still sleeping.
  • C 0. The last task isn't cancelled, so its search runs.
  • D 2. Only the final query survives the 300 ms wait.

30-second answer

Each time the id changes, SwiftUI cancels the running task and starts a new one. If the task sleeps briefly before doing work, only the last change survives, which gives a debounce with no Combine. The pattern is: sleep, check cancellation, then load.

Practise saying it · 30 s. No recording, just a timer

Explain .task(id:) debouncing.

Say your answer out loud, then check which of these points you covered:

  • id change cancels and restarts
  • sleep then check cancellation
  • stale results never written

Deep dive

try await Task.sleep(for:) throws on cancellation, so with try the old task stops at once.

With try? you must check Task.isCancelled yourself.

The id must be Equatable; use a struct if several inputs matter.

This also prevents stale results: a cancelled task never writes its result.

Common mistakes

  • Swallowing cancellation and still writing stale results.
  • Using a Timer for debouncing in SwiftUI.
  • Making the id something that changes every render.

Senior take & follow-ups

Seniors explain that cancellation, not the sleep, is what prevents stale results.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you combine two inputs as the id?

    Show hint

    A small Equatable struct of both.

  2. How would you test this?

    Show hint

    Move the logic into a model method and test it with a clock.

Suggest a fix if something here is wrong or out of date.