I'm using the following code to fetch data out of my Core Data graph:
- (void)setupFetchedResultsController
{
// 1 - Decide what Entity you want
NSString *entityName = #"Snag"; // Put your entity name here
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 3 - Filter it if you want
request.predicate = [NSPredicate predicateWithFormat:#"project.id = %#", projectPassedToController.id];
// 4 - Sort it if you want
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"dateTaken"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
However, I only want to return projects that have the same unique identifier as the one that is being passed to this View Controller (using the ProjectPassedToController variable)
I don't want to filter by the project.name, as this field is editable by the user. I need to filter by the project's unique identifier but i'm not sure how to do this.
Solved this by filtering on the relationship between the project and person.
request.predicate = [NSPredicate predicateWithFormat:#"belongsToProject = %#", _selectedProject];
Related
I have a core data entity, "Entity 1" it has a one to many relationship lets call it "entityRelationship" to another entity "Entity 2".
I'd like to be able to perform a NSFetchRequest for use with a NSFetchResultsController to return the list of "Entity 2" objects for a specific "Entity 1" object.
I have the "Entity 1" stored out as it's own variable, but i can't seem to find the correct way to set up an NSPredicate to return the objects:
Here's my code:
NSFetchedResultsController *fetchedEvents;
NSFetchRequest *fetchRequest;
NSError *error = nil;
fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Entity2"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"Entity2 IN self = %#",entity1Object]];
[fetchRequest setSortDescriptors:#[]];// no sort descriptors
fetchedEvents = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:theManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
[fetchedEvents performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
return fetchedEvents;
This crashes with the following error:
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "NSMDEvents IN self = %#"'
Am i doing something wrong? Or is this the incorrect way to go about returning entities with relationships?
Since you have entity1Object and the defined relationship, you can retrieve the Entity2 objects directly from there
NSSet *entity2Objects = [entity1Object valueForKey:#"entityRelationship"];
An extra fetch is not needed.
But if you really need the fetch define a reverse relationship and use a property with an unique value.
For example let's assume that entity1 are clubs and entity2 are their members and you want to get all members for a specific club use this predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Member"];
[NSPredicate predicateWithFormat:#"club.name == %#", currentClub.name];
The literal club in the predicate is the reverse relationship object.
Or translated to your example
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Entity2"];
[NSPredicate predicateWithFormat:#"entity1.property == %#", entity1Object.property];
Trying the suggested code (thanks vadian) kept causing my app to crash with various errors regarding keys not existing etc, this turned out to be down to a relationship issue.
"Entity2" was inheriting from another entity (had its parent Entity field set in the Data Model Inspector)"Entity 0". However the relationship between "Entity1" was between itself and "Entity0" not "Entity2".
So after a rejig of the core data model "Entity2" had a relationship added (lets call it "EntityEvents") between itself and "Entity1". Now using the following code i was able to select the specific events from the current object:
NSFetchedResultsController *fetchedEvents;
NSFetchRequest *fetchRequest;
NSError *error = nil;
fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Entity2"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"EntityEvents == %#",Entity1]];
[fetchRequest setSortDescriptors:#[]];// no sort descriptors
fetchedEvents = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:theManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
[fetchedEvents performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
return fetchedEvents;
I have a simple data model with two entities. A parent entity called Character and a child entity called Statiscis. A Character can have multiple Statistics and each statistic can have only one parent, so the relationship is many to one.
From the view controller that displays the details of a Character I call to a new Table VC to list all the Statistics related to this Character. On this controller I have a nice SIGABRT when I try to build the fetchedResultsController: "Unable to generate SQL for predicate (character == currentCharacter) (problem on RHS)".
When I create the Table VC I send the managedObjectContext and the character displayed on the details VC through two properties (same name) on prepareForSegue, so in the table VC self.currentCharacter hosts an instance of a Character managed object.
#pragma mark - NSFetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Statistic"];
// Stupid predicate :(
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"character == self.currentCharacter"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"statName"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
NSError *error = nil;
// Going to crash
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Core Data error: %#, %#", error, [error localizedDescription]);
abort();
}
return _fetchedResultsController;
}
Do not know how to create the predicate, and I tried unsuccessfully several ways
Perhaps:
[NSPredicate predicateWithFormat:#"character == %#", self.currentCharacter];
You want
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"character == %#", self.currentCharacter];
My Core Data app has two entities: "Note" and "Marker". The Note entity has a 1-to-many relationship to markers (ie. A note contains many markers). I have a fetchedRequestController which is responsible for fetching all the "Note" entities. After creating 1 note and 1 marker (which belongs to that note) I get an error because the fetchedRequestController fetches both the Marker and Note. The Note is expected but the Marker should not be fetched.
Here is my fetched request controller
TCModel *model = [TCModel sharedModel];
NSManagedObjectContext *context = [model managedObjectContext];
NSParameterAssert(context);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Note" inManagedObjectContext:context];
NSParameterAssert(entity);
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"creationDate" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:#"Root"];
NSParameterAssert(controller);
self.fetchedResultsController = controller;
controller.delegate = self;
NSError *error;
BOOL success = [controller performFetch:&error];
if ( success == NO )
{
NSLog(#"Failed to fetch!");
NSParameterAssert(nil);
}
The objects are created using two helper methods and then saved using a third
- (TCNote *)newNote
{
TCNote *note = [NSEntityDescription insertNewObjectForEntityForName:#"Note"
inManagedObjectContext:self.managedObjectContext];
note.creationDate = [NSDate new];
return note;
}
- (TCMarker *)newMarker
{
TCMarker *marker = [NSEntityDescription insertNewObjectForEntityForName:#"Marker"
inManagedObjectContext:self.managedObjectContext];
return marker;
}
- (void)_save
{
NSError *error;
NSLog(#"Saving");
if (![self.managedObjectContext save:&error])
{
NSLog(#"Error saving context: Error = %#", error);
}
}
Here is the console output:
2013-01-03 17:41:12.062 timecode[10269:c07] CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
2013-01-03 17:41:12.063 timecode[10269:c07] CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK, t0.Z_OPT, t0.ZNOTEDATE, t0.ZSCENEDESCRIPTION, t0.ZSCENETITLE, t0.ZTIMECODEDATE, t0.ZDATE, t0.ZTITLE, t0.ZPARENT, t0.Z1_PARENT FROM ZNOTE t0
2013-01-03 17:41:12.064 timecode[10269:c07] CoreData: annotation: sql connection fetch time: 0.0005s
2013-01-03 17:41:12.064 timecode[10269:c07] CoreData: annotation: total fetch execution time: 0.0011s for 2 rows.
You have defined Note as "parent entity" of Marker, i.e. Marker is a "child entity" of Note:
That means that every Marker object is also a Note object (same as with class and subclass). Fetching all objects of the Note entity does therefore also return the objects of the Marker entity.
The SQLite file contains in this case only one table ZNOTE, which has columns for the properties of Note and for the additional properties of Marker.
So if you don't really need Marker to be a child entity of Note, just set the "Parent Entity" to "No Parent Entity".
I am creating an iOS 5 app using Core Data.
I have two entities Item and Category there is a many-to-many relationship between these two entities, i.e. an Item belongs to many categories and a Category has many items.
What I am trying to do is, given a category, I want to know all the categories the items of that category is associated with. For that I am trying to execute the following request:
NSEntityDescription *catEntity = [NSEntityDescription entityForName:#"Category" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = catEntity;
request.predicate = [NSPredicate predicateWithFormat:#"items IN %#",[category.items allObjects]];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]];
NSError *error = nil;
NSArray *categories = [context executeFetchRequest:request error:&error];
But when executing this I get the following error:
'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (items IN {<Item: 0x89b9c50> (entity: Item; id: 0x89be960 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Item/p17> ; data: {
attributes = (
);
categories = (
"0x89be540 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Category/p28>",
"0x89be680 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Category/p29>"
);
name = "Some Item";
})})'
What am I doing wrong here?
Try this, I'm not sure but it might work:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY items IN %#", [category.items allObjects]];
is it possible, to filter the results of a NSFetchedResultsController without a new call to the databaselayer, like I do it with an NSArray with "filteredArrayUsingPredicate"
Thanks
You can set a predicate on the NSFetchRequest that you use to initialize your NSFetchedResultsController. For example:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = <YOUR ENTITY>
fetchRequest.predicate = [NSPredicate predicateWithFormat:<YOUR PREDICATE>];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc sectionNameKeyPath:nil cacheName:nil];
// ...
You shouldn't need to "refresh" the fetched results controller since it should update as changes are made and saved. You may need to use the boilerplate code for using and/or updating a table view with a fetched results controller.