7

I'm using am NSFetchedResultsController to populate data onto a UITableView. It's a simple chat app and I want to load the latest 25 messages onto the table first and load more as the user scrolls up to see older messages (the chat message are in a ascending order).

I call a method that will setFetchLimit: for the NSFetchedResultsController in the willDisplayCell: like so....

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row == 0)
    {
        [self performSelector:@selector(getMoreMessages) withObject:nil afterDelay:1.0];
    }
}

when the first row of the UITableView has been displayed, getMoreMessages will try to reset the fetchLimit reload the UITableView like so.....

- (void)getMoreMessages
{
    maxListItems += 25;
    NSLog(@"set maxListItems: %d", maxListItems);
    [self.resultsController.fetchRequest setFetchLimit:maxListItems];
    [self._tableView reloadData];
}

However, it doesn't seem to be working, the table data will not change. The initial NSFetchRequest is set like so...

NSFetchRequest *chatDataRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ChatData" inManagedObjectContext:appDelegate.managedObjectContext];
[chatDataRequest setEntity:entity];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(key != 0 OR messageNo != 0) and matchNo = %d", matchNo];
[chatDataRequest setPredicate:predicate];

NSSortDescriptor *sortDescripter1 = [[NSSortDescriptor alloc] initWithKey:@"status" ascending:YES];
NSSortDescriptor *sortDescripter2 = [[NSSortDescriptor alloc] initWithKey:@"messageNo" ascending:YES];
NSArray *sortDescripters = [[NSArray alloc] initWithObjects:sortDescripter1, sortDescripter2, nil];
[chatDataRequest setSortDescriptors:sortDescripters];
[sortDescripters release];
[sortDescripter1 release];
[sortDescripter2 release];

[chatDataRequest setFetchLimit:25];

NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:chatDataRequest managedObjectContext:appDelegate.managedObjectContext sectionNameKeyPath:nil cacheName:[NSString stringWithFormat:@"%d_chat.cache", matchNumber]];
[chatDataRequest release];
fetchedResultsController.delegate = self;
NSError *error;
BOOL success = [fetchedResultsController performFetch:&error];
if(!success) NSLog(@"error: %@", error);
self.resultsController = fetchedResultsController;

And back to the question.

How can one dynamically change the fetchLimit for an NSFetchedResultsController?

Any hits would be awesome! Thanks!

Jiho Kang
  • 2,482
  • 1
  • 28
  • 38

2 Answers2

8

Instand using setFetchLimit, using setBatchSize, see below for detail answer.

The count of the fetchedObjects array might not what you want to do, since it does not update the changes from the persistent store. From NSFetchedResultsController documentation:

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.

If you only want to fetch 20 objects, set the fetch limit of the NSFetchRequest. If you want only to keep 20 objects in memory, use setBatchSize of the NSFetchRequest object.

Community
  • 1
  • 1
Jiejing Zhang
  • 1,030
  • 8
  • 16
7

figured this one out. looks like I have to run performFetch: after I change the fetchLimit. :D

[self.resultsController.fetchRequest setFetchLimit:maxListItems];
[self.resultsController performFetch:&error];
Jiho Kang
  • 2,482
  • 1
  • 28
  • 38
  • 2
    also, make sure you reload the UITableView afterwords – Jiho Kang Jul 22 '11 at 07:40
  • does it work well with uifetchedresultscontrollerdelegate controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: ? Doesn't work well for me =( – Irina Dec 16 '13 at 20:37
  • You need to call [NSFetchedResultsController deleteCacheWithName:@"cacheName"] before doing this as stated by Apple Documentation of NSFetchedResultsController.fetchRequest : "You must not change it, its predicate, or its sort descriptor after initialization without disabling caching or calling +deleteCacheWithName" – Christophe Fondacci Jun 18 '15 at 19:05
  • doing this and reloading the tableView will jump to the top of the messages you just loaded which I don't think is desirable. You want a smooth scrolling experience for the user. if you don't have a transient sectionNameKeyPath and you are using AutoDimensions then use the setBatchSize as recommended below. But if you are not using AutoDimension or you are using transient properties then setBatchSize will not work. In this case you are better off not using a fetchedResultsController and just loading more messages to add to your array rather than loading all messages again as you are. – alionthego Jul 09 '18 at 04:43