Is it possible to source an up-to-date snapshot from an NSFetchedResultsController

喜夏-厌秋 提交于 2020-07-07 05:59:25

问题


As of iOS 13, the easiest way to keep a UITableView in sync with a NSFetchedResultsController seems to be with snapshots.

The NSFetchedResultsController vends a snapshot reference to its delegate whenever the managedObjectContext reports additions, deletions, or updates. When using snapshots (NSDiffableDataSourceSnapshot), there is only one FRC delegate method that needs to be implemented: controller(_:didChangeContentWith:). In order to make that delegate method work, the UITableViewDiffableDataSource and the Snapshot has to be typed <String, NSManagedObjectID>.

It works mostly.

But what if the entire table needs to be updated? Using tableView.reloadData() or frc.performFetch() seems anti-pattern.

edit

I manually built a snapshot, and call apply when necessary. But since my snapshot is based on NSFetchedResultsSectionInfo objects, it seems like I'm duplicating what the FRC already has available: Hashable section titles, and Hashable NSManagedObjectIDs


回答1:


I apologize for my previous (deleted) answer. The snapshot is irrelevant in a Core Data context.

The purpose of NSFetchedResultsController in conjunction with Core Data is to update the UI when the NSManagedObjectContext is saved.

To be able to control the animation of the diffable data source (to work around the ridiculous behavior) you have to subclass UITableViewDiffableDataSource and add a property animatingDifferences. Further adopt NSFetchedResultsControllerDelegate in the subclass (not in the view controller).

class DiffableCoreDataSource: UITableViewDiffableDataSource<String,NSManagedObjectID> {
    var animatingDifferences = false
}

extension DiffableCoreDataSource : NSFetchedResultsControllerDelegate
{
    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
        apply(snapshot as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>, animatingDifferences: animatingDifferences)
        animatingDifferences = true // set it to the default
    }
}

In the view controller set the delegate of the FRC to the subclass assuming there is a dataSource property representing DiffableCoreDataSource

frc.delegate = dataSource

If a record is updated set dataSource.animatingDifferences to false right before saving the context.

To reload the entire table view call frc.performFetch(). Never call reloadData() on the table view.




回答2:


TL;DR: Even though the NSFetchedResultsController is able to furnish its delegate with an up-to-date snapshot reference when the managedObjectContext reports additions, deletions, and updates, it's not possible (for now at least) to programmatically access a snapshot directly from the FRC.

As vadian suggests above, use the FRC instance method performFetch() to apply the newest snapshot to all cells in a DiffableData-backed UITableview. This is an undocumented approach to snapshot management. But it enables the use of FRC-provided snapshots solely, instead of having to write a stand-alone snapshot. Less code, and a 'single-source-of-truth' for snapshot updates.

Based on feedback from others, the drawback is that animatingDifferences argument in apply(_: animatingDifferences:) method triggers bugs if set to true. Two reproduceable bugs are that the table fails to load at all when the snapshot is applied the first time. The other bug is if attempting to delete a record using trailingSwipeActionsConfigurationForRowAt method, and perhaps others. So the only way to use this backdoor approach is to set that bool to false in all cases. Which means, well, no animations.



来源:https://stackoverflow.com/questions/62142358/is-it-possible-to-source-an-up-to-date-snapshot-from-an-nsfetchedresultscontroll

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!