Preventing stale search results

Combine Senior Swift 5.1+ iOS 13+ Reviewed By Alok Singh

Spot the bug

Results sometimes show an older query. Tap the line that allows it.

  1. $query
  2. .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
  3. .removeDuplicates()
  4. .flatMap { query in api.searchPublisher(query) } // AnyPublisher<[Item], Never>
  5. .receive(on: RunLoop.main)
  6. .sink { [weak self] items in self?.results = items }
  7. .store(in: &cancellables)
Hint 1 of 1

What happens to the previous request when a new query arrives?

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

Show answer
  • Answer: line 4 .flatMap { query in api.searchPublisher(query) } // AnyPublisher<[Item], Never>

Line 4: flatMap keeps every request alive, so a slow older response can arrive last. Use .map { api.searchPublisher($0) }.switchToLatest() to cancel the previous request.

30-second answer

Debounce input, drop duplicates, and make sure only the latest query's response can be shown. In Combine, map each query to a request and use switchToLatest(), which cancels the previous request. In SwiftUI with async/await, .task(id: query) does the same: changing the id cancels the old task. Also ignore empty queries and show a loading state.

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

Explain how you prevent stale search results.

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

  • Debounce + removeDuplicates
  • switchToLatest or .task(id:)
  • Cancel old requests

Deep dive

flatMap keeps every inner request alive, so responses can arrive out of order.

Check Task.isCancelled before assigning results in async code.

Cache recent queries to make backspacing instant.

Handle errors inside the inner request so the pipeline survives.

Common mistakes

  • Using flatMap for search requests.
  • Assigning results without checking they match the current query.

Senior take & follow-ups

Seniors explain the out-of-order race and show both Combine and async solutions.

Follow-up questions

Think of your answer first, then open the hint.

  1. Why are results sometimes from an older query?

    Show hint

    An older, slower response arrived after a newer one.

  2. How does .task(id:) help?

    Show hint

    It cancels the previous task when the id changes.

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