I am looking for a decent solution to this problem. I am wanting to implement some simple search functionality on a TableView that I have.
All the examples I have found
Swift 4.2
This is a working solutions from one of my app. I have trimmed it down to make it simple to show how it works.
I have a database of around 6000 rows in which I search via 3 different scopes: Notes, Author and Keywords. I call initializeFetchedResultsController
in the viewDidLoad
function with default values. And later when user starts typing in the Search field, start calling it again with the required value.
The Fetch part:
let EMPTY_STRING = "" // I don't like string literals in my code so define them as static variables separately
// Giving two default values
func initializeFetchedResultsController(_ text: String: EMPTY_STRING, _ scope: Int = 0) {
fetchRequest.sortDescriptors = [NSSortDescriptor(key: NotesAttributes.author.rawValue, ascending: true)]
if searchedStringTemp != EMPTY_STRING { // Whatever conditions you want to pass on
let p0 = NSPredicate(format: NotesAttributes.scope.rawValue + " != \(scope)")
let p1 = NSPredicate(format: "\(column) CONTAINS[cd] %@", "\(text)")
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p0, p1])
}
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: AppDelegate().sharedInstance().persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("Error 12312: Unable to Perform Fetch Request")
print("\(fetchError), \(fetchError.localizedDescription)")
}
}
The search controller:
// MARK: - UISearchBar Delegate
extension AllNotesVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
// This is my function which I call to start search
notesSearched(searchBar.text!, searchBar.scopeButtonTitles![selectedScope])
}
}
// MARK: - UISearchResultsUpdating Delegate
extension AllNotesVC: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let text = searchController.searchBar.text!
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
// This is my function which I call to start search
notesSearched(text, scope)
}
}
And this in my notesSearched
method which re-initialize the fetch results controller and reload the table every time.
// MARK: - Private instance methods
private func notesSearched(_ text: String, _ scope: Int) {
initializeFetchedResultsController(text, scope)
tableView.reloadData()
}
While calling doing so many table reloads might not be the most efficient way to do this, but it is lightening fast, and since this updates the table in real-time as user is typing it provides a wonderful user experience.
From your code, I assume you want to use the same table view to display the results. So you just need to update your FRC with a new filter based on the search term.
Store the search term in a variable. In the FRC factory function, include the predicate, something like this:
request.predicate = searchText?.characters.count > 0 ?
NSPredicate(format:"title contains[cd] %@", searchText!) : nil
When the text changes, reset the FRC and reload.
fetchedResultsController = nil
tableView.reloadData()
If you have additional filters, such as scope buttons, add additional terms to the predicate.