I have a UISearchController which is configured to search a very large array of data. As such, when I am typing in the search bar, it takes a very long time to actually type out my search. It performs the search comparison with every character entry in the search field, which is very slow.
I am wondering how to fix this. My thoughts are:
I would like to use method 1 but I can't seem to figure out how to do this with the new UISearchController.
Below is my relevant project code:
class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating{
var airportData = [Dictionary<String, String>]()
var filteredData = [Dictionary<String, String>]()
var searchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
searchController.hidesNavigationBarDuringPresentation = false
self.tableView.reloadData()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
filteredData = []
let searchPredicate = NSPredicate(format: "SELF CONTAINS[cd] %@", searchController.searchBar.text!)
let array = (airportData as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredData = array as! [Dictionary<String, String>]
self.tableView.reloadData()
}
Bonus question If I search for a string, it doesn't appear to be returning any results if the string doesn't match perfectly. For example: "orida" does not find "Florida". Isn't my search predicate supposed to find this using CONTAINS?
Update This code nearly works, but it basically throws a bunch of stuff on the background thread and then chugs through it. The keyboard is lively now, but it seems to crash if I change things in the text field quickly on it while dismissing and re-entering the search bar...
func updateSearchResultsForSearchController(searchController: UISearchController) {
appDel.backgroundThread(background: {
self.filteredData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[cd] %@", searchController.searchBar.text!)
let array = (self.airportData as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredData = array as! [Dictionary<String, String>]
},
completion: {
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
});
}
Update 2 After playing a bit with it, I was able to get it to work pretty decently by both waiting for a searchBar.text length >= 3 characters as well as making sure the character count didn't change within 1 second of the updateSearchResultsForSearchController: being called. The combination of these as well as integrating a search button execute command should resolve my problem.
func updateSearchResultsForSearchController(searchController: UISearchController) {
let startCount = searchController.searchBar.text!.length
delay(1) {
if searchController.searchBar.text!.length >= 3 && searchController.searchBar.text!.length == startCount{
self.view.addSubview(self.progressHud)
self.appDel.backgroundThread(background: {
self.filteredData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[cd] %@", searchController.searchBar.text!)
let array = (self.airportData as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredData = array as! [Dictionary<String, String>]
},
completion: {
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.progressHud.removeFromSuperview()
}
});
}
}
}
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
You can use the delegate method searchBarSearchButtonPressed:
to monitor when the search button is pressed. Set a flag when this delegate method is executed and then in your searchBarTextDidChange:
method you can check this flag to see if you should execute the search. Of course this will only search after the user presses search, and most people expect to see something happening while they're typing.