Logo for tanaschita.com

How to delay server requests for user's search query with SwiftUI and Combine

Learn how to debounce on a practical example.

15 Jul 2024 · 5 min read

Search is a common feature in many applications. When a user types in a search query, we often want to avoid sending a new server request every time the query changes. Instead, we might want to delay it the request and only send it after the user has stopped typing for a certain amount of time.

This is where Combine's debounce operator comes in. It helps us achieve precisely this behavior. Let's see how we can implement it in combination with SwiftUI.

Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE

SwiftUI search implementation

When working with SwiftUI, our UI code for searching might look as follows:

struct SearchView: View {
@State private var userSearchInput: String = ""
var body: some View {
VStack {
// Show loading indicator, error, or results
}
.searchable(text: $userSearchInput)
.onChange(of: userSearchInput) { _, newValue in
viewModel.updateSearch(userSearchInput: newValue)
}
}
}
  1. We use SwiftUI's searchable modifier to configure the display of a search field.
  2. We use a @State variable to pass in as a binding to the searchable modifier. It will get updated automatically as the user changes the search query.
  3. We subscribe to changes of userSearchInput to notify our view model that it might need to refresh the results.

ViewModel debounce implementation

Next, let's look at how the view model might be implemented:

class SearchViewModel {
private var userSearchInputSubject = PassthroughSubject<String?, Never>()
private var cancellables = Set<AnyCancellable>()
init() {
userSearchInputSubject
.debounce(for: 0.5, scheduler: RunLoop.main)
.sink { [weak self] searchInput in
self?.reload(with: searchInput)
}
.store(in: &cancellables)
}
func updateSearch(userSearchInput: String?) {
userSearchInputSubject.send(userSearchInput)
}
}

By using the debounce operator, we ensure that the server requests are sent only when the user has stopped typing for half a second, reducing the number of unnecessary requests and improving the overall performance of our application.

Sponsorship logo
Preparing for a technical iOS job interview
Preparing for a technical iOS Job Interview with over 300 questions & answers. Covering Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, iOS File System, Core Data, Concurrency with async/await, Security, Automated Testing, Dependency Management, AI & Machine Learning and more.
LEARN MORE
Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE
Sponsorship logo
Become a sponsor of tanaschita.com
By publishing an article on different iOS topics every week, tanaschita.com is constantly growing in the developer community and may provide a great audience for you as a sponsor.
CLICK TO LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

combine

swift

ios

How to use a Connectable publisher in Combine

Learn how to prevent data loss by using Combine's ConnectablePublisher.

18 Nov 2024 · 3 min read

Latest articles and tips

© 2026 tanaschita.com

Privacy policy

Impressum