Created
September 8, 2018 21:47
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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