Skip to content

Instantly share code, notes, and snippets.

@ryanmasondavies
Created September 8, 2018 21:47
Show Gist options
  • Save ryanmasondavies/2f469f14386253773f36913442b122f2 to your computer and use it in GitHub Desktop.
Save ryanmasondavies/2f469f14386253773f36913442b122f2 to your computer and use it in GitHub Desktop.
TableController for Core Data managed objects of any type. Represents a single cell type.
class TableController<EntityType: NSManagedObject>: NSObject, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {
let tableView: UITableView
let managedObjectContext: NSManagedObjectContext
let fetchRequest: NSFetchRequest<EntityType>
let cellIdentifier: String
let configureCell: (UITableViewCell, EntityType) -> Void
init(tableView: UITableView, managedObjectContext: NSManagedObjectContext, fetchRequest: NSFetchRequest<EntityType>, cellIdentifier: String, configureCell: @escaping (UITableViewCell, EntityType) -> Void) {
self.tableView = tableView
self.managedObjectContext = managedObjectContext
self.fetchRequest = fetchRequest
self.cellIdentifier = cellIdentifier
self.configureCell = configureCell
}
func startUpdating() {
tableView.dataSource = self
tableView.delegate = self
do {
try fetchedResultsController.performFetch()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
subscript(indexPath: IndexPath) -> EntityType {
return fetchedResultsController.object(at: indexPath)
}
// MARK: - Table View
func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
let object = fetchedResultsController.object(at: indexPath)
configureCell(cell, object)
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let context = fetchedResultsController.managedObjectContext
context.delete(fetchedResultsController.object(at: indexPath))
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
// MARK: - Fetched results controller
lazy var fetchedResultsController: NSFetchedResultsController<EntityType> = {
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
return controller
}()
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type {
case .insert:
tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
case .delete:
tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
default:
return
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
tableView.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
configureCell(tableView.cellForRow(at: indexPath!)!, anObject as! EntityType)
case .move:
configureCell(tableView.cellForRow(at: indexPath!)!, anObject as! EntityType)
tableView.moveRow(at: indexPath!, to: newIndexPath!)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
/*
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.
func controllerDidChangeContent(controller: NSFetchedResultsController) {
// In the simplest, most efficient, case, reload the table view.
tableView.reloadData()
}
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment