I used the new iOS 13 UITableViewDiffableDataSource to hook up my UITableView, and have a NSFetchedResultsController updating it with the core data objects using the new controller:didChangeContentWithSnapshot: delegate. So far so good. The table view is populated and rows are inserted and removed as expected. But I don't get any updates for updated NSManagedObjects, only for inserted or removed NSManagedObjects. The delegate method is called when a NSManagedObject is updated in the Core Data, but the new snapshot is identical to the old one, as the order of the items is not changed and neither is the NSManagedObjectID returned by the snapshot. How am I expected to update unmoved rows?
Updates with UITableViewDiffableDataSource and NSFetchedResultsController?
Hmm, that sounds like it could be a bug. I'd suggest filing that with the Feedback Assistant ASAP. Still might have a chance of being fixed before GM.
I think I have the same issue.
I have a UITableView populated by a NSFetchedResultsController.
When I perform the fetch (fetchedResultsController.performFetch()), I call
final fileprivate func updateSnapshot(animated: Bool = true) {
var diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<Int, SKPRCrewMemberMO>()
diffableDataSourceSnapshot.appendSections([0])
diffableDataSourceSnapshot.appendItems(fetchedResultsController.fetchedObjects ?? [])
dataSource.apply(diffableDataSourceSnapshot, animatingDifferences: animated)
}
The table view is populated as normal and display all the items fetched. So far so good.
When I edit and save an NSManagedObject from a pushed view controller,
controllerDidChangeContent
is called. In this method, I update the snapshot (ie. I call the updateSnapshot above). But the table view is not refreshed. I have to manually call the tableView.reloadData() method to refresh the cells and have the updated content displayed.
DId you ever figure this out? I can only have it work for added/removed items - if their actual content changes then it doesn't report a change.
As soon as i wrote that message i realised it will never work for changes to fields inside the objects - as the datasource is of type <String, NSManagedObjectID> so its obvoiusly only going to change if objects change based on their ID and nothing else. RIP.
Code Block swift struct WrappedItem: Hashable { /* to trigger model change update */ let item: Item /* this is an NSManagedObject */ let name: String } /* this is the declaration for the snapshot which u */ var diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<Int, WrappedItem>() func updateSnapshot() { diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<Int, WrappedItem>() diffableDataSourceSnapshot.appendSections([0]) do { let mappedObjects = try (fetchedResultsController.fetchedObjects ?? []).map { (item) -> WrappedItem in return WrappedItem(item: item, name: item.name) } diffableDataSourceSnapshot.appendItems(mappedCollections) } catch { os_log(.error, "Error updating snapshot - %@", error.localizedDescription) } diffableDataSource?.apply(diffableDataSourceSnapshot) } }
When managed object attribute is updated, the change, initially, does not propagate to the view.
The blog post on SwiftLee "How-to use Diffable Data Sources with Core Data" shows how to solve the issue. Look for sample code calling this method
Code Block snapshot.reloadItems(reloadIdentifiers)
For managed objects with attributes changed, you should add the managed object identifier by calling reloadItems method.
I recently run into similar issue where, when attributes of a managed object changes, the view is not updated.
FRC's delegate method is called:
Code Block func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference)
The snapshot object contains the identifier (MOID) to the changed object as well!
In order to get my table cell refreshed, I use the implementation similar to the one discussed in "How-to use Diffable Data Sources with Core Data" by SwiftLee.
Look for the sample implementation of the delegate method there.
The gist is... for object with attributes updated, add them to the snapshot by calling
Code Block snapshot.reloadItems(_:)