mlevi
mlevi

Reputation: 1463

Filtering Core Data for UISearchBar with Swift

I have a UISearchBar thats hooked up to a table view thats populated by Core Data. I've been having a lot of trouble getting filtering to work. Most tutorials on this are extremely old and haven't worked for me. I was thinking about converting the entity to an array and doing the filtering on the array but I've read that that's inefficient. I'm thinking I should use NSPredicate, but I honestly don't know what to do. Any ideas? Thanks.

Upvotes: 2

Views: 3073

Answers (2)

krisacorn
krisacorn

Reputation: 831

As of iOS 8, the searchDisplayController is deprecated.

Upvotes: 2

Ian
Ian

Reputation: 12768

I've actually been working on a demo of this and I have just put it up on GitHub. It can be found here. As far as the implementation of it goes, you have to set up UITableViewController, NSFetchedResultsController, and UISearchDisplayDelegate for your class:

class ContactsViewController: UITableViewController, NSFetchedResultsController, UISearchDisplayDelegate {
}

And with this there will be two table views:

  • Your tableView from the tableViewController (self.tableView)
  • Your tableView from searchDisplayController (searchDisplayController?.searchResultsTableView)

    You also make class variables for fetchedResultsController, searchResultsController, appDelegate, and managedObjectContext:

    let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
    
    var managedObjectContext: NSManagedObjectContext? {
        get {
            if let delegate = appDelegate {
                return delegate.managedObjectContext
        }
        return nil
        }
    }
    
    var fetchedResultsController: NSFetchedResultsController?
    var searchResultsController: NSFetchedResultsController?
    

    In the viewDidLoad() you must register your cell for your searchResultsTableView because it does not exist in interface:

    searchDisplayController?.searchResultsTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "CellID")
    

    It is also here that you setup your fetchRequest:

    fetchedResultsController = NSFetchedResultsController(fetchRequest: someFetchRequest, managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
            fetchedResultsController?.delegate = self
            fetchedResultsController?.performFetch(nil)
    

    You will need a function that returns which FRC you will need depending on the tableView. You make this function return a NSFetchedResultsController and you will use this in all of the table view functions where you pull data from the FRC such as cellForRowAtIndexPath: because it will get you the correct set of data

    func fetchedResultsControllerForTableView(tableView: UITableView) -> NSFetchedResultsController? {
        return tableView == searchDisplayController?.searchResultsTableView ? searchResultsController? : fetchedResultsController?
    }
    

    Finally, you need to implement searchDisplayControllerWillUnloadSearchResults and searchDisplayControllerShouldReloadTableForSearchString:

    func searchDisplayController(controller: UISearchDisplayController, willUnloadSearchResultsTableView tableView: UITableView) {
            searchResultsController?.delegate = nil
            searchResultsController = nil
        }
    
    func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
    
        let firstNamePredicate = NSPredicate(format: "nameFirst CONTAINS[cd] %@", searchString.lowercaseString)
        let lastNamePredicate = NSPredicate(format: "nameLast CONTAINS[cd] %@", searchString.lowercaseString)
        let predicate = NSCompoundPredicate.orPredicateWithSubpredicates([firstNamePredicate!, lastNamePredicate!])
    
        searchResultsController = NSFetchedResultsController(fetchRequest: someOtherFetchRequestWithPredicate(predicate), managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
        searchResultsController?.performFetch(nil)
    
            return true
        }
    

    If you have any trouble filling in the other stuff, such as the data model or the NSFetchedResultsControllerDelegate methods, check out the demo!

    Upvotes: 2

  • Related Questions