UISearchController shows incorrect results

421 views Asked by At

I have application with UITableViewController, two UIViewController and Core Data. I want to make UISearchController for the first time. I looked on the other questions by this theme, but I have working incorrect UISearchController. When I write into searchBar, it give incorrect results. And I don't know how show them in the table view cell. Please help me.

My code in the UITableViewController

var resultSearchController: UISearchController!
var searchPredicate: NSPredicate?
var filteredObjects: [Note]? = nil

override func viewDidLoad() {
    super.viewDidLoad()

    self.navigationItem.leftBarButtonItem = self.editButtonItem()

    fetchedResultsController = NSFetchedResultsController(fetchRequest: allEmployeesFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: "mynote", cacheName: "mynote") // both mynote
    fetchedResultsController?.delegate = self
    fetchedResultsController?.performFetch(nil)

    // UISearchController setup
    self.resultSearchController = ({
        let controller = UISearchController(searchResultsController: nil)
        controller.delegate = self
        controller.searchResultsUpdater = self
        controller.hidesNavigationBarDuringPresentation = false
        controller.dimsBackgroundDuringPresentation = true
        controller.searchBar.sizeToFit()
        controller.searchBar.delegate = self
                    self.tableView.tableHeaderView = controller.searchBar
        return controller
    })()
    self.tableView.reloadData()
}

func updateSearchResultsForSearchController(searchController: UISearchController) {
    let searchText = self.resultSearchController.searchBar.text
    println(searchText)
    if let searchText = searchText {
       searchPredicate = NSPredicate(format: "mynote contains[c] %@", searchText)
        filteredObjects = self.fetchedResultsController?.fetchedObjects?.filter() {
            return self.searchPredicate!.evaluateWithObject($0)
        } as! [Note]?
        self.tableView.reloadData()
        println(searchPredicate)
    }
}

and

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    if searchPredicate == nil {
        return fetchedResultsController?.sections?.count ?? 0
    } else {
        return 1
    }
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if searchPredicate == nil {
        return fetchedResultsController?.sections?[section].numberOfObjects ?? 0 //0 default
    } else {
        return filteredObjects?.count ?? 0
    }
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("CellNote", forIndexPath: indexPath) as! UITableViewCell
    if searchPredicate == nil {
        if var cellContact = fetchedResultsController?.objectAtIndexPath(indexPath) as? Note {
            cell.textLabel?.text = cellContact.mynote
            cell.detailTextLabel?.text = cellContact.mytime
            return cell
        }
    }
    return cell
}

My core data class has

import Foundation
import CoreData
@objc(Note)
class Note: NSManagedObject {

    @NSManaged var mynote: String
    @NSManaged var mytime: String

}
2

There are 2 answers

0
Alexander Khitev On BEST ANSWER

I found a solve, I changed my UITableViewController

class FirstTableViewController: UITableViewController, NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate
{
@IBAction func addNew(sender: UIBarButtonItem) {

}

let managedObjectContext: NSManagedObjectContext? = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext

var fetchedResultsController: NSFetchedResultsController?


// MARK: - setting for search controller
var searchController: UISearchController!
var searchPredicate: NSPredicate!
var filteredObjects: [Note]? = nil
var filteredDataFromObjects = [String]()

override func viewDidLoad() {
    super.viewDidLoad()

    self.navigationItem.leftBarButtonItem = self.editButtonItem()

    fetchedResultsController = NSFetchedResultsController(fetchRequest: allEmployeesFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: "mynote", cacheName: "mynote") // both mynote
    fetchedResultsController?.delegate = self
    fetchedResultsController?.performFetch(nil)

    self.searchController = ({
        var controllerS = UISearchController(searchResultsController: nil)
        controllerS.delegate = self
        controllerS.searchBar.delegate = self
        controllerS.searchResultsUpdater = self
        controllerS.searchBar.sizeToFit()
        controllerS.dimsBackgroundDuringPresentation = false
        controllerS.hidesNavigationBarDuringPresentation = false
     //   self.definesPresentationContext = true
        self.tableView.tableHeaderView = controllerS.searchBar
        return controllerS
    })()
}

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.tableView.reloadData()
}

func updateSearchResultsForSearchController(searchController: UISearchController) {
    filteredDataFromObjects.removeAll(keepCapacity: false)
    var searchText = searchController.searchBar.text
    if var searhText = searchText {
        searchPredicate = NSPredicate(format: "mynote contains[c] %@", searchText)
        filteredObjects = self.fetchedResultsController?.fetchedObjects?.filter() {
            return self.searchPredicate.evaluateWithObject($0)
        } as! [Note]?
        self.tableView.reloadData()
        println("Search Predicate \(searchPredicate)")
    }

}


func allEmployeesFetchRequest() -> NSFetchRequest {

    var fetchRequest = NSFetchRequest(entityName: "Note")

    let sortDescriptor = NSSortDescriptor(key: "mytime", ascending: false) // change on date

    fetchRequest.predicate = nil
    fetchRequest.sortDescriptors = [sortDescriptor]
    fetchRequest.fetchBatchSize = 20

    return fetchRequest
}

//MARK: UITableView Data Source and Delegate Functions
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    if searchPredicate == nil {
        return fetchedResultsController?.sections?.count ?? 0 //after 0
    } else {
        return 1 ?? 0
    }
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if searchPredicate == nil {
        return fetchedResultsController?.sections?[section].numberOfObjects ?? 0 //0 default
    } else {
        return filteredObjects?.count ?? 0
    }
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("CellNote", forIndexPath: indexPath) as! UITableViewCell
    if searchPredicate == nil {
        if var cellContact = fetchedResultsController?.objectAtIndexPath(indexPath) as? Note {
            cell.textLabel?.text = cellContact.mynote
            cell.detailTextLabel?.text = cellContact.mytime
        }
    } else {
        if var textFromFiltered = filteredObjects?[indexPath.row] {
            cell.textLabel?.text = textFromFiltered.mynote
            cell.detailTextLabel?.text = textFromFiltered.mytime
        }
    }
    return cell
}

//MARK: NSFetchedResultsController Delegate Functions
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

    var tableView = UITableView()
    if searchPredicate == nil {
        tableView = self.tableView
    } else {
        tableView = (searchController.searchResultsUpdater as! FirstTableViewController).tableView
    }

    switch type {
    case NSFetchedResultsChangeType.Insert:
        tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Delete:
        tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Move:
        break
    case NSFetchedResultsChangeType.Update:
        break
    default:
        break
    }
}

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
    }
    switch editingStyle {
    case .Delete:
    managedObjectContext?.deleteObject(fetchedResultsController?.objectAtIndexPath(indexPath) as! Note)
        managedObjectContext?.save(nil)
    case .Insert:
        break
    case .None:
        break
    }

}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
    // I made test which controller is need to show
    var tableView = UITableView()
    if searchPredicate == nil {
        tableView = self.tableView
    } else {
        tableView = (searchController.searchResultsUpdater as! FirstTableViewController).tableView
    }

    switch type {
    case NSFetchedResultsChangeType.Insert:
        tableView.insertRowsAtIndexPaths([AnyObject](), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Delete:
        tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath!) as [AnyObject], withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Move:
        tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath!) as [AnyObject], withRowAnimation: UITableViewRowAnimation.Fade)
        tableView.insertRowsAtIndexPaths(NSArray(object: newIndexPath!) as [AnyObject], withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Update:
        tableView.cellForRowAtIndexPath(indexPath!)
        break
    default:
        break
    }
}

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    if searchPredicate == nil {
        tableView.beginUpdates()
    } else {
        (searchController.searchResultsUpdater as? FirstTableViewController)?.tableView.beginUpdates()
    }
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    if searchPredicate == nil {
        tableView.endUpdates()
    } else {
        (searchController.searchResultsUpdater as? FirstTableViewController)?.tableView.endUpdates()
    }
}

@IBAction func unwindToFirst(segue: UIStoryboardSegue) {

}

// for sent data in detail controller 
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if segue.identifier == "showDetail" {
        if let indexPath = self.tableView.indexPathForSelectedRow() {
            if searchPredicate == nil {
            let object = self.fetchedResultsController!.objectAtIndexPath(indexPath) as! NSManagedObject
            let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController //DetailViewController
                controller.detailItem = object
            } else {
                let object = filteredObjects![indexPath.row] as Note
                let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
                controller.detailItem = object
            }
        }
        self.searchController.active = false
    }
}

func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
    let object = self.fetchedResultsController!.objectAtIndexPath(indexPath) as! NSManagedObject
    cell.textLabel!.text = object.valueForKey("mynote")!.description
}


// MARK: - UISearch functions 
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
    updateSearchResultsForSearchController(searchController)
}

func didDismissSearchController(searchController: UISearchController) {
    filteredObjects = nil
    searchPredicate = nil
    tableView.reloadData()
 }
}
1
Julio Bailon On

I have a class that I use for multiple queries where I just pass a predicate, sometimes a sort and returns me an array of objects of the entity class.

class QueryDatabase: NSObject {
    private var _managedObjectContext: NSManagedObjectContext?

    init(managedObjectContext: NSManagedObjectContext){
       super.init()
       _managedObjectContext = managedObjectContext
    }

    func QueryTable(entityName: String, sortBy: String?, ascending: Bool, searchPredicate: NSPredicate?) -> AnyObject?{
        if _managedObjectContext != nil {
            let fetchRequest = NSFetchRequest(entityName: entityName)
            if(sortBy != nil){
                let sort = NSSortDescriptor(key: sortBy!, ascending: ascending)
                fetchRequest.sortDescriptors = [sort]
            }

            if(searchPredicate != nil){
                fetchRequest.predicate = searchPredicate!
            }

            var err: NSError?
            if let fetchResults = _managedObjectContext!.executeFetchRequest(fetchRequest, error: &err){
                return fetchResults
            }else{
                NSLog("ERROR:\(err?.description)")
            }
        }
        return nil
    }
}

I have used it in conjunction with a searchBar but in my case my predicate adds another modifier [cd] on its definition:

func findPricesBySearchCriteria(searchCriteria: String) -> [Prices]{
    if(_managedObjectContext != nil){
        let queryDatabase = QueryDatabase(managedObjectContext: _managedObjectContext!)
        if let results = queryDatabase.QueryTable("Prices", sortBy: "item_description", ascending: true, searchPredicate:  NSPredicate(format: "item_description contains[cd] %@", searchCriteria)) as? [Prices]{
            arrSearch = results
        }
    }
    return arrSearch
}