When building User interfaces for mobile, we constantly have to juggle responsiveness of the app and actual work that needs to be performed as a result of a particular user interaction. An example of this is a search functionality that queries a remote server, and displays the results in a table view. In this post, we will use the concept of a debouncer to keep the app responsive, while ensuring that we make as few requests as required to retrieve up to date search results for the user.
What is Debouncing?
Debouncing refers to the means of limiting the frequency of execution of a function. This is usually used when the function is question has a high cost of execution attributed to it. A debouncer therefore is an abstraction that handles debouncing a particular function. It is made up of two components, a delay/timeout and the function to be invoked after the delay (the debounced function). Let’s see the representation in code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
In the code above, the
call function can be invoked multiple times, but the actual
handler will not be executed until the duration of the
delay duration is exceeded. As a preference, I’ve opted to make the
handler mutable so that we can easily update the underlying debounced function without having to create a new debouncer. We can now use this to implement our aforementioned search functionality.
Responsive & Efficient Searching
Let’s imagine we have a search service that has the following API:
1 2 3
This service is the abstraction in charge of querying our remote service with the search query, and returning the results in a callback (We will assume our result type to be a
When the user is typing in the search field, we don’t want to send requests for each time the text changes as this could be very wasteful. We can use our
Debouncer defined above to limit the frequency at which we invoke the search API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
textFieldDidChange, we first initialize the
searchDebouncer if one
didn’t exist already using a
guard let syntax. Next, we then
invalidate the current debouncer, preventing it
from firing the old handler while we are in the middle of updating it. Then, we
update then debouncer’s handler with a new handler that will invoke the search
service with the new value of the
searchTextfield. Finally, we invoke the
method on the debouncer which will schedule the handler to be invoked after
the user stops typing for
1 second. 🚀
Using a debouncer, we were able to implement an efficient search functionality. The debouncer is a powerful tool that should always be considered when juggling responsiveness and executing an expensive operation that the user can trigger multiple times. I hope this article helps you understand the concept of debouncing and how it can be used in iOS.
Thanks for taking the time 🙏🏿