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
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.
"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
Related
I'm trying to fetch two entities eg: Student with email attribute and Professor, both of have same parent entity eg: Person with attributes entityId, firstName and lastName i want to generate them in two sections using NSFetchedResultsController.
Here is a part from getter for fetchedResultsController
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setFetchBatchSize:20];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *description = [NSEntityDescription entityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:description];
NSSortDescriptor *firstNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"firstName" ascending:YES];
NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:YES];
[fetchRequest setSortDescriptors:#[firstNameDescriptor, lastNameDescriptor]];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"entityId"
cacheName:nil];
All Students have the same entityId and all Professors too
In tableView I have two prototype cells one for Student and another for Professor.
I get two sections as expected but students are in different sections, i have printed all objects from fetchedResultsController in console like this, po [self.fetchedResultsController objectAtIndexPath: [NSIndexPath indexPathForItem:1 inSection:1]] all professors are printed with fault:
<Professor: 0x6080000befc0> (entity: Professor; id: 0xd0000000001c0002 <x-coredata://03A3ECAD-CCA7-424E-86F9-258D25372BA1/Professor/p7> ; data: <fault>)
I have forced the fetch request to return full objects using [request setReturnsObjectsAsFaults:NO] but it had no effect.
Why is it happening so?
To avoid "data fault" issue you should set this field of NSFetchRequest:
request.returnsObjectsAsFaults = NO;
To separate students from professors in two sections you can use multiple NSFetchedResultsControllers, as described here:
https://stackoverflow.com/a/2309855/1689376
To avoid duplication of your code, just create a method like this and call it twice:
- (NSFetchedResultsController) createFetchedResultsController: (NSString *) entityName {
//move your code here
}
I want to show Project such that it shows all the projects on the basis of sharedToUsers basis. Means section name with User.user_id and it should shows all the shared project with that user. but i am not able to set section value for NSFetchedResultsController properly because it is a too many relation and it is crashing on sectionNameKeyPath .
Any Help
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *theParent = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:_managedObjectContext];
//the name key exsit in parent entity of project.
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:10];
[fetchRequest setEntity:theParent];
[fetchRequest setPredicate:_predicate];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_managedObjectContext sectionNameKeyPath:#"ANY sharedToUsers.user_id"
cacheName:[self getCacheNameForNSFetchedResultsController]];
if you have a senario like i have then when ever you change data in the core data then try donot use to add or update relation ship items using object of parent or category object. if you had to link some thing with category then try to link from child object.
donot use this
[category addRelationShipItem:child];
use this
`[child addToCategory:category];`
when you use this then it will not triger update call in NSFetchResultController call.
Happy coding.
Hi If you want fetch the Project shared by a user you need this predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *theParent = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:_managedObjectContext];
NSString *userId; // If you user_id is a NSNumber change you to it.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY sharedToUsers.user_id = %#",userId];
[fetchRequest setPredicate:predicate];
Last things you are using a NSSort: name , but name it's not a Project property.
In my app I have two entities which are Items and Lists. Each item belongs to only one list and each list has many items. Thus in the modal the entities has the following relationships:
For Items: to-one relationship (belongs_to_list) pointing to one list
For Lists: to-many relationship (has_items) pointing to many items
How can I fetch the items using a predicate to check whether the list it's related to is equal to a specific list I provide? I don't want to fetch the items through the list (like fetching objects of has_items). I want to be able to use belongs_to_list in a predicate to compare it to a managed object I have. I tried the following but it's not working. Any help please?
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Items" inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"item_detail" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"list.list_name == %#", [self.currentList valueForKey:#"list_name"]];
[fetchRequest setPredicate:predicate];
If I understood well your question, the following predicates would be correct:
[NSPredicate predicateWithFormat:#"belongs_to_list == %#", [self currentList]];
Let me know if this does work for you.
I am using an NSFetchedResultsController to display data in a table. The data store contains lots of rows that have a groupID, and multiple entities can share a groupID.
Each entity has an insertion date property as well.
I would like to get a list of distinct groupID ordered by the insertion date property. The following code works, except it is not sorted by the insertion date property. I assume that since that property is not one of the ones being fetched, it is not available for sorting.
And I am using MagicalRecord as well, fwiw.
Is there a way to use other properties of the entity for sorting but not have them as part of the result? Adding in the insertion date to the fetched properties makes distinct superfluous since the dates are all unique as they are.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userID like[c] %#", THEUSERUSERID];
NSFetchRequest *fetchRequest = [AnEntity MR_requestAllWithPredicate:predicate];
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:#[#"groupID"]];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"inDate" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
_frController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[NSManagedObjectContext MR_rootSavingContext] sectionNameKeyPath:nil cacheName:nil];
Chadbag ... I had the same problem. I researched the setPropertiesToFetch and found out it requires an NSArray of NSPropertyDescriptions ... not just the attribute name.
I think if you REMOVE this:
[fetchRequest setPropertiesToFetch:#[#"groupID"]];
and ADD this:
NSDictionary *entityProperties = [entity propertiesByName];
NSPropertyDescription *propDescription = [entityProperties objectForKey:#"groupID"];
NSArray *propArray = [NSArray arrayWithObject:propDescription];
[fetchRequest setPropertiesToFetch:propArray];
That should work for you ... it did for me!
I am new to iOS Developing, and I'm getting my feet wet in Core Data. I've got an app that I'm slowly piecing together (thanks to help from this site!), but I'm stuck on how to fetch data.
The app:
My app has two main screens, both UITableViews: A "Class List" view where they can add new classes, and an "Add My data model has two entities: Course (for class) and Student, with a to-many relationship from Course-Student. Right now I have it working so that when a I tap on a class in the "Class List" view I am taken to the "Add Students" view where I can add students to the class, but my fetch results controller is returning all students that I have added. My question: how do I format the fetch request in the "Add Students" view to fetch only those students that should belong to that class? Here is the fetched results controller I have right now:
-(NSFetchedResultsController *) fetchedResultsController {
if (_fetchedResultsController !=nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Student" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"name" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
I'm guessing it has something to do with predicates, but I haven't gotten that far in my learning. Any help at all would be appreciated. Thanks!
You are guessing right. To fetch all students that belong to a particular course,
add the following predicate to the fetch request:
Course *theCourse = ...; // your Course object
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"course = %#", theCourse];
[fetchRequest setPredicate:predicate];
(assuming that the to-one relationship from Student to Course is called "course").