Core data: The fetched object at index x has an out of order section name 'xxxxxx. Objects must be sorted by section name

sixstatesaway picture sixstatesaway · Aug 27, 2012 · Viewed 8.7k times · Source

I know I'm not the first to ask this question but I'm really stumped..

Basically I have a screen with two buttons. Each button loads data into a tableview below based on a date. On the first load of the first tableview (the left button is selected by default) everything displays fine. If I click on the right button and I get a blank tableview, and I get the error

The fetched object at index x has an out of order section name 'xxxxxx. Objects must be sorted by section name.

Switching back to the left table view, the data is gone. Both tableviews are empty.

Each tableview has 2 sections depending on the start time of the item. If I eliminate the sections the data displays fine. Unfortunately I need them.. the data is sorted into the two sections like so:

@interface NSString(agendaSessionKeyPath)
@property (nonatomic, readonly) NSString *sessionSection;
@end

@implementation NSString(agendaSessionKeyPath)

- (NSString *)sessionSection
{
    int timeValue = [[self stringByReplacingOccurrencesOfString:@":" withString:@""] intValue]; //turns 11:00 to 1100
    if (timeValue < 1200)
        return @"Morning";
     else
        return @"Afternoon";
}

Fetch request

- (void)viewDidLoad
{
     //other viewDidLoad stuff
    [self fetchSessions];
}

method which sorts the data from the left and right button based on date:

- (void)fetchSessions
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
    [dateFormatter setDateFormat:@"yyyy-MM-dd"];

    NSDate* date = nil;
    if (selected == 0) //left button is selected
    {
        date = [dateFormatter dateFromString:@"2012-09-26"];
    }
    else if (selected == 1) //right button is selected
    {
        date = [dateFormatter dateFromString:@"2012-09-27"];
    }

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"date == %@", date];
    [self.fetchedResultsController.fetchRequest setPredicate:predicate];

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

fetched results controller

- (NSFetchedResultsController *)fetchedResultsController {
    self.managedObjectContext = [[MATCDatabaseController sharedDatabaseController] managedObjectContext];
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Session"];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc]
                              initWithKey:@"title" ascending:YES];
    NSSortDescriptor *timeSort = [NSSortDescriptor sortDescriptorWithKey:@"timeValue" ascending:YES];
    [fetchRequest setSortDescriptors:@[timeSort, sort]];
    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"startTime.sessionSection"
                                                   cacheName:nil];

    self.fetchedResultsController = theFetchedResultsController;
    [self.fetchedResultsController setDelegate:self];

    return _fetchedResultsController;

}

any help is appreciated!

Answer

Jody Hagins picture Jody Hagins · Aug 28, 2012

OK, I did take a quick peek.

You initialize the FRC with:

[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:self.managedObjectContext
                                      sectionNameKeyPath:@"startTime.sessionSection"
                                               cacheName:nil];

which tells it that your section titles are to be obtained via the key path startTime.sessionSection.

Now, the first sort descriptor of a fetch request that is given to a FRC will be used to sort the sections. The sort descriptor you are providing first is for timeValue which does not seem right.

Your first sort descriptor should specify a sort for your section titles. Change that and you may be good to go.

EDIT

Thanks guys for the info. I'm still a bit lost though. Did you mean that I should add a sort descriptor on startTime.sessionSection before assigning it to the sectionNameKeyPath? I tried, but still no luck. timeValue and startTime.sessionSection are related. Could that be it? – pigeonfactory

You have to make sure that the very first sort descriptor will properly sort your data based on the section. In your case, times are being converted into words. Your initial sort descriptor is for times, and when the data is sorted based on time, the sections are not sorted properly, which is causing your error.

The very first sort descriptor must satisfy the section data. So, initially, I would try...

[fetchRequest setSortDescriptors:@[
    [NSSortDescriptor sortDescriptorWithKey:@"startTime.sessionSection"
                                  ascending:NO],
    [NSSortDescriptor sortDescriptorWithKey:@"timeValue"
                                  ascending:YES],
    [NSSortDescriptor sortDescriptorWithKey:@"title"
                                  ascending:YES] ];

Note, if you have lots and lots of data, you may find that your section mechanism gets slow. If that happens, you may want to add this section data to your database.