How to sort Core Data fetched properties
Asked Answered
Z

8

22

The Core Data Documentation states that:

The fetch request associated with the [fetched] property can have a sort ordering, and thus the fetched property may be ordered.

How do I specify the sort descriptors for the fetched property in Xcode's data model editor? I can't find a relevant field anywhere. I'm developing for the iPhone platform, if this makes any difference.

If this is not possible via the graphical model editor, how do I go about modifying the fetch request for the fetched property in code so that it has a sort descriptor?

Zany answered 1/7, 2009 at 23:9 Comment(0)
L
5

The modeling tool doesn't appear to have a way to set the sort descriptors on the fetch request.

It should be possible[1] to, after loading the model but before associating it with a persistent store coordinator, to find the fetched property descriptions for which you want to control the sort order, and replace their fetch requests with fetch requests that have sort descriptors set on them.

[1] In principle this should work. In practice, I have not done so or tested it.

Lammas answered 2/7, 2009 at 22:22 Comment(2)
It's too bad the modeling tool doesn't have this feature -- seems like it largely diminishes the utility of being able to specify fetch requests in the tool.Zany
This question provides an example of programmatically creating a NSFetchedPropertyDescription and adding it to a NSManagedObjectModel: #5638864Tague
A
37

You can actually grab the model fetched property and add the sort descriptors to it (again, in code). I did this in the standard method that XCode generates in your AppDelegate if you choose one of the templates with Core Data:

By the way. This sorts ALL fetched properties on ALL models in your data model. You could get fancy and adaptive with it, but it was the most succinct way to handle sorting the 7 separate models that each had fetched properties that needed to be sorted by name. Works well.

/**
 Returns the managed object model for the application.
 If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
 */
- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }
    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];    

    // Find the fetched properties, and make them sorted...
    for (NSEntityDescription *entity in [managedObjectModel entities]) {
        for (NSPropertyDescription *property in [entity properties]) {
            if ([property isKindOfClass:[NSFetchedPropertyDescription class]]) {
                NSFetchedPropertyDescription *fetchedProperty = (NSFetchedPropertyDescription *)property;
                NSFetchRequest *fetchRequest = [fetchedProperty fetchRequest];

                // Only sort by name if the destination entity actually has a "name" field
                if ([[[[fetchRequest entity] propertiesByName] allKeys] containsObject:@"name"]) {
                    NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
                    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortByName]];
                    [sortByName release];
                }
            }
        }
    }

    return managedObjectModel;
}
Analogize answered 26/9, 2009 at 5:19 Comment(1)
Also, Key Value Coding can be a help with this too, for instance: [managedObjectModel setValue:sortDescriptorArray forKeyPath:@"entitiesByName.EntityName.propertiesByName.fetchPropertyName.fetchRequest.sortDescriptors"];Swifter
P
13

You don't specify them in the graphical editor (as far as I know).

You specify them in the code where you make the fetch.

NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription entityForName:@"whatYouAreLookingFor"
    inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];

// here's where you specify the sort
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc]
                                initWithKey:@"name" ascending:YES];
NSArray* sortDescriptors = [[[NSArray alloc] initWithObjects: sortDescriptor, nil] autorelease];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];

fetchedResultsController = [[NSFetchedResultsController alloc]
               initWithFetchRequest:request
               managedObjectContext:self.managedObjectContext
                 sectionNameKeyPath:nil
                          cacheName:@"myCache"];
Peta answered 2/7, 2009 at 22:31 Comment(2)
This is correct above. You have to add the NSSortDescriptor you cannot specify this in your datamodel sorry. Yes its long winded, but this is exactly what I am doing in my core data app.Petunia
The above is fine for a normal fetch request but has nothing to do with the question, which is about fetched properties and their sorting.Compel
L
5

The modeling tool doesn't appear to have a way to set the sort descriptors on the fetch request.

It should be possible[1] to, after loading the model but before associating it with a persistent store coordinator, to find the fetched property descriptions for which you want to control the sort order, and replace their fetch requests with fetch requests that have sort descriptors set on them.

[1] In principle this should work. In practice, I have not done so or tested it.

Lammas answered 2/7, 2009 at 22:22 Comment(2)
It's too bad the modeling tool doesn't have this feature -- seems like it largely diminishes the utility of being able to specify fetch requests in the tool.Zany
This question provides an example of programmatically creating a NSFetchedPropertyDescription and adding it to a NSManagedObjectModel: #5638864Tague
O
5

Using Tim Shadel's great answer I added per-NSManagedObject subclass sorting...

...in Tier.m (which is a NSManagedObject subclass)...

+ (void)initialize
{
    if(self == [Tier class])
    {
        NSFetchedPropertyDescription *displayLessonPropertyDescription = [[[Tier entityDescription] propertiesByName] objectForKey:@"displayLesson"];
        NSFetchRequest *fetchRequest = [displayLessonPropertyDescription fetchRequest];

        NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"displayOrder" ascending:YES];
       [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortByName]];
        [sortByName release];
    }
}
Oppugnant answered 4/11, 2012 at 1:22 Comment(3)
Where is -[Tier entityDescription] defined?!Tumpline
Ah, I'm using RestKit in this situation which has a bunch of convenience methods like that. Check out their NSManagedObject+ActiveRecord category.Oppugnant
Not sure why @AndyOS's edit was rejected, I definitely had an error in my code.Oppugnant
I
1

For a single fetched property, Swift 4, Xcode 9.4:

// retrieve the fetched property's fetch request    
let fetchedPropertyRequest = (modelName.entitiesByName["entityName"]!.propertiesByName["fetchedPropertyName"] as! NSFetchedPropertyDescription).fetchRequest

// set up the sort descriptors
let sortDescriptors = [NSSortDescriptor(key: "keyName", ascending: true)]

// add the sort descriptors to the fetch request
fetchedPropertyRequest!.sortDescriptors = sortDescriptors

Here's the same thing the loooonnnnnnggggggg way:

// retrieve the fetched property's fetch request
let theEntityDescription: NSEntityDescription = modelName.entitiesByName["entityName"]!
let theFetchedPropertyDescription = theEntityDescription.propertiesByName["fetchedPropertyName"]! as! NSFetchedPropertyDescription
let theFetchedPropertyRequest = theFetchedPropertyDescription.fetchRequest

// set up the sort descriptors
let sortDescriptor1 = NSSortDescriptor(key: "keyName", ascending: true)
let theSortDescriptors = [sortDescriptor1]

// add the sort descriptors to the fetch request
theFetchedPropertyRequest!.sortDescriptors = theSortDescriptors

Note: for this example, I force-unwrapped values. Make sure that you account for optional values in your actual code!

Incommunicable answered 30/6, 2018 at 23:33 Comment(0)
S
0

Sadly, though, the ability to sort is somewhat limited. For example, you cannot take a field that is an NSString containing a number, and sort it numerically, at least not with a SQLite backing store. As long as you are sorting alphabetically on strings, numerically only on values stored as numbers and so forth, though, the NSSortDescriptor applied to the fetch request works just fine.

Signory answered 6/9, 2009 at 16:13 Comment(1)
You can write more sophisticated sorting methods, using NSSortDescriptor's initWithKey:ascending:selector:Okwu
R
0

Put this into your NSManagedObject subclass:

+ (void)initialize
{
    if (self != [EntityManagedObjectSubClass class]) return;
    NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    NSEntityDescription *entityDescription = [managedObjectModel entitiesByName][@"entityName"];
    NSFetchedPropertyDescription *fetchedPropertyDescription = [entityDescription propertiesByName][@"fetchedPropertyName"];
    NSFetchRequest *fetchRequest = [fetchedPropertyDescription fetchRequest];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sortDescriptorKey" ascending:YES];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
}

Replace EntityManagedObjectSubClass, entityName, fetchedPropertyName and sortDescriptorKey with your own stuff.

Radioscope answered 23/1, 2013 at 13:1 Comment(0)
S
-1

Jeff, if the strings are right-aligned, you could just sort on the strings; " 123" > " 23" and so on. But iirc ascii space is after the numbers, and if so, then what you would do is create a dynamic property that is an NSNumber (which supports the compare: method), and use the numberFromString: method to make a number from the string. Then you can specify the number field in the sort. In the interface:

@property NSString *stringIsaNumber; // in the data model
@property NSNumber *number;

in the implementation:

@dynamic stringIsaNumber; 
- (NSNumber *) number ;
{ return [self.stringIsaNumber numberFromString]; }
- (void) setNumber:(NSNumber *)value;
{ self.stringIsaNumber = [NSString stringWithFormat:@"%5i",value) }

ps plz forgive coding errors, this is off the top of my head.

Stingy answered 16/2, 2011 at 0:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.