Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save nor0x/c48463e429ba7b053fff6e277c72f8ec to your computer and use it in GitHub Desktop.
Save nor0x/c48463e429ba7b053fff6e277c72f8ec to your computer and use it in GitHub Desktop.
UICollectionView + NSFetchedResultsController Swift 3 / iOS 10
var _fetchedResultsController: NSFetchedResultsController<Entity>? = nil
var blockOperations: [BlockOperation] = []
var fetchedResultController: NSFetchedResultsController<Entity> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest: NSFetchRequest<Entity> = Entity.fetchRequest()
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext!
fetchRequest.predicate = NSPredicate(format: "...")
// sort by item text
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "...", ascending: true)]
let resultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
resultsController.delegate = self;
_fetchedResultsController = resultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror)")
}
return _fetchedResultsController!
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if type == NSFetchedResultsChangeType.insert {
print("Insert Object: \(newIndexPath)")
if (collectionView?.numberOfSections)! > 0 {
if collectionView?.numberOfItems( inSection: newIndexPath!.section ) == 0 {
self.shouldReloadCollectionView = true
} else {
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
DispatchQueue.main.async {
this.collectionView!.insertItems(at: [newIndexPath!])
}
}
})
)
}
} else {
self.shouldReloadCollectionView = true
}
}
else if type == NSFetchedResultsChangeType.update {
print("Update Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
DispatchQueue.main.async {
this.collectionView!.reloadItems(at: [indexPath!])
}
}
})
)
}
else if type == NSFetchedResultsChangeType.move {
print("Move Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
DispatchQueue.main.async {
this.collectionView!.moveItem(at: indexPath!, to: newIndexPath!)
}
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print("Delete Object: \(indexPath)")
if collectionView?.numberOfItems( inSection: indexPath!.section ) == 1 {
self.shouldReloadCollectionView = true
} else {
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
DispatchQueue.main.async {
this.collectionView!.deleteItems(at: [indexPath!])
}
}
})
)
}
}
}
public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
if type == NSFetchedResultsChangeType.insert {
print("Insert Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
DispatchQueue.main.async {
this.collectionView!.insertSections(NSIndexSet(index: sectionIndex) as IndexSet)
}
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print("Update Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
DispatchQueue.main.async {
this.collectionView!.reloadSections(NSIndexSet(index: sectionIndex) as IndexSet)
}
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print("Delete Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
DispatchQueue.main.async {
this.collectionView!.deleteSections(NSIndexSet(index: sectionIndex) as IndexSet)
}
}
})
)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
// Checks if we should reload the collection view to fix a bug @ http://openradar.appspot.com/12954582
if (self.shouldReloadCollectionView) {
DispatchQueue.main.async {
self.collectionView.reloadData();
}
} else {
DispatchQueue.main.async {
self.collectionView!.performBatchUpdates({ () -> Void in
for operation: BlockOperation in self.blockOperations {
operation.start()
}
}, completion: { (finished) -> Void in
self.blockOperations.removeAll(keepingCapacity: false)
})
}
}
}
deinit {
for operation: BlockOperation in blockOperations {
operation.cancel()
}
blockOperations.removeAll(keepingCapacity: false)
}
@jerry-jpreng
Copy link

jerry-jpreng commented Aug 4, 2018

I am having the same issue as tobitech. CollectionView with 2 sections, 0, and 1. Section 0 contains a header and one cell that never changes. Section 1 consists of 0 or more cells. Inserts and deletes only happen for Section 1. IndexPath in controller controller: didChange anObject: at indexPath: IndexPath? for type: newIndexPath: always returns [0,0] as indexPath for inserts and deletes, leading to essentially the same error as above because the change is not to the cell at [0,0] but always to a cell at [1,X]_ Thoughts?

@X901
Copy link

X901 commented Nov 25, 2018

what is shouldReloadCollectionView ?

@idevChandra6
Copy link

The whole point of having NSFetchedResultsController is to avoid reload and have automatic updates. Isn't it? Am I missing something?

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