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;
Related
I have 3 entities in my data model which are connected as follow:
User<-->>Performance<-->>Trials
Meaning that every user has several performances, in each he/she goes under several trials.
For each entity, I have one table view and one array controller object. For all, I have bound their Managed Object Context parameter to App Delegate.
Then I bound PerformanceArrayController to UserArrayController Content Set (Controller Key: selection and performances relationship). And the same for TrialArrayController: I bound it to PerformanceArrayController (on selection) and trials relationship.
I have no problem in binding single columns of User table view and performance table view to entities attributes. but when I want to do the same for trial table view, first I don't get autocompletion and second when I write the name of the attributes manually, I get a gray exclamation mark. and only the first trial is saved this way but not the rest of them.
Here is my function for inserting into Trial:
- (void) insertIntoTrial: (NSString *) result
{
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
Trial *trial = [NSEntityDescription insertNewObjectForEntityForName:#"Trial" inManagedObjectContext:context];
trial.result = result;
trial.time = [NSNumber numberWithDouble:[cueTimestamp timeElapsedInSeconds]];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Performance" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"user.fName==%#", userName]];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (Performance *performance in fetchedObjects) {
[performance addTrialsObject:trial];
trial.performance = performance;
}
if (![context save:&error]) {
NSLog(#"couldn't save Trial info: %#", [error localizedDescription]);
}
}
Thanks in advance,
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];
I'm about to pull my hair out trying to figure out why this isn't working.
I have two entities:
Quote
Customer
A Quote has a one-to-one relationship property with a Customer called simply 'customer'. The Customer has a CoreData objectID (obviously). I am trying to search through all the Quote's and return the one's that have a specific Customer associated with it based off the Customer objectID. Reading through all the tutorials I've managed to get this but I keep getting a crash:
+ (void)fetchQuotesForCustomerID:(NSManagedObjectID*)objectID results:(void(^)(NSError* error, NSArray *fetchedResults))completion {
NSManagedObjectContext *context = [[QuoteGenerator sharedGenerator] managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Quote"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"customer.objectID == %#", objectID];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"fetch error = %#", [error localizedDescription]);
completion(error, nil);
} else {
NSLog(#"fetch count = %d", fetchedObjects.count);
completion(nil, fetchedObjects);
}
}
Output error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath customer.objectID not found in entity <NSSQLEntity Quote id=13>'
Is the predicated setup wrong? Reading through the documentation is says that you can use dot syntax to access properties in the predicate.
Please help...
Turns out a lack of sleep and #Gary lead me to the right answer.
I should have had a to-many relationship from customer to Quote.
When comparing an entities NSManagedObjectID property you don't have to explicitly state it. So the following modification to the NSPredicate fixed my issue.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY
customer == %#", objectID];
You shouldn't need to do a fetch at all, core data should generate a quotes member on your customer object that will return an NSSet of all the related quote objects.
I have the following Core Data model (simplified for example):
Person
->Address
City
>Region
RegionName
>Country
CountryName
When a new Person is created
NSManagedObjectModel *objectModel=[[AppCoreData sharedInstance]objectModel];
NSEntityDescription *entity=[[objectModel entitiesByName] valueForKey:#"Beverage"];
Person *person=(Person*)[[NSManagedObject alloc]initWithEntity:entity insertIntoManagedObjectContext:nil];
a Person entity is created w/o inserting into the object context so it is easy to abandon the insert if necessary.
Then the user can select a Region which may or may not exist in the database. A search is performed to see if the Region exists
NSEntityDescription *entityDescription=[NSEntityDescription entityForName:#"Region" inManagedObjectContext:self.objectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
request.returnsObjectsAsFaults=NO;
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"id == %#",region.id];
[request setPredicate:predicate];
NSError *error=nil;
NSArray *array= [self.objectContext executeFetchRequest:request error:&error];
if ([array count] == 1) {
//use existing object
person.region=(Region*)[array objectAtIndex:0];
} else {
//create new object
NSEntityDescription *entity=[[objectModel entitiesByName] valueForKey:#"Region"];
self.collectionItem.beverage.region=(Region*)[[NSManagedObject alloc]initWithEntity:entity insertIntoManagedObjectContext:nil];
person.region.id=[NSNumber numberWithInt:[regionID intValue]];
person.region.regionName=regionName;
}
Finally, if the user does not abandon the insert, the person object is inserted into the object context and saved
[self.objectContext insertObject:self.collectionItem];
What is the best approach to dealing with nested Core Data objects where the nested objects, e.g., Region may or may not exist in Core Data? Examples or references appreciated.
I have two core data entities, Articles and Favorite. Articles has To-Many relationship to Favorite. First, I inserted all Articles object successfully.
Now, I'm trying to insert ArticleID in “Favorite” entity but I cant. Either the record is inserted with an empty relationship or it is inserted with new record in “Articles” entity.
I think that I should be getting the related record in Articles entity first and then using it to insert in Favorite but I'm not sure how to do this. My current code:
NSManagedObjectContext *context =[appDelegate managedObjectContext] ;
favorite *Fav =[NSEntityDescription insertNewObjectForEntityForName:#"favorite" inManagedObjectContext:context];
Articles * Article = [NSEntityDescription insertNewObjectForEntityForName:#"Articles" inManagedObjectContext:context];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Articles" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *secondpredicate = [NSPredicate predicateWithFormat:#"qid = %#",appDelegate.GlobalQID ];
NSPredicate *thirdpredicate = [NSPredicate predicateWithFormat:#"LangID=%#",appDelegate.LangID];
NSPredicate *comboPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects: secondpredicate,thirdpredicate, nil]];
[fetchRequest setPredicate:comboPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
// ?????????????????????????
}
}
Any suggestions would be appreciated.
First, make sure you have a reciprocal i.e. two way relationship between Article and Favorite. Something like this:
Article{
favorites<-->>Favorite.article
}
Favorite{
article<<-->Article.favorites
}
Defining reciprocal relationships in Core Data means that setting the relationship from one side automatically sets it for the other.
So, to set a new Favorite object for a newly created Article object you would just:
Favorite *fav =[NSEntityDescription insertNewObjectForEntityForName:#"favorite" inManagedObjectContext:context];
Articles *article = [NSEntityDescription insertNewObjectForEntityForName:#"Articles" inManagedObjectContext:context];
[article.addFavoriteObject:fav];
//... or if you don't use custom NSManagedObject subclasses
[[article mutableSetValueForKey:#"favorites"] addObject:fav];
If either the Article object or the Favorite object already exist, you would fetch the object first but setting the relationship would work exactly the same way.
The key is to make sure you have the reciprocal relationship so that the managed object context knows to set the relationship in both objects.
I solved it by creating new Article object:
Articles *NewObj = [fetchedObjects objectAtIndex:0];
and use it to insert relationship :
[Fav setFavArticles:NewObj];
[NewObj setArticlesFav:Fav];
thanks very much TechZen..