title: Indexing results from an NSFetchedResultsController
body: <p>I'm having some issues with a mixed, indexed, searchable results set from an NSFetchedResults controller. I have it set up to store an indexed A-Z first initial for an entity, and then want it to display numeric first initials (i.e. # as the UILocalizedIndexCollation would do). </p>
  
  <p>I have already written the code that saves a "firstInitial" attribute of an Artist object as NSString @"#" if the full name started with a number, and I seem to have gotten the code half working in my UITableViewController with a customised sort descriptor. The problem is that it only works until I quit/relaunch the app. </p>
  
  <p>At this point, the # section from the fetched results appears at the top. It will stay there until I force a data change (add/remove a managed object) and then search for an entry, and clear the search (using a searchDisplayController). At this point the section re-ordering will kick in and the # section will be moved to the bottom...</p>
  
  <p>I'm obviously missing something/have been staring at the same code for too long. Alternatively, there's a much easier way of doing it which I'm not aware of/can't find on Google!</p>
  
  <p>Any help would be appreciated!</p>
  
  <p>Thanks</p>
  
  <p>Sean</p>
  
  <p>The relevant code from my UITableViewController is below.</p>
  
  <pre><code>- (void)viewDidLoad
  {
      // ----------------------------------
      // Various other view set up things in here....
      // ...
      // ...
      // ----------------------------------
  
      NSError *error;
      if (![[self artistResultsController] performFetch:&amp;error]) {
          // Update to handle the error appropriately.
          NSLog(@"Failed to fetch artists: %@, %@", error, [error userInfo]);
          exit(-1);  // Fail
      }
  
  }
  - (NSFetchedResultsController *)artistResultsController {
  
  if (_artistResultsController != nil) {
      return _artistResultsController;
  }
  
  NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
  NSEntityDescription *entity = [NSEntityDescription 
                                 entityForName:@"Artist" inManagedObjectContext:_context];
  [fetchRequest setEntity:entity];
  
  NSSortDescriptor *initialSort = [[NSSortDescriptor alloc] 
                            initWithKey:@"firstInitial" 
                            ascending:YES 
                            comparator:^(id obj1, id obj2) {
                                // Various number conditions for comparison - if it's a # initial, then it's a number
                                if (![obj1 isEqualToString:@"#"] &amp;&amp; [obj2 isEqualToString:@"#"]) return NSOrderedAscending;
                                else if ([obj1 isEqualToString:@"#"] &amp;&amp; ![obj2 isEqualToString:@"#"]) return NSOrderedDescending;
                                if ([obj1 isEqualToString:@"#"] &amp;&amp; [obj2 isEqualToString:@"#"]) return NSOrderedSame;
                                // Else it's a string - compare it by localized region
                                return [obj1 localizedCaseInsensitiveCompare:obj2];
                            }];
  
  NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
  
  [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:initialSort, nameSort, nil]];
  
  [fetchRequest setFetchBatchSize:20];
  
  NSFetchedResultsController *theFetchedResultsController = 
  [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                                      managedObjectContext:_context 
                                        sectionNameKeyPath:@"firstInitial" 
                                                 cacheName:nil];
  self.artistResultsController = theFetchedResultsController;
  _artistResultsController.delegate = self;
  
  [nameSort release];
  [initialSort release];
  [fetchRequest release];
  [_artistResultsController release];
  
  return _artistResultsController;}
  
  - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  {
      if (tableView == self.searchDisplayController.searchResultsTableView) {
          return nil;
      } else {
          return [[[_artistResultsController sections] objectAtIndex:section] name];
      }
  }
  
  - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
  {
      if (tableView == self.searchDisplayController.searchResultsTableView) {
          return nil;
      } else {
          return [[NSArray arrayWithObject:UITableViewIndexSearch] arrayByAddingObjectsFromArray:
                  [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles]];
  
      }
  }
  
  - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
  {
      if (tableView == self.searchDisplayController.searchResultsTableView) {
          return 0;
      } else {
          if (title == UITableViewIndexSearch) {
              [tableView scrollRectToVisible:self.searchDisplayController.searchBar.frame animated:NO];
              return -1;
          } 
          else {
              for (int i = [[_artistResultsController sections] count] -1; i &gt;=0; i--) {
                  NSComparisonResult cr = 
                  [title localizedCaseInsensitiveCompare:
                   [[[_artistResultsController sections] objectAtIndex:i] indexTitle]];
                  if (cr == NSOrderedSame || cr == NSOrderedDescending) {
                      return i;
                  }
              }
              return 0;
          }
      }
  }
  </code></pre>
  
  <p>EDIT: Forgot to mention - my search filter is using a predicate on the fetchedResults controller, so this causes a new fetch request, like so</p>
  
  <pre><code>- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
      NSFetchRequest *aRequest = [_artistResultsController fetchRequest];
  
      NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", searchText];
      // set predicate to the request 
      [aRequest setPredicate:predicate];
      // save changes
      NSError *error = nil;
      if (![_artistResultsController performFetch:&amp;error]) {
          NSLog(@"Failed to filter artists: %@, %@", error, [error userInfo]);
          abort();
      }  
  } 
  </code></pre>