読者です 読者をやめる 読者になる 読者になる

hachinoBlog

hachinobuのエンジニアライフ

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

CoreDataを使用したコードでNSFetchedResultsControllerを作成する際に
下記コードのようにsectionNameKeyPathにEntityモデルのカラムを連結した値をセットして実行したら

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

  NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
  [fetchRequest setEntity:Entity];

  NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:@"columnA" ascending:YES];
  NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:@"columnB" ascending:YES];
  NSSortDescriptor *sort3 = [[NSSortDescriptor alloc] initWithKey:@"columnC" ascending:YES];
  NSSortDescriptor *sort4 = [[NSSortDescriptor alloc] initWithKey:@"columnD" ascending:YES];

  [fetchRequest setSortDescriptors:@[sort1, sort2, sort3, sort4]];
  [fetchRequest setFetchBatchSize:20];

  NSFetchedResultsController *theFetchedResultsController =
  [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:[CoreData instance].managedObjectContext sectionNameKeyPath:@"sectionKey"
                                               cacheName:nil];
  self.fetchedResultsController = theFetchedResultsController;

  return _fetchedResultsController;
}

//EntityオブジェクトのsectionKeyメソッド
- (NSString *)sectionKey
{

  return [NSString stringWithFormat@"%@ %@ %@ %@ %@", [self columnA], [self columnB], [self columnC], [self columnD], [self columnE]];

}

The fetched object at index x has an out of order section name 'columnA columnB columnC columnD columnE. Objects must be sorted by section name
というエラーが出た。
これはCoreDataの制約でsectionNameKeyPathで指定したものとsetSortDescriptorsで設定した第1ソートキーは同一であることという制約が守られていない状況に出る。
正し、sectionNameKeyPathはカラムを5つ連結した文字列であるので今回は第5ソートまでsectionNameKeyPathで指定したカラムを定義してあげないといけない。

  //修正前のコード(エラーが出る)
  NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:@"columnA" ascending:YES];
  NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:@"columnB" ascending:YES];
  NSSortDescriptor *sort3 = [[NSSortDescriptor alloc] initWithKey:@"columnC" ascending:YES];
  NSSortDescriptor *sort4 = [[NSSortDescriptor alloc] initWithKey:@"columnD" ascending:YES];

  [fetchRequest setSortDescriptors:@[sort1, sort2, sort3, sort4]];

  //修正後のコード
  NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:@"columnA" ascending:YES];
  NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:@"columnB" ascending:YES];
  NSSortDescriptor *sort3 = [[NSSortDescriptor alloc] initWithKey:@"columnC" ascending:YES];
  NSSortDescriptor *sort4 = [[NSSortDescriptor alloc] initWithKey:@"columnD" ascending:YES];
  NSSortDescriptor *sort5 = [[NSSortDescriptor alloc] initWithKey:@"columnE" ascending:YES]; //漏れていた

  [fetchRequest setSortDescriptors:@[sort1, sort2, sort3, sort4, sort5]];

CoreDataを使う際に本来モデルファイルには存在しないカラムなど自作したものをsectionNameKeyPathに指定したら
それに応じて整合性が保たれるようにsortDescriptorsをセットしてやること。