Ron
Ron

Reputation: 748

NSFetchedResultsController doesn't update count after delete

I'm learning CoreData on mac os. I have a demo program which basically works, except that when I delete a row from my tableView,

fetchedResultsController.fetchedObjects?.count

hasn't updated.

Revelevant properties are defined as follows

    var container: NSPersistentContainer!
    var fetchedResultsController: NSFetchedResultsController<Commit>!
    var count: Int { get {
        if fetchedResultsController == nil { return 0 }
        return fetchedResultsController.fetchedObjects?.count ?? 0
        } }

The remove method:

    func remove(itemAt index: Int) {
        guard let n = fetchedResultsController.fetchedObjects?.count,
            index < n && index >= 0,
            let commit = fetchedResultsController.fetchedObjects?[index] else { return }
        container.viewContext.delete(commit)
        saveContext()
        ddt("remove \(count)", caller: self)
    }

The count after remove hasn't been updated, however the persistent store is correct. If I remove another row, it still doesn't update, but if I change predicate (even to the nil predicate) it refreshes properly.

On relaunch, all is updated.

My delegate doesn't do anything. The table gets updated in its delegate:

    func tableView(_ tableView: NSTableView, rowActionsForRow row: Int, edge: NSTableView.RowActionEdge) -> [NSTableViewRowAction] {
        // left swipe
        if edge == .trailing {
            let deleteAction = NSTableViewRowAction(style: .destructive, title: "Delete", handler: {
                [unowned self] (rowAction, row) in
                    self.dataContainer.remove(itemAt: row)
                    tableView.removeRows(at: [row], withAnimation: .slideLeft)
            })
            deleteAction.backgroundColor = NSColor.red
            return [deleteAction]
        }
        // Anything other than left does nothing
        return []
    }

What have I left out?

(ddt, for those too young to remember, was an insecticide banned many years ago because it tended to propagate up the food chain, killing off birds, among other species. I think it's safe to use on bugs in Swift.)

Upvotes: 1

Views: 827

Answers (1)

vadian
vadian

Reputation: 285059

  • First of all declare the fetchedResultsController as lazy non-optional property and set the delegate in the closure

    lazy var fetchedResultsController: NSFetchedResultsController<Commit> = {
    
        ...
    
        let controller = NSFetchedResultsController( ...
        controller.delegate = self
    
        ...
        return controller
    }()
    
  • Adopt NSFetchedResultsControllerDelegate and implement the delegate methods

    • func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
    • func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
    • func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
  • To delete a record in a NSTableViewRowAction get the item at indexPath and delete the record in the Core Data stack. You don't need to check the index, the row does exist

    let indexPath = IndexPath(item: row, section: 0)
    let commit = fetchedResultsController.object(at: indexPath)
    container.viewContext.delete(commit)
    saveContext()
    

    The NSFetchedResultsControllerDelegate methods manage the update of the UI

Upvotes: 3

Related Questions