MiddleCodeCommonNot answered yet
Build a search pipeline — debounce → removeDuplicates → switchToLatest — bound to the UI
You have a @Published var query: String and a search(_:) -> AnyPublisher<[Result], Never> API. Build a pipeline that reacts to typing and drives the UI.
Requirements:
- Debounce keystrokes by 300 ms and ignore repeated identical queries.
- Map each query to
search(_:)and keep only the latest request's results (cancel stale ones). - Deliver results on the main thread and assign them to
results.
final class SearchViewModel: ObservableObject {
@Published var query = ""
@Published var results: [Result] = []
private var cancellables = Set<AnyCancellable>()
func bind() {
// your code here — build the pipeline from $query
}
}
Write the implementation.
Chain $query.debounce(...), removeDuplicates(), then map each query to search(_:) and switchToLatest() so a newer query cancels the in-flight request. Finish with receive(on:) on main and assign(to: &$results).
- ✗Using
flatMapwhereswitchToLatestis needed, so stale results overwrite fresh ones - ✗Mutating UI state off the main thread without
receive(on:) - ✗Dropping
debounce, firing a request on every keystroke
- →Why does
switchToLatestcancel the previous request butflatMapdoes not? - →Where would you place
receive(on:)relative toassign?
Bind $query to the search and keep only the latest request:
func bind() {
$query
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
.removeDuplicates()
.map { [weak self] q in
self?.search(q) ?? Empty().eraseToAnyPublisher()
}
.switchToLatest()
.receive(on: DispatchQueue.main)
.assign(to: &$results)
}
switchToLatest cancels the in-flight request the moment a newer query arrives, so stale results never overwrite fresh ones. debounce coalesces keystrokes and removeDuplicates skips repeated queries, so the network is hit at most once per settled unique query.