Skip to content

Instantly share code, notes, and snippets.

@Sorix
Last active September 8, 2022 12:29
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sorix/8bda87efdf46c9beb0ff3daf258da564 to your computer and use it in GitHub Desktop.
Save Sorix/8bda87efdf46c9beb0ff3daf258da564 to your computer and use it in GitHub Desktop.
UITableViewDataSource boilerplate with NSFetchedResultsController
import UIKit
import CoreData
public protocol FetchedResultsDelegate {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: AnyObject) -> UITableViewCell
}
public class FetchedResultsDataSource: NSObject, UITableViewDataSource, NSFetchedResultsControllerDelegate {
public weak var tableView: UITableView?
public var frc: NSFetchedResultsController
public var delegate: FetchedResultsDelegate?
public init(fetchRequest: NSFetchRequest, context: NSManagedObjectContext = DataController.dataStack.mainContext, sectionNameKeyPath: String?) {
frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
super.init()
frc.delegate = self
}
/// Set `tableView.datasource` to `self`, perform fetch and reload table data
public func performFetchAndConfigureTableView(tableView: UITableView) {
self.tableView = tableView
tableView.dataSource = self
do {
try frc.performFetch()
dispatch_async(dispatch_get_main_queue()) { tableView.reloadData() }
} catch {
assertionFailure("\(#function): \(error)")
}
}
public func objectAtIndexPath(indexPath: NSIndexPath) -> AnyObject {
return frc.objectAtIndexPath(indexPath)
}
public func titleForHeaderInSection(section: Int) -> String? {
return self.frc.sections?[section].name
}
// MARK: - UITableViewDataSource
public func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.frc.sections?.count ?? 0
}
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = frc.sections else { return 0 }
return sections[section].numberOfObjects
}
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let delegate = delegate {
return delegate.tableView(tableView, cellForRowAtIndexPath: indexPath, object: self.objectAtIndexPath(indexPath))
} else {
assertionFailure("Delegate is not set, can't return cell")
return UITableViewCell()
}
}
// func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// return self.frc.sections?[section].name
// }
// MARK: - Right section titles
// func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
// return self.frc.sectionForSectionIndexTitle(title, atIndex: index) ?? 0
// }
//
// func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
// return self.frc.sectionIndexTitles
// }
// MARK: - NSFetchedResultsControllerDelegate
public func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView?.beginUpdates()
}
public func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
let sectionIndexSet = NSIndexSet(index: sectionIndex)
switch type {
case NSFetchedResultsChangeType.Insert:
tableView?.insertSections(sectionIndexSet, withRowAnimation: .Automatic)
case NSFetchedResultsChangeType.Delete:
tableView?.deleteSections(sectionIndexSet, withRowAnimation: .Automatic)
case .Update:
tableView?.reloadSections(sectionIndexSet, withRowAnimation: .Automatic)
case .Move:
// TODO: maybe we have better solution instead of reload, possible bugs
tableView?.reloadSections(sectionIndexSet, withRowAnimation: .Automatic)
}
}
public func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
guard let newIndexPath = newIndexPath else { return }
tableView?.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Automatic)
case .Delete:
guard let indexPath = indexPath else { return }
tableView?.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
case .Update:
guard let indexPath = indexPath else { return }
tableView?.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
case .Move:
guard let indexPath = indexPath, let newIndexPath = newIndexPath else { return }
tableView?.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
tableView?.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
}
public func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView?.endUpdates()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment