How to write Async SearchBarController

Anil Saini
2 min readJul 13, 2018

--

How do you handle the search if you are searching in millions of record?

What happens when a user enters some text to search and realised that he wanted to search something else or mistyped something?

Ideally, when a user changes the text we should stop our search and begin a search with the new text entered by a user.

How should we write such a code in swift to make sure we cancel the search before starting a new search?

OperationQueue is a good way to handle these kinds of situations. So, let’s get dirty our hands to write a piece of code for it.

We need a delegate which provides a set of APIs to use search functionality as below:

protocol SearchControllerDelegate: class {var searchQueue:OperationQueue {get set}var searchResult:[FlickrPhoto] {get set}func searchDisplayController(controller:SearchViewController, searchText:String)func cancelSearch()}

We just wrote a SearchControllerDelegate protocol which has properties and methods as:

Properties:

  • searchQueue to perform async search task
  • searchResult stores results returned from the search

Methods:

  • searchDisplayController(controller:SearchViewController, searchText:String) to start a search with the searchText
  • cancelSearch() to cancel the search

Here, how do we use delegate protocol in our ViewController:

extension SearchViewController {func searchDisplayController(controller:SearchViewController, searchText:String) {guard !searchText.isEmpty else {self.searchResult.removeAll()return}self.searchQueue.cancelAllOperations()self.searchQueue.addOperation { [weak self] inDispatchQueue.main.async(execute: { () -> Void inUIApplication.shared.isNetworkActivityIndicatorVisible = true})FlickrDataManager().fetchPhotosForSearchText(searchText: searchText, onCompletion: { (error: NSError?, flickrPhotos: [FlickrPhoto]?) -> Void inDispatchQueue.main.async(execute: { () -> Void inUIApplication.shared.isNetworkActivityIndicatorVisible = false})if error == nil {self?.searchResult = flickrPhotos!} else {self?.searchResult = []if (error!.code == FlickrDataManager.Errors.invalidAccessErrorStatusCode) {DispatchQueue.main.async(execute: { () -> Void inself?.showErrorAlert()})}}DispatchQueue.main.async(execute: { () -> Void inself?.title = searchTextself?.tableView.reloadData()})})}}func cancelSearch() {self.searchQueue.cancelAllOperations()self.searchResult.removeAll()self.title = “”}}

How to connect SearchControllerDelegate to UI:

class SearchViewController: UIViewController, SearchControllerDelegate, UISearchBarDelegate {var searchQueue: OperationQueue = OperationQueue()var searchResult: [FlickrPhoto] = []@IBOutlet weak var searchBar: UISearchBar!@IBOutlet weak var tableView: UITableView!func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {self.cancelSearch()
self.searchDisplayController(controller: self, searchText: searchBar.text!)
}func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {self.cancelSearch()}func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {self.searchDisplayController(controller: self, searchText: searchBar.text!)}

A simple example of the async search can be found here.

--

--