2

After executing an NSBatchBatchInsertRequest and calling fetchedResultsController.performFetch(), the frc’s delegate method controller(_:didChangeContentWith:) is called with a NSDiffableDataSourceSnapshot<Section, NSManagedObjectID> that unexpectedly contains all data in Core Data.

However I've only just inserted new data into Core Data. Applying a snapshot with all the data at each performFetch is causing a high memory load and jittery scrolling when I test this with thousands of cells.

Is there a way to make this delegate method only receive a snapshot with incremental updates to the data source?

Code

Here's how the NSFetchedResultsController is created:

let fetchedResultsController = NSFetchedResultsController<MyManagedObject>(
        fetchRequest: fetchRequest,
        managedObjectContext: persistentContainer.viewContext,
        sectionNameKeyPath: nil,
        cacheName: nil
    )

The NSFetchRequest:

let fetchRequest = MyManagedObject.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "value", ascending: true)]
fetchRequest.fetchBatchSize = 10

The snapshot is applied in this NSFetchedResultsControllerDelegate method below.

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
    dataSource.apply(snapshot as NSDiffableDataSourceSnapshot<Section, NSManagedObjectID>, animatingDifferences: false)
}

The most obvious solution to me was implementing this delegate method only, but that method doesn't seem to get called:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith diff: CollectionDifference<NSManagedObjectID>) {
    print(diff)
}

Data source to Core Data connection

  • When the data source is asked to make a cell for a specific NSManagedObjectID, the right MyManagedObject is found for that NSManagedObjectID using fetchedResultsController.managedObjectContext.object(with:).
  • Then the MyManagedObject is converted into an Item, which is finally used to configure the cell.
Alex Walczak
  • 1,276
  • 1
  • 12
  • 27
  • why are you using a diffable datasource with NSFetchedResultsController? the diffable datasource needs to figure out what the changes were, but the NSFetchedResultsController already knows what they are and tells you in the delegate. – Jon Rose Mar 18 '21 at 21:06
  • Hi Jon, you’ve brought up the exact mystery I’m hoping to solve: why the FRC tells the delegate *all* the data has changed, when only new data has been stored in CD (via batch insert). – Alex Walczak Mar 26 '21 at 18:02
  • As for why I’m using a diffable data source with the FRC: the connection is natural seeing that there is an FRC delegate method built for this purpose `func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference)` – Alex Walczak Mar 26 '21 at 18:08
  • I am not clear when you care calling the performFetch(). it should just be called once for the lifecycle of the FRC, and there is no reason to call it after you performed an insert. – Jon Rose Mar 31 '21 at 10:29
  • `performFetch` is called after the successful completion of every batch insert. That’s to get the UI to refresh with the newly stored data. Is there a better way? – Alex Walczak Mar 31 '21 at 17:44
  • that is very strange. generally you do performFetch once, and then the FRC monitors the changes to coredata and can make a diff. If you are doing a performFetch it reboots the FRC and doesn't do a diff. I think you should close this question and ask a new one on why your FRC is not updating. There are a few reasons why a FRC does not get the changes from core data. what does your core data stack look like? – Jon Rose Apr 04 '21 at 19:29

0 Answers0