Falha no nome da seção personalizada NSFetchedResultsController

Eu tenho um objeto gerenciado com um atributo dueDate. Em vez de exibir usando uma sequência de datas feia como cabeçalho da seção do meu UITableView, criei um atributo transitório chamado "categoria" e o defini da seguinte forma:

- (NSString*)category
{
    [self willAccessValueForKey:@"category"];

    NSString* categoryName;
    if ([self isOverdue])
    {
        categoryName = @"Overdue";
    }
    else if ([self.finishedDate != nil])
    {
        categoryName = @"Done";
    }
    else
    {
        categoryName = @"In Progress";
    }

    [self didAccessValueForKey:@"category"];
    return categoryName;
}

Aqui está o NSFetchedResultsController configurado:

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

NSMutableArray* descriptors = [[NSMutableArray alloc] init];
NSSortDescriptor *dueDateDescriptor = [[NSSortDescriptor alloc] initWithKey:@"dueDate"
                                                                  ascending:YES];
[descriptors addObject:dueDateDescriptor];
[dueDateDescriptor release];
[fetchRequest setSortDescriptors:descriptors];

fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"category" cacheName:@"Root"];

A tabela inicialmente exibe boa, mostrando os itens inacabados cuja data de vencimento não passou em uma seção intitulada "Em andamento". Agora, o usuário pode tocar em uma linha na exibição de tabela, que coloca uma nova exibição de detalhes na pilha de navegação. Nesta nova visualização, o usuário pode tocar em um botão para indicar que o item agora está "Concluído". Aqui está o manipulador do botão (self.task é o objeto gerenciado):

- (void)taskDoneButtonTapped
{
    self.task.finishedDate = [NSDate date];
}

Assim que o valor do atributo "finishedDate" mudar, eu sou atingido com esta exceção:

2010-03-18 23:29:52.476 MyApp[1637:207] Serious application error.  Exception was caught during Core Data change processing: no section named 'Done' found with userInfo (null)
2010-03-18 23:29:52.477 MyApp[1637:207] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no section named 'Done' found'

Consegui descobrir que o UITableView atualmente oculto pela nova exibição de detalhes está tentando atualizar suas linhas e seções porque o NSFetchedResultsController foi notificado de que algo mudou no conjunto de dados. Aqui está o código de atualização da minha tabela (copiado da amostra Core Data Recipes ou da amostra CoreBooks - não me lembro qual):

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch(type)
    {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

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

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

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    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
{
    [self.tableView endUpdates];
}

Coloquei pontos de interrupção em cada uma dessas funções e descobri que apenas o controllerWillChange é chamado. A exceção é lançada antes que qualquer controlador: didChangeObject: atIndexPath: forChangeType: newIndex ou controlador: didChangeSection: atIndex: forChangeType sejam chamados.

Neste ponto, eu estou preso. Se eu alterar meu sectionNameKeyPath para apenas "dueDate", tudo funcionará bem. Eu acho que é porque o atributo dueDate nunca muda, enquanto a categoria será diferente quando lida novamente após a alteração do atributo finishDate.

Por favor ajude!

ATUALIZAR:

Aqui está o meu código UITableViewDataSource:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [[self.fetchedResultsController sections] count];
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    [self configureCell:cell atIndexPath:indexPath];    

    return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];    
    return [sectionInfo name];
}

questionAnswers(2)

yourAnswerToTheQuestion