El Tomato
El Tomato

Reputation: 6707

Core Data: Showing NSFetchRequestResult Search Results with UITableView

I've been playing Core Data for the past several days. I'm fetching data with NSFetchedResultsController. My entity has such attributes as age (Int), firstName (String), lastName (String), uuid (String). I am able to insert a new record to the database. I am able to delete a record. I am also able to edit a record. What I cannot do is to show a search result with the table.

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    // MARK: - Instance variables
    private let persistentContainer = NSPersistentContainer(name: "Profiles") // core data model file (.xcdatamodeld)
    var managedObjectContext: NSManagedObjectContext?

    // MARK: - IBOutlets
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var searchButton: UIButton!

    // MARK: - IBActions
    @IBAction func searchTapped(_ sender: UIButton) {
        searchRecords()
    }

    // MARK: - Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()

        // loading persistentContainer //
        persistentContainer.loadPersistentStores { (persistentStoreDescription, error) in
            if let error = error {
                print("Unable to Load Persistent Store")
                print("\(error), \(error.localizedDescription)")
            } else {
                do {
                    try self.fetchedResultsController.performFetch()
                } catch {
                    let fetchError = error as NSError
                    print("Unable to Perform Fetch Request")
                    print("\(fetchError), \(fetchError.localizedDescription)")
                }
            }
        }
    }
    // MARK: - Life cycle

    // MARK: - fetchedResultsController(controller with the entity)
    fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Person> = {
        // Create Fetch Request with Entity
        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()

        // Configure Fetch Request
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "lastName", ascending: true)]

        // Create Fetched Results Controller
        let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)

        // Configure Fetched Results Controller
        fetchedResultsController.delegate = self
        return fetchedResultsController
    }()
    // MARK: - fetchedResultsController

    // MARK: - TableView
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard let people = fetchedResultsController.fetchedObjects else { return 0 }
        return people.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ProfileTableViewCell
        let person = fetchedResultsController.object(at: indexPath)

        // Configure Cell
        cell.firstLabel.text = person.firstName
        cell.lastLabel.text = person.lastName
        cell.ageLabel.text = String(person.age)
        return cell
    }
    // MARK: - TableView

    // MARK: - Showing search result
    func searchRecords() {
        let context = self.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
        let predicate = NSPredicate(format: "firstName CONTAINS[c] %@", "Sandra")
        fetchRequest.predicate = predicate
        do {
            try fetchedResultsController.performFetch()
            /*
            let result = try context.fetch(fetchRequest)
            if (result.count > 0) {
                print(result.count)
            }
            */
        } catch {
            print("bad")
        }
    }
    // MARK: - Showing search result
}

extension HomeViewController: NSFetchedResultsControllerDelegate {
    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.beginUpdates()
    }

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.endUpdates()
    }

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
        switch (type) {
        case .insert:
            if let indexPath = newIndexPath {
                tableView.insertRows(at: [indexPath], with: .fade)
            }
            break;
        case .delete:
            if let indexPath = indexPath {
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
            break;
        case .update:
            if let indexPath = indexPath {
                tableView.reloadRows(at: [indexPath], with: .automatic)
            }
            break;
        default:
            tableView.reloadData()
        }
    }

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    }
}

If I run searchRecords(), result.count will correctly return the number of records. But the table stays the same. So how can I show my search result with the table? Thanks.

Upvotes: 1

Views: 836

Answers (2)

Jake
Jake

Reputation: 2216

You've got more experience than I do, but the last time I used CoreData and a collectionView I needed to force cast the result like this:

       do {
            return try context.fetch(request) as? [Friend]
        } catch let err {
            print(err)
        }

Then I was able to pull the info to the table more easily.

Upvotes: 0

Prashant Tukadiya
Prashant Tukadiya

Reputation: 16416

Replace your tableview datasource with

// MARK: - Table View

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return self.fetchedResultsController.sections?.count ?? 0
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sectionInfo = self.fetchedResultsController.sections![section]
    return sectionInfo.numberOfObjects
}

Hope it is helpful

Upvotes: 1

Related Questions