How do you handle the search if you are searching among millions of records?
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 our hands dirty 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] in DispatchQueue.main.async(execute: { () -> Void in UIApplication.shared.isNetworkActivityIndicatorVisible = true }) FlickrDataManager().fetchPhotosForSearchText(searchText: searchText, onCompletion: { (error: NSError?, flickrPhotos: [FlickrPhoto]?) -> Void in DispatchQueue.main.async(execute: { () -> Void in UIApplication.shared.isNetworkActivityIndicatorVisible = false }) if error == nil { self?.searchResult = flickrPhotos! } else { self?.searchResult = [] if (error!.code == FlickrDataManager.Errors.invalidAccessErrorStatusCode) { DispatchQueue.main.async(execute: { () -> Void in self?.showErrorAlert() }) } } DispatchQueue.main.async(execute: { () -> Void in self?.title = searchText self?.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.
Originally published at https://medium.com/@coolanil.saini/how-to-write-async-searchbarcontroller-6e8e00082dbc