maggix maggix - 4 months ago 40
iOS Question

How to throttle search (based on typing speed) in iOS UISearchBar?

I have a UISearchBar part of a UISearchDisplayController that is used to display search results from both local CoreData and remote API.
What I want to achieve is the "delaying" of the search on the remote API. Currently, for each character typed by the user, a request is sent. But if the user types particularly fast, it does not make sense to send many requests: it would help to wait until he has stopped typing.
Is there a way to achieve that?

Reading the documentation suggests to wait until the users explicitly taps on search, but I don't find it ideal in my case.


Performance issues. If search operations can be carried out very
rapidly, it is possible to update the search results as the user is
typing by implementing the searchBar:textDidChange: method on the
delegate object. However, if a search operation takes more time, you
should wait until the user taps the Search button before beginning the
search in the searchBarSearchButtonClicked: method. Always perform
search operations a background thread to avoid blocking the main
thread. This keeps your app responsive to the user while the search is
running and provides a better user experience.


Sending many requests to the API is not a problem of local performance but only of avoiding too high request rate on the remote server.

Thanks

Answer

Thanks to this link, I found a very quick and clean approach. Compared to Nirmit's answer it lacks the "loading indicator", however it wins in terms of number of lines of code and does not require additional controls. I first added the dispatch_cancelable_block.h file to my project (from this repo), then defined the following class variable: __block dispatch_cancelable_block_t searchBlock;.

My search code now looks like this:

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
    if (searchBlock != nil) {
        //We cancel the currently scheduled block
        cancel_block(searchBlock);
    }
    searchBlock = dispatch_after_delay(searchBlockDelay, ^{
        //We "enqueue" this block with a certain delay. It will be canceled if the user types faster than the delay, otherwise it will be executed after the specified delay
        [self loadPlacesAutocompleteForInput:searchText]; 
    });
}

Notes:

  • The loadPlacesAutocompleteForInput is part of the LPGoogleFunctions library
  • searchBlockDelay is defined as follows outside of the @implementation:

    static CGFloat searchBlockDelay = 0.2;