Search code examples
iosobjective-ccore-datansfetchrequestnssortdescriptor

Core Data one-to-many sorting


I have CoreData object "Entity" with @property (nonatomic, retain) NSSet *ImagesSet;

Inside ImagesSet are many "ImageEntity" objects.

"ImageEntity" have @property (nonatomic, retain) NSNumber * sortKey;

How create fetchRequest for "Entity" with imagesSet ordered by sortKey field?

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Entity"
                                          inManagedObjectContext:[[DataManager sharedInstance] mainObjectContext]];
[fetchRequest setEntity:entity];
NSString * predicate = some predicate string......;
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:predicate]];
[fetchRequest setFetchBatchSize:2500];
[fetchRequest setFetchLimit:25];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"ImageEntity.sortKey" ascending:NO];

NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

[fetchRequest setSortDescriptors:sortDescriptors];

NSFetchedResultsController *frc = nil;

 frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                          managedObjectContext:mainObjectContext
                                            sectionNameKeyPath:nil
                                                     cacheName:cashName];

Program crashes with message "***** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'**"


Solution

  • When you fetch Entity objects, there is no way to control the order of the related ImageEntity objects as part of the fetch. You have a few options instead:

    1. Specify in your data model that the relationship is ordered. The relationship will be represented as NSOrderedSet instead of NSSet. You will have to ensure that the set is ordered as you need (instead of using sortKey).

    2. Keep the relationship as unordered (NSSet), and sort the set when you need to. Eg.

      NSSortDescriptor *imageSort = [NSSortDescriptor sortDescriptorWithKey:@"sortKey" ascending:NO];
      NSArray *sortedImages = [myEntity.imagesSet sortedArrayUsingDescriptors:@[imageSort]];
      
    3. Fetch the ImageEntity objects directly, using a predicate to filter the results to the relevant Entity object, and the required sort descriptor:

      NSSortDescriptor *imageSort = [NSSortDescriptor sortDescriptorWithKey:@"sortKey" ascending:NO];
      NSFetchRequest *imageFetch = [NSFetchRequest fetchRequestWithEntityName:@"ImageEntity"];
      imageFetch.sortDescriptors = @[imageSort];
      imageFetch.predicate = [NSPredicate predicateWithFormat:@"entity == %@",myEntity];
      NSError *error;
      NSArray *sortedImages = [context executeFetchRequest:imageFetch error:&error];