Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSFetchedResultsController always returns no rows

Tags:

ios

core-data

I am trying to get a NSFetchedResultsController working with my tableview, but despite my best efforts to get it setup correctly, it is always returning no rows. I have opened up my data store through Finder and validated through a SQLite editor that there are in fact plenty of records, but it always returns zero. What am I missing?

Custom getter for the controller:

- (NSFetchedResultsController *)fetchedResultsController {
    if (fetchedResultsController_ != nil) {
        return fetchedResultsController_;
    }

    RBGameItemController* itemController = [RBGameItemController sharedInstance];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"GameItem" inManagedObjectContext:itemController.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest  
                                                                                                  managedObjectContext:itemController.managedObjectContext 
                                                                                                    sectionNameKeyPath:nil 
                                                                                                             cacheName:@"Root"];
    self.fetchedResultsController = theFetchedResultsController;
    self.fetchedResultsController.delegate = self;

    [sort release];
    [fetchRequest release];
    [theFetchedResultsController release];

    return self.fetchedResultsController;
}

I am fetching the data in viewDidLoad:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }
}

Fetched results delegate:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.tableView beginUpdates];

    NSLog(@"controllerWillChangeContent");
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    NSLog(@"controller:didChangeObject:");
    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            // Reloading the section inserts a new row and ensures that titles are updated appropriately.
            [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
    NSLog(@"controller:didChangeSection:");
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    NSLog(@"controllerDidChangeContent:");
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];
}

I am implementing both methods for section and row number, which returns 1 for sections and 0 for rows:

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
    int sections = [[self.fetchedResultsController sections] count];
    NSLog(@"sections=%i", sections);
     return sections;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    int rows = [sectionInfo numberOfObjects];

    NSLog(@"rows=%i", rows);

    return rows;
}
like image 761
Wayne Hartman Avatar asked Dec 04 '11 06:12

Wayne Hartman


2 Answers

Have you tried manually performing a query with your fetch request to make sure it returns what you expect? In your custom fetchedResultsController try doing:

NSArray *entities = [itemController.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(@"%d",entities.count);

and see what comes back.

Also, try setting up your fetchedRequestController without a cache. It may be that you are reusing the same cache by name in other parts of the application?

like image 87
gschandler Avatar answered Nov 18 '22 09:11

gschandler


The assignment to the delegate:

 self.fetchedResultsController.delegate = self;

Last line:

return self.fetchedResultsController;

These will result in a circular call to this method. Don't write code like this.

The critcal points in this call are the managedObjectContext, sortDescriptor, and the entity. So make sure none of these are nil and that the values are what you expect.

static NSString *const NSString *kMyFetchedResultsControllerCacheName = @"RootCache";



- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController == nil) {
        [NSFetchedResultsController deleteCacheWithName:];
        RBGameItemController* itemController = [RBGameItemController sharedInstance];

        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([GameItem class]) inManagedObjectContext:itemController.managedObjectContext];
        [fetchRequest setEntity:entity];

        NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
        [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

        [fetchRequest setFetchBatchSize:20];

        NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:itemController.managedObjectContext sectionNameKeyPath:nil cacheName:kMyFetchedResultsControllerCacheName];
        self.fetchedResultsController = fetchedResultsController;
        fetchedResultsController.delegate = self;
    }

    return _fetchedResultsController;
}

Finally, you always need to make sure performFetch is called, probably in viewDidLoad:

NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error != nil) {
    NSLog(@"%@", error.localizedDescription);
}
like image 20
Cameron Lowell Palmer Avatar answered Nov 18 '22 10:11

Cameron Lowell Palmer