Reputation: 18670
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
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
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