Rog
Rog

Reputation: 18670

Coredata fetching and grouping objects in sections

I am working with a pretty complex object model and am having a bit of trouble with breaking some of my fetches down into sections to display in a tableview.

I have the need to group Meeting managed objects into a few different "pockets" such as project, client and a few others. For several reasons I've decided to implement these as tags that can be associated with a Meeting entity.

So I have created a new Tag entity which has a type and a value and established the relationships between the two:

Meeting <<-->> Tag

If I want to associate a Meeting with a project, I create a Tag with name 'project' and value 'Project Name', then add it to the Meeting entity via the relationship.

I initially thought about using a NSFetchedResultsController but I am getting all sorts of issues all of which I don't really understand.

For instance, this fetch (I'm omitting the unnecessary bits):

NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:[Meeting entityName] inManagedObjectContext:moc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"tags.name contains[] 'client'"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[fetch setEntity:entity];
[fetch setPredicate:predicate];
[fetch setSortDescriptors:sortDescriptors];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch 
                                                                      managedObjectContext:moc 
                                                                        sectionNameKeyPath:@"self.tags.value" 
                                                                                 cacheName:nil];

In this particular case the fetch does work but somehow I am getting unexpected results where not only Tags with value client are presented but also ones with value project???

If I change my predicate to tags.name == 'project' I get an exception:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'

I'm probably missing something basic here and admittedly I don't have a lot of experience with predicates but Apple's documentation on the subject leaves a lot to be desired.

As a side question and something that I don't understand either is why do I have to add self to my sectionNameKeyPath in self.tags.value? What does it do?? In this case if I don't add it I get an exception thrown as well:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid to many relationship in setPropertiesToFetch: (tags.value)

Finally, what is the alternative to using fetched results controller in this case? Will it be a bunch fetch requests where I first get each instance of Tag where name == 'project' and iterate through the array to pull the Meeting objects associated with it? It seems highly inefficient but all I can think of at the moment so if you have any other ideas I am very interested in hearing them.

Many thanks in advance for your time,

Rog

Upvotes: 2

Views: 4684

Answers (2)

Nathan de Vries
Nathan de Vries

Reputation: 15501

The issue is that Meeting has-many tags, so you're going to need to use aggregate operations:

NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:[Meeting entityName] inManagedObjectContext:moc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY tags.name contains[cd] 'client'"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[fetch setEntity:entity];
[fetch setPredicate:predicate];
[fetch setSortDescriptors:sortDescriptors];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch 
managedObjectContext:moc 
sectionNameKeyPath:@"clientName"
cacheName:nil];

i.e. Give me a list of all Meeting objects where ANY of the tags are of type client, and group them by clientName. For the clientName key path to work, you'll need to implement a transient property:

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

    // Set clientName to the value of the first tag with name 'client'
    NSString* clientName = @"...";

    [self didAccessValueForKey:@"clientName"];
    return clientName;
}

If a number of your NSManagedObject subclasses need the clientName property, you can implement it in a common abstract NSManagedObject subclass and make your concrete subclasses inherit from that.

Upvotes: 4

bshirley
bshirley

Reputation: 8356

"Apple's documentation on [NSPredicate] leaves a lot to be desired" - totally agreed!

tags.name is not valid because tags is a collection, there's no object, there are (let's say) three of them

i think you want something like "tags contains %@", projectTag but i'm not sure of the syntax. might be "%@ IN %@", projectTag, thing.tags

Upvotes: 1

Related Questions