1
votes

This is my NSFetchedResultsControllerDelegate with some prints:

//MARK: - NSFetchedResultsControllerDelegate

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    tableView.beginUpdates()
}

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

    let indexSet = NSIndexSet(index: sectionIndex)

    switch type {
    case .Insert:

        print("SECTION INSERT --->>> \(sectionIndex)")
        tableView.insertSections(indexSet, withRowAnimation: .Fade)

    case .Delete:

        tableView.deleteSections(indexSet, withRowAnimation: .Fade)

    case .Update:

        fallthrough

    case .Move:

        tableView.reloadSections(indexSet, withRowAnimation: .Fade)
    }
}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    switch type {
    case .Insert:

        if let newIndexPath = newIndexPath {

            print("ROW INSERT --->>> \(newIndexPath)")
            tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
        }

    case .Delete:

        if let indexPath = indexPath {
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        }

    case .Update:

        if let indexPath = indexPath {
            tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        }

    case .Move:

        if let indexPath = indexPath, let newIndexPath = newIndexPath {

            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
            tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
        }
    }

    print("First sumOfRows --->>> \(fetchedResultsController.fetchedObjects?.count)")

    var sumOfRows = 0
    for section in fetchedResultsController.sections! {

       sumOfRows += section.objects!.count
    }

    print("Second sumOfRows--->>> \(sumOfRows)")
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    tableView.endUpdates()
}

//MARK: - UITableViewDataSource

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

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    print("Section --->>> \(section)")
    print("Number of rows in section --->>> \(fetchedResultsController.sections![section].objects!.count)")
    return fetchedResultsController.sections![section].objects!.count
}

BEFORE the insert I have only one section with one row there.

When I insert new row the output on console is following:

SECTION INSERT --->>> 1
ROW INSERT --->>> <NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}
First sumOfRows --->>> Optional(2)
Second sumOfRows--->>> 3
Section --->>> 0
Number of rows in section --->>> 2
Section --->>> 1
Number of rows in section --->>> 1

Error I get:

CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)

Why NSFetchedResultsController returns wrong number of rows in section?

1
Exactly, the same issue here;( some FRC's bug or wrong logic in code? - Bartłomiej Semańczyk
The error relates to your inserting and deleting data rows without properly commencing or completing the updates to table views - your data set does not match your view. I would recommend you try to include the calls to beginUpdates and endUpdates within each function and not split across functions. (You don't know what swift is doing between these FRC delegate methods that might be upsetting your attempts to update your view.) - andrewbuilder
@andrewbuilder There is no reason to change the delegate methods of the FRC (especially begin/endUpdates). They are created implicitly by Xcode in the Core Data template and work flawlessly if the developer observe the rules to manage the data source. - vadian
@vadian so why don't you write that as your answer to the OP instead of messaging me? I often add code to my FRC delegate methods to manage app view behaviour at a very fine level, and I'm assuming the OP has a similar need. Perhaps this is not the case? - andrewbuilder
@andrewbuilder what exactly do you mean by calling beginUpdates() within each function? - P. Pawluś

1 Answers

1
votes

Just use numberOfObjects instead of objects!.count for your numberOfRowsInSection method.

Please see this answer.