Skip to content

Instantly share code, notes, and snippets.

@Sorix
Created October 10, 2017 23:15
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sorix/987af88f82c95ff8c30b51b6a5620657 to your computer and use it in GitHub Desktop.
Save Sorix/987af88f82c95ff8c30b51b6a5620657 to your computer and use it in GitHub Desktop.
NSFetchedResultsControllerDelegate for UICollectionView
protocol FRCCollectionViewDelegate: class {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
}
class FRCCollectionViewDataSource<FetchRequestResult: NSFetchRequestResult>: NSObject, UICollectionViewDataSource, NSFetchedResultsControllerDelegate {
let frc: NSFetchedResultsController<FetchRequestResult>
weak var collectionView: UICollectionView?
weak var delegate: FRCCollectionViewDelegate?
private var blockOperation = BlockOperation()
init(fetchRequest: NSFetchRequest<FetchRequestResult>, context: NSManagedObjectContext, sectionNameKeyPath: String?) {
frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
super.init()
frc.delegate = self
}
func performFetch() throws {
try frc.performFetch()
}
func object(at indexPath: IndexPath) -> FetchRequestResult {
return frc.object(at: indexPath)
}
// MARK: - UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return frc.sections?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let sections = frc.sections else { return 0 }
return sections[section].numberOfObjects
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let delegate = delegate {
return delegate.collectionView(collectionView, cellForItemAt: indexPath)
} else {
return UICollectionViewCell()
}
}
// MARK: - NSFetchedResultsControllerDelegate
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
blockOperation = BlockOperation()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
let sectionIndexSet = IndexSet(integer: sectionIndex)
switch type {
case .insert:
blockOperation.addExecutionBlock {
self.collectionView?.insertSections(sectionIndexSet)
}
case .delete:
blockOperation.addExecutionBlock {
self.collectionView?.deleteSections(sectionIndexSet)
}
case .update:
blockOperation.addExecutionBlock {
self.collectionView?.reloadSections(sectionIndexSet)
}
case .move:
assertionFailure()
break
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
guard let newIndexPath = newIndexPath else { break }
blockOperation.addExecutionBlock {
self.collectionView?.insertItems(at: [newIndexPath])
}
case .delete:
guard let indexPath = indexPath else { break }
blockOperation.addExecutionBlock {
self.collectionView?.deleteItems(at: [indexPath])
}
case .update:
guard let indexPath = indexPath else { break }
blockOperation.addExecutionBlock {
self.collectionView?.reloadItems(at: [indexPath])
}
case .move:
guard let indexPath = indexPath, let newIndexPath = newIndexPath else { return }
blockOperation.addExecutionBlock {
self.collectionView?.moveItem(at: indexPath, to: newIndexPath)
}
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
collectionView?.performBatchUpdates({
self.blockOperation.start()
}, completion: nil)
}
}
@tengelmeier
Copy link

The current version does not work on iOS 12 as the NSBlockOperation blocks are executed parallel on background threads but need to be performed on the main thread.

@sathishvgs
Copy link

@tengelmeier any solution for this ?

@cdf1982
Copy link

cdf1982 commented Jul 27, 2020

My UI freezes during significant, multi-object sync operations via iCloud; is it possible the cause is what @tengelmeier commented?
Update: It seems that the approach used in this 2020 Stack Overflow answer is not causing the UI to be frozen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment