iOS Developer Library

Developer

CoreData Framework Reference NSFetchedResultsController Class Reference

Options
Deployment Target:

On This Page
Language:

NSFetchedResultsController

Inheritance


Conforms To


Import Statement


Swift

import CoreData

Objective-C

@import CoreData;

Availability


Available in iOS 3.0 and later.

You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.

While table views can be used in several ways, fetched results controllers are primarily intended to assist you with a master list view. UITableView expects its data source to provide cells as an array of sections made up of rows. You configure a fetch results controller using a fetch request that specifies the entity, an array containing at least one sort ordering, and optionally a filter predicate. The fetched results controller efficiently analyzes the result of the fetch request and computes all the information about sections in the result set. It also computes all the information for the index based on the result set.

In addition, fetched results controllers provide the following features:

  • Optionally monitor changes to objects in the associated managed object context, and report changes in the results set to its delegate (see The Controller’s Delegate).

  • Optionally cache the results of its computation so that if the same data is subsequently re-displayed, the work does not have to be repeated (see The Cache).

A controller thus effectively has three modes of operation, determined by whether it has a delegate and whether the cache file name is set.

  1. No tracking: the delegate is set to nil.

    The controller simply provides access to the data as it was when the fetch was executed.

  2. Memory-only tracking: the delegate is non-nil and the file cache name is set to nil.

    The controller monitors objects in its result set and updates section and ordering information in response to relevant changes.

  3. Full persistent tracking: the delegate and the file cache name are non-nil.

    The controller monitors objects in its result set and updates section and ordering information in response to relevant changes. The controller maintains a persistent cache of the results of its computation.

Using NSFetchedResultsController

Creating the Fetched Results Controller

You typically create an instance of NSFetchedResultsController as an instance variable of a table view controller. When you initialize the fetch results controller, you provide four parameters:

  1. A fetch request. This must contain at least one sort descriptor to order the results.

  2. A managed object context. The controller uses this context to execute the fetch request.

  3. Optionally, a key path on result objects that returns the section name. The controller uses the key path to split the results into sections (passing nil indicates that the controller should generate a single section).

  4. Optionally, the name of the cache file the controller should use (passing nil prevents caching). Using a cache can avoid the overhead of computing the section and index information.

After creating an instance, you invoke performFetch: to actually execute the fetch.

  • NSManagedObjectContext *context = <#Managed object context#>;
  • NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
  • // Configure the request's entity, and optionally its predicate.
  • NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"<#Sort key#>" ascending:YES];
  • NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
  • [fetchRequest setSortDescriptors:sortDescriptors];
  • [sortDescriptors release];
  • [sortDescriptor release];
  • NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
  • initWithFetchRequest:fetchRequest
  • managedObjectContext:context
  • sectionNameKeyPath:nil
  • cacheName:@"<#Cache name#>"];
  • [fetchRequest release];
  • NSError *error;
  • BOOL success = [controller performFetch:&error];

The Controller’s Delegate

If you set a delegate for a fetched results controller, the controller registers to receive change notifications from its managed object context. Any change in the context that affects the result set or section information is processed and the results are updated accordingly. The controller notifies the delegate when result objects change location or when sections are modified (see NSFetchedResultsControllerDelegate). You typically use these methods to update the display of the table view.

The Cache

Where possible, a controller uses a cache to avoid the need to repeat work performed in setting up any sections and ordering the contents. The cache is maintained across launches of your application.

When you initialize an instance of NSFetchedResultsController, you typically specify a cache name. (If you do not specify a cache name, the controller does not cache data.) When you create a controller, it looks for an existing cache with the given name:

  • If the controller can’t find an appropriate cache, it calculates the required sections and the order of objects within sections. It then writes this information to disk.

  • If it finds a cache with the same name, the controller tests the cache to determine whether its contents are still valid. The controller compares the current entity name, entity version hash, sort descriptors, and section key-path with those stored in the cache, as well as the modification date of the cached information file and the persistent store file.

    If the cache is consistent with the current information, the controller reuses the previously-computed information.

    If the cache is not consistent with the current information, then the required information is recomputed, and the cache updated.

Any time the section and ordering information change, the cache is updated.

If you have multiple fetched results controllers with different configurations (different sort descriptors and so on), you must give each a different cache name.

You can purge a cache using deleteCacheWithName:.

Implementing the Table View Datasource Methods

You ask the object to provide relevant information in your implementation of the table view data source methods:

  • - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  • return [[<#Fetched results controller#> sections] count];
  • }
  • - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
  • if ([[<#Fetched results controller#> sections] count] > 0) {
  • id <NSFetchedResultsSectionInfo> sectionInfo = [[<#Fetched results controller#> sections] objectAtIndex:section];
  • return [sectionInfo numberOfObjects];
  • } else
  • return 0;
  • }
  • - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  • UITableViewCell *cell = <#Get the cell#>;
  • NSManagedObject *managedObject = [<#Fetched results controller#> objectAtIndexPath:indexPath];
  • // Configure the cell with data from the managed object.
  • return cell;
  • }
  • - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
  • if ([[<#Fetched results controller#> sections] count] > 0) {
  • id <NSFetchedResultsSectionInfo> sectionInfo = [[<#Fetched results controller#> sections] objectAtIndex:section];
  • return [sectionInfo name];
  • } else
  • return nil;
  • }
  • - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
  • return [<#Fetched results controller#> sectionIndexTitles];
  • }
  • - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
  • return [<#Fetched results controller#> sectionForSectionIndexTitle:title atIndex:index];
  • }

Responding to Changes

In general, NSFetchedResultsController is designed to respond to changes at the model layer, by informing its delegate when result objects change location or when sections are modified.

If you allow a user to reorder table rows, then your implementation of the delegate methods must take this into account—see NSFetchedResultsControllerDelegate.

Changes are not reflected until after the controller’s managed object context has received a processPendingChanges message. Therefore, if you change the value of a managed object’s attribute so that its location in a fetched results controller’s results set would change, its index as reported by the controller would typically not change until the end of the current event cycle (when processPendingChanges is invoked). For example, the following code fragment would log “same”:

  • NSFetchedResultsController *frc = <#A fetched results controller#>;
  • NSManagedObject *managedObject = <#A managed object in frc's fetchedObjects array#>;
  • NSIndexPath *beforeIndexPath = [frc indexPathForObject:managedObject];
  • [managedObject setSortKeyAttribute:
  • <#A new value that changes managedObject's position in frc's fetchedObjects array#>;
  • NSIndexPath *afterIndexPath = [frc indexPathForObject:managedObject];
  • if ([beforeIndexPath compare:afterIndexPath] == NSOrderedSame) {
  • NSLog(@"same");
  • }

Modifying the Fetch Request

You cannot simply change the fetch request to modify the results. If you want to change the fetch request, you must:

  1. If you are using a cache, delete it (using deleteCacheWithName:).

    Typically you should not use a cache if you are changing the fetch request.

  2. Change the fetch request.

  3. Invoke performFetch:.

Handling Object Invalidation

When a managed object context notifies the fetched results controller that individual objects are invalidated, the controller treats these as deleted objects and sends the proper delegate calls.

It’s possible for all the objects in a managed object context to be invalidated simultaneously. (For example, as a result of calling reset, or if a store is removed from the the persistent store coordinator.) When this happens, NSFetchedResultsController does not invalidate all objects, nor does it send individual notifications for object deletions. Instead, you must call performFetch: to reset the state of the controller then reload the data in the table view (reloadData).

iOS Version Issues

There are several known issues and behavior changes with NSFetchedResultsController on various releases of iOS.

iOS 4.0 and Later

On iOS 4.0 and later, NSFetchedResultsController does not silently correct results if you reuse the same cache for multiple controllers.

  • On iOS 4.0 and later, NSFetchedResultsController does not perform some checks that would detect when you erroneously use the same cache for multiple controllers. (This derives from an optimization that can help avoid complex string matching on launch.) If you reuse the same cache for multiple controllers, you will get incorrect results.

iOS 3.2 and Later

On iOS 3.2 and later, if the cache is stale or nil, the controller attempts to calculate the section information directly in the database instead of fetching the objects. This isn't possible if the sectionNameKeyPath includes any properties that are not persistent. In that case, the controller will fetch the objects to compute the sections.

Pre-iOS 4.0

Prior to iOS 4.0, there are a number of known issues with delegates handling callbacks. In many cases, you should just use

  • - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
  • [self.tableView reloadData];
  • }

instead of per-row updates. For the best user experience, you are encouraged to check the OS version number and use the finer grained delegate methods only on devices running iOS 4.0 and later.

NSFetchedResultsController does not support sections being deleted as a result of a UI-driven change.

Subclassing Notes

You create a subclass of this class if you want to customize the creation of sections and index titles. You override sectionIndexTitleForSectionName: if you want the section index title to be something other than the capitalized first letter of the section name. You override sectionIndexTitles if you want the index titles to be something other than the array created by calling sectionIndexTitleForSectionName: on all the known sections.

  • Returns a fetch request controller initialized using the given arguments.

    Declaration

    Swift

    init(fetchRequest fetchRequest: NSFetchRequest, managedObjectContext context: NSManagedObjectContext, sectionNameKeyPath sectionNameKeyPath: String?, cacheName name: String?)

    Objective-C

    - (id)initWithFetchRequest:(NSFetchRequest *)fetchRequest managedObjectContext:(NSManagedObjectContext *)context sectionNameKeyPath:(NSString *)sectionNameKeyPath cacheName:(NSString *)name

    Parameters

    fetchRequest

    The fetch request used to get the objects.

    The fetch request must have at least one sort descriptor. If the controller generates sections, the first sort descriptor in the array is used to group the objects into sections; its key must either be the same as sectionNameKeyPath or the relative ordering using its key must match that using sectionNameKeyPath.

    context

    The managed object against which fetchRequest is executed.

    sectionNameKeyPath

    A key path on result objects that returns the section name. Pass nil to indicate that the controller should generate a single section.

    The section name is used to pre-compute the section information.

    If this key path is not the same as that specified by the first sort descriptor in fetchRequest, they must generate the same relative orderings. For example, the first sort descriptor in fetchRequest might specify the key for a persistent property; sectionNameKeyPath might specify a key for a transient property derived from the persistent property.

    name

    The name of the cache file the receiver should use. Pass nil to prevent caching.

    Pre-computed section info is cached to a private directory under this name. If Core Data finds a cache stored with this name, it is checked to see if it matches the fetchRequest. If it does, the cache is loaded directly—this avoids the overhead of computing the section and index information. If the cached information doesn’t match the request, the cache is deleted and recomputed when the fetch happens.

    Return Value

    The receiver initialized with the specified fetch request, context, key path, and cache name.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • Executes the receiver’s fetch request.

    Declaration

    Swift

    func performFetch(_ error: NSErrorPointer) -> Bool

    Objective-C

    - (BOOL)performFetch:(NSError **)error

    Parameters

    error

    If the fetch is not successful, upon return contains an error object that describes the problem.

    Return Value

    YEStrue if the fetch executed successfully, otherwise NOfalse.

    Discussion

    After executing this method, you can access the receiver’s the fetched objects with the property fetchedObjects.

    Special Considerations

    This method returns NOfalse (and a suitable error in error) if the fetch request doesn’t include a sort descriptor that uses the section name key path specified in initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • The fetch request used to do the fetching.

    Declaration

    Swift

    var fetchRequest: NSFetchRequest { get }

    Objective-C

    @property(nonatomic, readonly) NSFetchRequest *fetchRequest

    Special Considerations

    If you want to modify the fetch request, you must follow the steps described in Modifying the Fetch Request.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • The managed object context used to fetch objects.

    Declaration

    Swift

    var managedObjectContext: NSManagedObjectContext { get }

    Objective-C

    @property(nonatomic, readonly) NSManagedObjectContext *managedObjectContext

    Discussion

    The controller registers to listen to change notifications on this context and properly update its result set and section information. 

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • The key path on the fetched objects used to determine the section they belong to.

    Declaration

    Swift

    var sectionNameKeyPath: String? { get }

    Objective-C

    @property(nonatomic, readonly) NSString *sectionNameKeyPath

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • cacheName cacheName Property

    The name of the file used to cache section information.

    Declaration

    Swift

    var cacheName: String? { get }

    Objective-C

    @property(nonatomic, readonly) NSString *cacheName

    Discussion

    The file itself is stored in a private directory; you can only access it by name using deleteCacheWithName:

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • delegate delegate Property

    The object that is notified when the fetched results changed.

    Declaration

    Swift

    unowned(unsafe) var delegate: NSFetchedResultsControllerDelegate?

    Objective-C

    @property(nonatomic, assign) id< NSFetchedResultsControllerDelegate > delegate

    Special Considerations

    If you do not specify a delegate, the controller does not track changes to managed objects associated with its managed object context.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • Deletes the cached section information with the given name.

    Declaration

    Swift

    class func deleteCacheWithName(_ name: String?)

    Objective-C

    + (void)deleteCacheWithName:(NSString *)name

    Parameters

    name

    The name of the cache file to delete.

    If name is nil, deletes all cache files.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • The results of the fetch.

    Declaration

    Swift

    var fetchedObjects: [AnyObject]? { get }

    Objective-C

    @property(nonatomic, readonly) NSArray *fetchedObjects

    Discussion

    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.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • Returns the object at the given index path in the fetch results.

    Declaration

    Swift

    func objectAtIndexPath(_ indexPath: NSIndexPath) -> AnyObject

    Objective-C

    - (id)objectAtIndexPath:(NSIndexPath *)indexPath

    Parameters

    indexPath

    An index path in the fetch results.

    If indexPath does not describe a valid index path in the fetch results, an exception is raised.

    Return Value

    The object at a given index path in the fetch results.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • Returns the index path of a given object.

    Declaration

    Swift

    func indexPathForObject(_ object: AnyObject) -> NSIndexPath?

    Objective-C

    - (NSIndexPath *)indexPathForObject:(id)object

    Parameters

    object

    An object in the receiver’s fetch results.

    Return Value

    The index path of object in the receiver’s fetch results, or nil if object could not be found.

    Special Considerations

    In versions of iOS before 3.2, this method raises an exception if object is not contained in the receiver’s fetch results.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • Returns the corresponding section index entry for a given section name.

    Declaration

    Swift

    func sectionIndexTitleForSectionName(_ sectionName: String?) -> String?

    Objective-C

    - (NSString *)sectionIndexTitleForSectionName:(NSString *)sectionName

    Parameters

    sectionName

    The name of a section.

    Return Value

    The section index entry corresponding to the section with name sectionName.

    Discussion

    The default implementation returns the capitalized first letter of the section name.

    You should override this method if you need a different way to convert from a section name to its name in the section index.

    Special Considerations

    You only need this method if you use a section index.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.

  • The array of section index titles.

    Declaration

    Swift

    var sectionIndexTitles: [AnyObject] { get }

    Objective-C

    @property(nonatomic, readonly) NSArray *sectionIndexTitles

    Discussion

    The default implementation returns the array created by calling sectionIndexTitleForSectionName: on all the known sections. You should override this method if you want to return a different array for the section index.

    Special Considerations

    You only need this method if you use a section index.

    Import Statement

    Objective-C

    @import CoreData;

    Swift

    import CoreData

    Availability

    Available in iOS 3.0 and later.