Search code examples
swiftcore-datauisearchcontroller

UISearchController returns only first results but Core Data has several results


I have UISearchController and it returns only first results but Core Data has several results.

I did many different variants but they didn't help me.

Else UISearchController returns incorrect results.

import UIKit
import Foundation
import CoreData

class SongTableVC: UITableViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate, UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {

    // MARK: - var and let
    var appDel = (UIApplication.sharedApplication().delegate as! AppDelegate)
    var context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!


    // MARK: - NSFetchedResultsController and its functions
    var fetchedResultsController: NSFetchedResultsController!

    func returnRequest() -> NSFetchRequest {
        var fetchRequest = NSFetchRequest(entityName: "Song")
        var sort = NSSortDescriptor(key: "songName", ascending: false)
        fetchRequest.fetchBatchSize = 50
        fetchRequest.predicate = nil
        fetchRequest.sortDescriptors = [sort]
        return fetchRequest
    }

    // MARK: - UISearchController and its fucntions
    var searchController: UISearchController!
    var searchPredicate: NSPredicate!
    var dataFiltered: [Song]? = nil

    func updateSearchResultsForSearchController(searchController: UISearchController) {
        var searchText = searchController.searchBar.text
        searchPredicate = NSPredicate(format: "songName contains[c] %@", searchText)
        dataFiltered = self.fetchedResultsController?.fetchedObjects?.filter(){
            return self.searchPredicate!.evaluateWithObject($0)
        } as! [Song]?
        self.tableView.reloadData()
        println(searchPredicate)
    }

    func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
        updateSearchResultsForSearchController(searchController)
    }

    func didDismissSearchController(searchController: UISearchController) {
        searchPredicate = nil
        dataFiltered = nil
        self.tableView.reloadData()
    }



    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationItem.leftBarButtonItem = self.editButtonItem()

        fetchedResultsController = NSFetchedResultsController(fetchRequest: returnRequest(), managedObjectContext: context, sectionNameKeyPath: "songName", cacheName: "songName")
        fetchedResultsController.delegate = self
        fetchedResultsController.performFetch(nil)

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

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Potentially incomplete method implementation.
        // Return the number of sections.
        if searchPredicate == nil {
            return fetchedResultsController?.sections?.count ?? 0
        } else {
            return 1 ?? 0
        }
    }

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

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("songID", forIndexPath: indexPath) as! UITableViewCell

        if searchPredicate == nil {
            if var dataForCell = fetchedResultsController.objectAtIndexPath(indexPath) as? Song {
                cell.textLabel?.text = dataForCell.songName
                cell.detailTextLabel?.text = dataForCell.songName
            } else {
                if var dataFilterForCell = dataFiltered?[indexPath.row] {
                    cell.textLabel?.text = dataFilterForCell.songName
                    cell.textLabel?.text = dataFilterForCell.songName
                }
            }
        }
        return cell
    }


    // Override to support conditional editing of the table view.
    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return NO if you do not want the specified item to be editable.
        return true
    }

    // Override to support editing the table view.
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            context.deleteObject(fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)
            context.save(nil)
        } else if editingStyle == .Insert {
            context.insertObject(fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)
            context.save(nil)
        }    
    }

    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.searchResultsController as! SongTableVC).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:
            tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
            tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
            break
        case NSFetchedResultsChangeType.Update:
            break
        default: break
        }
    }

    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
        var tableView = UITableView()
        if searchPredicate == nil {
            tableView = self.tableView
        } else {
            tableView = (searchController.searchResultsController as! SongTableVC).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: indexPath!) 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.searchResultsController as? SongTableVC)?.tableView.beginUpdates()
        }
    }

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

    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "add" {
            searchController.active == false

        }
    }

}

enter image description here enter image description here


Solution

  • I found a solution of my question

    import UIKit
    import Foundation
    import CoreData
    
    class SongTableViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate, UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
    
        // MARK: - var and lets
        var appDel = (UIApplication.sharedApplication().delegate as! AppDelegate)
        var context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: context, sectionNameKeyPath: "nameSong", cacheName: "nameSong")
            fetchedResultsController.delegate = self
            fetchedResultsController.performFetch(nil)
    
            self.navigationItem.leftBarButtonItem = self.editButtonItem()
    
            searchController = ({
                var controllerSearch = UISearchController(searchResultsController: nil)
                controllerSearch.delegate = self
                controllerSearch.searchBar.delegate = self
                controllerSearch.hidesNavigationBarDuringPresentation = true
                controllerSearch.definesPresentationContext = false
                controllerSearch.dimsBackgroundDuringPresentation = false
                controllerSearch.searchBar.sizeToFit()
                controllerSearch.searchResultsUpdater = self
                self.tableView.tableHeaderView = controllerSearch.searchBar
                return controllerSearch
            })()
    
            // 
            println(path)
        }
    
        override func viewDidAppear(animated: Bool) {
            super.viewDidAppear(animated)
            searchPredicate = nil
            filteredData = nil
            self.tableView.reloadData()
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        // MARK: - NSFetchedResultsController and its functions
        var fetchedResultsController: NSFetchedResultsController!
    
        func fetchRequest() -> NSFetchRequest {
            var fetchRequest = NSFetchRequest(entityName: "Song")
            var sort = NSSortDescriptor(key: "nameSong", ascending: false)
            fetchRequest.fetchBatchSize = 50
            fetchRequest.predicate = nil
            fetchRequest.sortDescriptors = [sort]
            return fetchRequest
        }
    
        // MARK: - UISearchController and its functions
        var searchController: UISearchController!
        var searchPredicate: NSPredicate!
        var filteredData: [Song]? = nil
    
        func updateSearchResultsForSearchController(searchController: UISearchController) {
            var searchText = searchController.searchBar.text
            if searchText != nil {
                searchPredicate = NSPredicate(format: "nameSong contains[c] %@", searchText)
                filteredData = fetchedResultsController.fetchedObjects!.filter() {
                    return self.searchPredicate.evaluateWithObject($0)
                } as? [Song]
                self.tableView.reloadData()
            }
        }
    
        func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
            updateSearchResultsForSearchController(searchController)
        }
    
        func didDismissSearchController(searchController: UISearchController) {
            searchPredicate = nil
            filteredData = nil
            self.tableView.reloadData()
        }
    
        // MARK: - Files from shared folder
    
        var fileManager = NSFileManager.defaultManager()
    
    
        var path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
    
    
    
        // MARK: - Table view data source
    
        override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
            if searchPredicate == nil {
                return fetchedResultsController?.sections?.count ?? 0
            } else {
                return 1 ?? 0
            }
        }
    
        override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            if searchPredicate == nil {
                return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
            } else {
                return filteredData?.count ?? 0
            }
        }
    
        override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCellWithIdentifier("songID", forIndexPath: indexPath) as! UITableViewCell
    
            if searchPredicate == nil {
                if var dataForCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? Song {
                    cell.textLabel?.text = dataForCell.nameSong
                }
            } else {
                if var filteredSearch = filteredData?[indexPath.row] {
                    cell.textLabel?.text = filteredSearch.nameSong
                }
            }
    
            return cell
        }
    
        // Override to support conditional editing of the table view.
        override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
            // Return NO if you do not want the specified item to be editable.
            return true
        }
    
        // Override to support editing the table view.
        override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
            if editingStyle == .Delete {
                context.deleteObject(fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)
                context.save(nil)
            } else if editingStyle == .Insert {
                context.insertObject(fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)
                context.save(nil)
            }    
        }
    
        func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
            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:
                tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
                tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
                break
            case NSFetchedResultsChangeType.Update:
                break
            default: break
    
            }
        }
    
        func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
            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: indexPath!) as [AnyObject], withRowAnimation: UITableViewRowAnimation.Fade)
                break
            case NSFetchedResultsChangeType.Update:
                tableView.cellForRowAtIndexPath(indexPath!)
                break
            default: break
            }
        }
    
        func controllerDidChangeContent(controller: NSFetchedResultsController) {
            tableView.endUpdates()
        }
    
        func controllerWillChangeContent(controller: NSFetchedResultsController) {
            tableView.beginUpdates()
        }
    
        /*
        // Override to support rearranging the table view.
        override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
    
        }
        */
    
        // Override to support conditional rearranging of the table view.
        override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
            // Return NO if you do not want the item to be re-orderable.
            return true
        }
    
        /*
        // MARK: - Navigation
    
        // In a storyboard-based application, you will often want to do a little preparation before navigation
        override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
            // Get the new view controller using [segue destinationViewController].
            // Pass the selected object to the new view controller.
        }
        */
    
    }