Skip to content

Instantly share code, notes, and snippets.

@grosch
Last active October 28, 2017 01:25
Show Gist options
  • Save grosch/8eea996caa5f87e66e0d to your computer and use it in GitHub Desktop.
Save grosch/8eea996caa5f87e66e0d to your computer and use it in GitHub Desktop.
Better NSFetchedResultscontroller
//
// CoreDataFetchController.swift
//
// Created by Scott Grosch on 12/18/14.
// Copyright (c) 2014 Gargoyle Software, LLC. All rights reserved.
//
import CoreData
final class CoreDataFetchController<T : NSManagedObject> {
let fetchedResultsController: NSFetchedResultsController
/**
* Returns a fetch controller initialized using the given fetchedResultsController
*
* :param: fetchedResultsController The previously configured NSFetchedResultsController to wrap.
*
* :returns: A properly typed CoreDataFetchController
*/
init(fetchedResultsController: NSFetchedResultsController) {
self.fetchedResultsController = fetchedResultsController
try! fetchedResultsController.performFetch()
}
/**
* Returns a fetch controller initialized using the given arguments.
*
* :param: managedObjectContext The managed object against which fetchRequest is executed.
* :param: sortDescriptors The sort descriptors of the receiver.
* :param: predicate The predicate of the receiver. Defaults to nil.
* :param: sectionNameKeyPath A key path on result objects that returns the section name. Defaults to nil.
* :param: cacheName The name of the cache file the receiver should use. Defaults to nil.
* :param: delegate The delegate for the NSFetchedResultsController.
*
* :returns: A properly typed CoreDataFetchController
*/
init(managedObjectContext: NSManagedObjectContext, sortDescriptors: [NSSortDescriptor], predicate: NSPredicate? = nil, sectionNameKeyPath: String? = nil, cacheName: String? = nil, delegate: NSFetchedResultsControllerDelegate? = nil) {
#if DEBUG
if let sectionNameKeyPath = sectionNameKeyPath {
assert(sortDescriptors[0].key == sectionNameKeyPath, "First sort descriptor must be the sectionNameKeyPath!")
}
#endif
let name = String(T.self)
let range = name.startIndex ..< name.endIndex
let entityName: String
if let index = name.rangeOfString(".", options: .BackwardsSearch, range: range, locale: nil) {
entityName = name.substringFromIndex(advance(index.startIndex, 1))
} else {
entityName = name
}
let request = NSFetchRequest(entityName: entityName)
request.sortDescriptors = sortDescriptors
request.predicate = predicate
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext, sectionNameKeyPath: sectionNameKeyPath, cacheName: cacheName)
fetchedResultsController.delegate = delegate
try! fetchedResultsController.performFetch()
}
// MARK: - Passthrough variables
/**
* The results of the fetch.
*
* The value of the property is nil if performFetch: hasn’t been called.
*
* The results array only includes instances of the entity specified by the fetch request (fetchRequest) and that match
* its predicate. (If the fetch request has no predicate, then the results array includes all instances of the entity
* specified by the fetch request.)
*
* The results array reflects the in-memory state of managed objects in the controller’s managed object context, not
* their state in the persistent store. The returned array does not, however, update as managed objects are inserted,
* modified, or deleted.
*/
var fetchedObjects: [T]? {
get {
return fetchedResultsController.fetchedObjects as? [T]
}
}
/**
* The managed object context used to fetch objects.
*
* The controller registers to listen to change notifications on this context and properly update its result set and
* section information.
*/
var managedObjectContext: NSManagedObjectContext {
get {
return fetchedResultsController.managedObjectContext
}
}
// MARK: - Helpers for UITableViewDataSource
/**
* Tells the data source to return the number of rows in a given section of a table view. Should
* be called from tableView(_:numberOfRowsInSection)
*
* :param: section An index number identifying a section in tableView.
*
* :returns: The number of rows in section.
*/
func numberOfRowsInSection(section: Int) -> Int {
if let s = fetchedResultsController.sections where (s.count - 1) >= section {
return s[section].numberOfObjects
} else {
return 0
}
}
/**
* Asks the data source to return the number of sections in the table view. Should be called from
* numberOfSectionsInTableView(_)
*
* :returns: The number of sections the table view should contain.
*/
func numberOfSections() -> Int {
return fetchedResultsController.sections?.count ?? 0
}
// MARK: - Ease of use methods
/**
* Returns the object at the given index path in the fetch results, cast to the appropriate type.
*
* :param: indexPath An index path in the fetch results.
*
* :returns: The object at a given index path in the fetch results, cast to the appropriate type.
*/
func objectAtIndexPath(indexPath: NSIndexPath) -> T {
return fetchedResultsController.objectAtIndexPath(indexPath) as! T
}
/**
* Returns the object at the indicated row and section.
*
* :param: row An index number identifying a row in a UITableView object in a section identified by section.
* :param: section An index number identifying a section in a UITableView object.
*
* :returns: The object at the given location
*/
func objectAt(row row: Int, section inSection: Int? = 0) -> T {
let section = inSection ?? 0
let indexPath = NSIndexPath(forRow: row, inSection: section)
return objectAtIndexPath(indexPath)
}
/**
* Executes the fetch request on the store to get objects. An error is thrown if the fetch request
* specified doesn't include a sort descriptor that uses sectionNameKeyPath. After executing this method,
* the fetched objects can be accessed with the property 'fetchedObjects'
*/
func performFetch() throws {
try fetchedResultsController.performFetch()
}
}
@grosch
Copy link
Author

grosch commented Mar 22, 2016

Thanks! Glad somebody else is using it. Just updated one line to make Xcode 7.3 happy.

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