I'm trying to learn how to use core data, but i got stuck on creating a relationship between two entities, i dont know if i'm looking at this at wrong angle, but basically, i have two Entities: "Listas" and "Tarefas". In my xcdatamodeld, i created a one-to-many relation between the objects, i just dont know how to set the correct relation when adding a "Tarefa".
To my question more clear, here is a image of what i have:
When adding a "Tarefas", object how do i relate it, with the passed "Listas" object?
Thank you!
It works the same as any other object attribute.
If you do not have custom NSManagedObject subclasses,
Listas *myListas = // passed in
Tarefas *newTarefas = [NSEntityDescription insertNewObjectForEntityForName:#"Tarefas" inManagedObjectContext:myManagedObjectContext];
[newTarefas setValue:myListas forKey:#"tarefaLista"];
If you do have custom NSManagedObject subclasses,
Listas *myListas = // passed in
Tarefas *newTarefas = [NSEntityDescription insertNewObjectForEntityForName:#"Tarefas" inManagedObjectContext:myManagedObjectContext];
[newTarefas setTarefaLista:myListas];
Keep in mind that since you have inverse relationships configured correctly, you only need to make the assignment on one side of the relationship. Core Data will make sure that the other side is also set. So above, I'm only setting a value for tarefaList, but the listaTarefa also gets a new value.
You could go:
Listas* l = //Your passed Listas
Terafas* t = //create your new Terafas
t.terafaLista = l;
It would be easier not to call your entities in a plural form (instead of Listas call it Lista). your code would make more sense.
As #TomHarrington mentioned, your inverse relationship will be automatically maintained.
I imagine that people will se this in future, so just in case, this is the code to filter the data based on the relationship between the two objects.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %#", self.currentListas.listaTarefa];
[fetchRequest setPredicate:predicate];
self.currentListas is the Lista object passed by the segue.
Related
Long question---thanks in advance for your time. After saving new managed objects, I am finding them added to a relationship on another object in my core data database---one for which my code calls no setter method and that has no inverse relationship. I have pored over the code and used logs to isolate the occurrence the best I can, but I'm encountering bizarre behavior I cannot explain (or fix).
More specifically:
I have an entity called PendingSyncTracker. It simply has one relationship, objectsToSync. I have not yet added any line in my code to call a setter method on this relationship. It is a to-many relationship. It points to BaseEntity. For the "Inverse" option, I have selected "No Inverse Relationship."
When I load a particular table view, 3 objects are downloaded from a server and then parsed into managed objects and saved. By the time the table view begins loading cells, 2 of those 3 objects will mystifyingly be present in the objectsToSync relationship.
I have used NSLog all over my code to figure out exactly when these objects can first be found as members of the objectsToSync set.
NSSet *objectsToSync = [[[SyncEngine sharedEngine] fetchClassNamed:#"PendingSyncTracker" withPredicates:nil][0] valueForKey:#"objectsPendingSync"];
NSLog(#"PendingSyncTracker objectsToSync set (%lu objects): %#", (unsigned long)[objectsToSync count], objectsToSync);
The answer to when they first appear in the set actually varies depending on where I do/don't place those 2 lines of code!
The objects are never found on the relationship before the managed object context is saved in the course of saving my 3 new core data objects.
If I don't use those 2 lines till I'm back in the Table View Controller that sent the new objects off to the Sync Engine to be stored locally (where the MOC is accessed and saved), then the log will there reveal that 2 objects have been added to the relationship.
If I use those 2 lines immediately after saving the MOC in the Sync Engine, then the logs will indicate (both there and back in the TVC) that only 1 object has been added to the relationship.
If I use those 2 lines immediately before and after saving the MOC (and back in the TVC), then all 3 logs will reveal that the relationship contains an empty set.
I also have those 2 lines at the beginning of cellForRowAtIndexPath. Regardless of prior logs, that log will always indicate that 2 objects have been added to the relationship.
All 3 of the managed objects that are created in the Sync Engine are stored as entity types that are subEntities of BaseEntity (to which the objectsToSync relationship points). The 2 types that get added to the relationship are each defined to have a reciprocal relationship, but with a different object, not PendingSyncTracker (although the different object is a subEntity of BaseEntity!).
So.. what explains these observations? How are these objects getting added to the relationship?
UPDATE:
- (NSArray*) fetchClassNamed:(NSString*)className withPredicates:(id)parameters;
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:className inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// set predicates
if (!(parameters == nil)) {
[fetchRequest setPredicate:parameters];
}
NSError *error;
NSArray *fetchedResults = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
return fetchedResults;
}
First, what does [[[SyncEngine sharedEngine] fetchClassNamed... do? Just a guess but it is doing something with KVC to set the relationship for you.
Also, you should always, always, always have an inverse relationship. Even if you never use it, Core Data does. Not having an inverse can lead to lots of issues, including but not limited to performance problems and potentially data corruption.
Add an inverse relationship and update your question with what -fetchClassNamed... does.
I have been at this single task for several days trying to get the relationships between core data entities working. I have achieved this but now I need to change it so that the new attribute value has its relationship added to an existing object. It is a 1 - to - many database.
I am not sure how to add a relationship to a object that already exists. So in the new object that is getting added to RoutineDetail, how would I create the relationship to the object that already exists in the routine Entity?
I have looked at several examples all showing how to add relationships to newly added objects but I need it so the new object in RoutinesDetails has a relationship with the value that already exists in Routines.
The value of Routines is held in a string called RoutineText
rout is the NSmangedObject for the entity Routines
routDet is the NSmanagedObject for the entity RoutinesDetails
I have left the commented out code that allows me to add a relationship when both new objects are created.
This is the last thing I have to do in my project but it is driving me insane. I will be eternally grateful for the fix here. Any advice will be appreciated as this is the best knowledge portal there is. Thank You.
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new device
ExcerciseInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];
//rout = [NSEntityDescription insertNewObjectForEntityForName:#"Routines" inManagedObjectContext:context];
routdet = [NSEntityDescription insertNewObjectForEntityForName:#"RoutinesDetails" inManagedObjectContext:context];
//Add attribute values
//[rout setValue: RoutineText forKey:#"routinename"];
[routdet setValue: info.name forKey:#"image"];
//Create Relationship
[rout addRoutinedetObject:routdet];
Your main problem statement is, I think, here:
I need it so the new object in RoutinesDetails has a relationship with the value that already exists in Routines.
I presume your data model looks like this:
Routine <----> RoutineDetail
i.e. every one routine has one routine detail (one-to-one relationship).
But this does not really make any sense. You could simply include the attributes of RoutineDetail in the Routine entity.
Instead of
desiredValue = routineDetail.routine.value;
you would simply have
desiredValue = routineDetail.value;
Also, please note that your code has a number of problems. The first line is completely unnecessary, just use self.managedObjectContext. Additionally, against the convention you are using Capital initials for variables. Those should be reserved for class names. Your method to add the relationship also does not look right.
You can add a relationship like this, without a method call:
routineObject.detail = detailObject;
I have a GameData entity which is meant to store an array of strings. So I made a 'Value' Entity which has a value string attribute and made a many-to-many relationship between the two entities.
To save the data I use the following code:
//Save values
NSMutableSet* values = [[NSMutableSet alloc] init];
for(NSString* n in gameData.values){
NSManagedObject *val = [NSEntityDescription
insertNewObjectForEntityForName:#"Value"
inManagedObjectContext:context];
[val setValue:n forKey:#"value"];
[values addObject:val];
}
[gd setValue:values forKey:#"values"];
The gameData.values array is currently empty so the code never actually goes into the for loop...but for some reason it crashes at this line [gd setValue:values forKey:#"values"] with the following error.
-[__NSSetM managedObjectContext]: unrecognized selector sent to instance 0x1f0485d0
Where or how am I sending a managedObjectContext selector to my values NSMutableSet??
Maybe you need to check whether the type of your entity is "To Many".
I cannot comment, thats why i am creating an answer.
Why don't you create the subclass for your entities using the xcode and import their header files and use code like below
//Save values
//NSMutableSet* values = [[NSMutableSet alloc] init]; -- No Need of this
for(NSString* n in gameData.values){
Value *val = [NSEntityDescription
insertNewObjectForEntityForName:#"Value"
inManagedObjectContext:context];
[val setValue:n]; // set your string
[val setGame:gd]; // set the game relation here. you can do this, if you have
configured inverse relations. If no create inverse relationship it will be helpful.
}
//[gd setValue:values forKey:#"values"]; you don't need this.
Now just save the context. Everything should be fine. This is much more cleaner than your way. I have never ever used key value for accessing core data entity properties because, it will be confusing and error prone as you have to remember the exact spelling of the property, and it wont throw any error if you have used wrong spelling or wrong key.
i think you should look at core data programming guide
Edit: If your GameEntity stores array of strings then one to many relationship is just enough. You need many to many only if GameEntity has many Strings and Each Strings i.e. Value entity also has many GameEntity. In that case the above code slightly changes.
Instead of
[val setGame:gd];
You need to use
[val addGameObject:gd];
I am trying to understand Core Data (To-Many) relationship. In the following code, I have two Entities
PeopleList <-->> TransactionDetails
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *personDetails = [NSEntityDescription
insertNewObjectForEntityForName:#"PeopleList"
inManagedObjectContext:context];
[personDetails setValue:[person fullName] forKey:#"name"];
NSManagedObject *transactionDetails = [NSEntityDescription
insertNewObjectForEntityForName:#"TransactionDetails"
inManagedObjectContext:context];
[transactionDetails setValue:[NSNumber numberWithFloat:oweAmount] forKey:#"amount"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
Now this code inserts a New Object (row) to the model. What I am confused with is:
1. Don't I have to write code for relating object values in two Entities (PeopleList and TransactionDetails)?
2. If I run this code again and again, It just keep on adding same object in first Entity (PeopleList). How to write for To-Many relationship? What I can get from last few hours of reading is I have to fetch the results, search for that particular object and if it exist then dont insert a new object with same name. But in that case, how will it relate the two entities.
Are your model entities correctly wired in the model editor as far as the to-many relationship is concerned? Have you generated the class files for your entities? If you can answer both questions with yes you create a personlist entity as you did and the details entity too but you need to the details to your personlist. Have a look into the class files for the method name(s).
It won't, because you're not setting the relationships on either of your objects. I don't see where you're setting the PeopleList property of your newly minted TransactionDetail object (sorry, I don't know how you've got the properties named in your model, so I'm just using the class names). So, after creating your transactionDetails object, you'd need to do something like transactionDetails.PeopleList = personDetails, and both relationships would be set at that point; transactionDetails.PeopleList property would point to your personDetails object, and personDetails.TransactionDetails set would contain transactionDetails.
What is your person object, that you're using to set the name from?
On another note, you might want to consider moving all this sort of stuff into subclasses of NSManagedObject; write your own super easy constructors/initializers, etc, for each of your entities. Lots of people never do this, and end up littering their controller code with lots of CoreData boiler plate, which is a mystery to me, because it's what makes using CoreData so nice.
In my core data object model I have 3 entities with appropriate relationships so that MyObject can have many MyObjectProperties, and each property can have one MyObjectPropertyImage.
Given a myObject I want to fetch all the images.
I try to do it using the following predicate, however I get an empty array:
[NSEntityDescription entityForName:#"MyObjectPropertyImage" inManagedObjectContext:managedObjectContext];
[NSPredicate predicateWithFormat:#"ANY myObjectProperty.myObject == %#", myObject];
Any ideas?
When working with Core Data it's best to think of your entities as just that: entities in an object graph, instead of tables in a database. Therefore, you don't need to fetch entities related to others using a predicate. Instead, navigate the object graph using the relationships defined in the model. To get all the images related to myObject:
// assuming the relationships are called 'myObjectProperties' and 'myObjectPropertyImage', respectively
NSSet *allImages = [myObject.myObjectProperties valueForKey:#"myObjectPropertyImage"];
Note that this may trigger additional trips to the database if your object graph is not loaded in memory for your myObject entity. To avoid that, make sure you set the pre-fetching relationship keypaths in your fetch request for myObject.
I hope this helps...
Since you have a MyObject instance in hand and it has the relationship path of myObjectProperties-->ObjectProperty-->>PropertyImages you just need to traverse the relationships. It's easy to do this with valueForKeyPath:
Thusly:
NSArray *images=[myObjectInstances valueForKeyPath:#"myObjectProperties.propertyImage"];
(Note: I might have your attribute names wrong but you can get the idea.)
As general rule, you never fetch when have an object from the graph available. You fetch to "pick out thread" of objects matching the predicate and then to find all related objects you follow the thread/relationships to the related objects.