I am new to core data and trying to create a simple apps using core data. I am currently working on app to save data in to-many relationship, there are several questions and tutorials but I am still confused.
I have two entities person and contactNumbers, I am fetching person and its contact numbers from address book, person A has mobile number, iphone number, home, work, other... which creates to-many relationship.
In my code I didnt subclass any entity, Is it necessary to subclass entities to save data in relationship? I am asking this because I have read that its not necessary to subclass direct working with NSManagedObject class will do the job.
All I want is to save person A first name, last name in person entity and its contact numbers in contactNumbers entity. How to save data in to-many relationship using core data?
Please I request do not refer other stackoverflow questions, raywenderlich tutorial, app code blog tutorials etc.
Please provide a clearly understandable code with concepts to deal this situation. Thanks a lot.
This is my model
if I Do this
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:#"person" inManagedObjectContext:self.managedObjectContext];
[person setValue:#"first name A" forKey:#"firstName"];
[person setValue:#"last name B" forKey:#"lastName"];
[person setValue:#"123" forKey:#"mobile"];
I got this error
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSManagedObject 0x82abcc0> setValue:forUndefinedKey:]: the entity Person is not key value coding-compliant for the key "mobile".
If i do this
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
[person setValue:#"first name A" forKey:#"firstName"];
[person setValue:#"last name B" forKey:#"lastName"];
NSManagedObject *contactNumber = [NSEntityDescription insertNewObjectForEntityForName:#"ContactNumber" inManagedObjectContext:self.managedObjectContext];
[contactNumber setValue:#"123" forKey:#"home"];
[contactNumber setValue:#"456" forKey:#"iPhone"];
[contactNumber setValue:#"789" forKey:#"mobile"];
[contactNumber setValue:#"111" forKey:#"work"];
[contactNumber setValue:#"112" forKey:#"other"];
its working fine, but how data got related to each other in two entities?
You don't need to subclass.
You just need relations.
Make a relation on Person named contactNumbers that points to the ContactNumber entity, and a matching relation on ContactNumber entity called person that points back to person.
The contactNumber relation should be one to many (because one person has many contacts), while the relation from ContactNumber going back to Person should be one to one (because each contact entry will appear in only one person).
When you get a Person entity then, you'll be able to get a set of ContactNumber objects from the contactNumbers property, and then look through elements in the set. Or you can search ContactNumber entities where the person ID matches the person relation going back to Person.
Followup: Your first code does not work because you cannot reference a single instance through a set.
Your second section of code is fine, just add the line
contactNumber.person = person;
Then you can access the set of contacts from a person at any time with:
NSSet *contacts = person.contactNumbers;
And get the contact number you want. You cannot go directly from a person to one contact without some very tricky keypath stuff.
I would EXTREMELY STRONGLY suggest using Mogenerator to generate data objects after you set up the model, it makes the data clearer as to how you can use it. Using the setKey mechanism with Core Data is UTTERLY INSANE.
You do not need to do any subclassing at all to read and write Core Data relationships (or any other values for that matter). In Core Data, a relationship is represented as an NSMutableSet. Here is some sample code to show you how to retrieve this set and modify it:
// Assuming myPerson is an NSManagedObject instance of the Person entity
NSMutableSet *contactNumbers = [myPerson mutableSetValueForKey:#"contactNumbers"];
// Add numbers normally by calling the set's addObject property
[contactNumbers addObject:myNewNumber];
//etc.
I would also highly recommend taking a look at the Sensible TableView framework if you haven't already done so. The framework will automatically generate the UI for you Core Data entities, including all relationships. It will also automatically handle adding/removing new relationship objects. I myself wouldn't imagine going back to doing stuff manually again without it. Good luck!
Related
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 condition where I have many-many for my two entities:
A category can have many places, in other hand, places can be also
under many categories
The data are successfully stored in my sqlite, but when I check the structure of both entites in sqlite manager software, the relationship between two of it is gone.
Usually, when I declare 1..* , for example:
Entity B has one entity A
Place is under one category
Category will become an attribute in Place, where I can use it for later use:
Category *category = (Category*)place.category;
NSString *catId = category.catId;
and it should returns a value of catId if there is value there.
But in my many-many case, I can't even get the catId, it crashes like this:
-[_NSFaultingMutableSet catID]: unrecognized selector sent to instance
Do I miss a concept here? I believe core data supports many to many relationship as I've read it in some web. Thanks!
You have a 'to-many' relationship from Place->Category, so place.category is a NSMutableSet object not a Category object
Try this
NSMutableSet *categorySet = place.category;
foreach (Category *category in categorySet)
{
NSString *catId = category.catId;
NSLog(#"catId: %#", catId);
}
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.
I'm having difficulty with a one to one relationship. At the highest level, I have a one to many relationship. I'll use the typical manager, employee, example to explain what I'm trying to do. And to take it a step further, I'm trying to add a one to one House relationship to the employe.
I have the employees being added no problem with the addEmployeesToManagereObject method that was created for me when I subclassed NSManagedObject. When I select an Employee in my table view, I set the currentEmployee of type Employee - which is declared in my .h.
Now that I have that current employee I would like to save the Houses entities attributes in relation to the current employee.
The part that I'm really struggling with is setting the managedObjectContext and setting and saving the houses attributes in relation to the currentEmployee.
I've tried several things but here's my last attempt:
NOTE: employeeToHouse is a property of type House that was created for
me when I subclassed NSManagedObject
House *theHouse = [NSEntityDescription
insertNewObjectForEntityForName:#"House"
inManagedObjectContext:self.managedObjectContext];
// This is where I'm lost, trying to set the House
// object through the employeeToHouse relationship
self.currentEmployee.employeeToHouse
How can I access and save the houses attributes to the currentEmployee?
since House is setup as an Entity it can be considered a table within the data store. If that truly is the case, you need to setup a 1 to 1 relationship between Employee and House in your data model.
If you have already done so, then it is as simple as calling. Although I'm not as familiar with one to one relationships with Core Data as I am with to-many. In either case, try one of the following
[self.currentEmployee addHouseObject: theHouse];
or
self.currentEmployee.employeeToHouse=theHouse;
then to the save to the managedObjectContext:
NSError *error=nil;
if (![self.managedObjectContext save:&error]{
NSLog(#"Core Data Save Error: %#", error);
}
Also, I'm not sure about your particular situation, but your self.managedObjectContext should already be the same as the one pointed to by self.currentEmployee.managedObjectContext.
Good luck,
Tim
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.