简体   繁体   中英

NSFetchedResultController not calling controllerDidChangeContent in delegate

There are many very similar looking questions on StackOverflow but I am asking this because almost every existing question is caused by not calling performFetch on the FRC.

In this case we are calling it.

fetchedResultsController = NSFetchedResultsController(fetchRequest: StepRecord.fetchRequest(forDate: date),
                                              managedObjectContext: theMainThreadContext,
                                                sectionNameKeyPath: nil,
                                                         cacheName: nil)

fetchedResultsController.delegate = self

do {
    try fetchedResultsController.performFetch()
} catch (let error) {
    print(error)
}

Then in a later function, we have something like this...

func updateScreen() {
    if fetchedResultsController.fetchedObjects.count == 0 {
        // download data and store into core data
    }

    // update the screen
}

And we have the delegate method...

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    updateScreen()
}

The place that is writing into core data is definitely writing into a backgroundThreadContext (this is a pattern we have used several times in the app and it is working elsewhere).

However, the delegate method is not getting called in this case.

If we exit the screen and go back in the updateScreen method runs and the FRC DOES have data. So the fetch request is correct, the download is putting records in the correct place and saving properly and the update screen method is able to get those items and populate the screen.

The only problem we are having here is that the delegate method is not being called.

Is there something we have missed here? Like I said, we have used this same pattern in a few places and it works. It's just in this case that it isn't working.

Let me know if there is any other code you would like to see and I'll pass it along if I can.

What is happening is that you are using a different NSManagedObjectContext for saving the downloaded data. That context uses a background queue as you say. But perhaps that context is not a child of the context of the FRC. In that case, you need to merge into the main context listening the notification of the other changing.

But the easy way to fix it is using a background context that is a child of the main context. That is, create a new context of type privateQueue and set its parentContext equal to the current context.

TL:DR - Objects that don't exist, won't listen for NSNotifications.

OK, after some serious debugging we finally found the problem that was causing this and it's a doozy.

So, the code in my question existed inside a class that was essentially a "worker" for a factory class.

This worker was used by the factory to create an object and return it to the owner of the factory (the "consumer" if you like).

Consumer                                - view controller
 - Factory                              - strongly referenced
    - Worker                            - function variable
       - FetchedResultsController stuff - strongly referenced

While we were keeping a strong reference to the Factory, the factory didn't actually store a reference to the worker outside of the function it was used in.

This means that by the time the download completed and saved stuff into CoreData, the Fetched Results Controller and the "worker" didn't actually exist in memory anymore.

So, the easy fix was to add a stored reference to the worker.

The longer fix is to refactor some code I think, but that's a job for another day.

Thanks for all the help. It definitely helped us root out the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM