Thanks, @numist, for your response.
Ideally, I'd use the NSDiffableDataSourceSnapshot API but it has a couple of limitations that mean it won't work for my use case:
It's a NSDiffableDataSourceSnapshot and not a NSDiffableDataSourceSectionSnapshot so it can't be composed very well.
It tightly couples Core Data to the view which isn't ideal for those of us who like to modularise our code. Ideally NSDiffableDataSource(Section)Snapshot would by lazily mappable which would perhaps go some ways to facilitating de-coupling Core Data from the View.
For the adapter example, if I get time I'll put one together but it's pretty easy to see for your self:
Within a NSFetchedResultsControllerDelegate, add/remove some items from a section and then check the section counts in controllerWillChangeContent(_:) (storing the sections within the delegate for later) then check the stored section counts again in controllerDidChangeContent(_:).
For NSFetchedResultsSectionInfo to be usable as a collection view data source, you would expect the section counts of the sections stored in controllerWillChangeContent(_:) to stay constant. However, by controllerDidChangeContent(_:) the section counts have changed. In other words, there's some 'spooky action at a distance' occurring which means NSFetchedResultsSectionInfo can't effectively be stored for use as part of a UICollectionViewDataSource and performBatchUpdates specifically.
Reproducible code:
final class SomeController: NSObject, NSFetchedResultsControllerDelegate {
private var willChangeSections: [NSFetchedResultsSectionInfo]?
func controllerWillChangeContent(
_ controller: NSFetchedResultsController<NSFetchRequestResult>
) {
print(#function)
if let sections = controller.sections {
print("sections will change: \(ObjectIdentifier(sections as NSArray))")
for (i, section) in sections.enumerated() {
guard let objects = section.objects else { return }
print("\t\(ObjectIdentifier(objects as NSArray)) - section \(i) count: \(section.numberOfObjects)")
}
}
self.willChangeSections = controller.sections
}
func controllerDidChangeContent(
_ controller: NSFetchedResultsController<NSFetchRequestResult>
) {
print(#function)
if let oldSections = willChangeSections {
print("sections did change (old): \(ObjectIdentifier(oldSections as NSArray))")
for (i, section) in oldSections.enumerated() {
guard let objects = section.objects else { return }
print("\t\(ObjectIdentifier(objects as NSArray)) - section \(i) count: \(section.numberOfObjects)")
}
}
if let newSections = controller.sections {
print("sections did change (new): \(ObjectIdentifier(newSections as NSArray))")
for (i, section) in newSections.enumerated() {
guard let objects = section.objects else { return }
print("\t\(ObjectIdentifier(objects as NSArray)) - section \(i) count: \(section.numberOfObjects)")
}
}
}
}
// EXPECTED:
// controllerWillChangeContent(_:)
// sections will change: ObjectIdentifier(0x0000600001019020)
// ObjectIdentifier(0x0000600001347c00) - section 0 count: 10
//
// controllerDidChangeContent(_:)
// sections did change (old): ObjectIdentifier(0x00006000010187a0)
// ObjectIdentifier(0x0000600001347aa0) - section 0 count: 10 // <--- count the same as in willChange ✅
// sections did change (new): ObjectIdentifier(0x0000600001018660)
// ObjectIdentifier(0x0000600001347b20) - section 0 count: 11
// ACTUAL:
// controllerWillChangeContent(_:)
// sections will change: ObjectIdentifier(0x0000600001019020)
// ObjectIdentifier(0x0000600001347c00) - section 0 count: 10
//
// controllerDidChangeContent(_:)
// sections did change (old): ObjectIdentifier(0x00006000010187a0)
// ObjectIdentifier(0x0000600001347aa0) - section 0 count: 11 // <--- count has updated since willChange ❌
// sections did change (new): ObjectIdentifier(0x0000600001018660)
// ObjectIdentifier(0x0000600001347b20) - section 0 count: 11
Topic:
UI Frameworks
SubTopic:
UIKit
Tags: