I am building a very simple application with two entities: Person and Categories.
In my AddPersonViewController, I have a field for the person's name and a Table view with multiple selection enabled with predefined categories.
I would like to be able to select multiple categories and save them at once together with the person's name when I push the Save button.
I could find a lot of examples that save one related entity but no one for several at once.
EDITED
I select the categories and put them in an array then I save the user but it saves only the last category of the array.
Here is my save method:
- (IBAction)save:(id)sender {
//saves the user name
Users *name = [NSEntityDescription insertNewObjectForEntityForName:#"Users"inManagedObjectContext:self.managedObjectContext];
name.userName = addUserField.text;
NSError *error = nil;
if(![managedObjectContext save:&error]){
NSLog(#"Error! %#", error);
}
//saves the related categories
Kind *kind = [NSEntityDescription insertNewObjectForEntityForName:#"Kind"inManagedObjectContext:self.managedObjectContext];
for (int k=0; k < [_addCathegoryArray count]; k++) {
NSString *kindString = [[_addCathegoryArray objectAtIndex:k] description];
[kind setValue:kindString forKey:#"kindName"];
[name addHasKindsObject:kind];
}
if(![managedObjectContext save:&error]){
NSLog(#"Error! %#", error);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Saving is done on the context, not on individual attributes or entities.
Put simply, when you save, all of the changes you've made to the objects in the context are saved.
Related
I've an NSArray in which I'm adding objects after user selects multiple rows from a tableview. After selecting user press confirm and it saves the data. So based on some example I found here I have implemented the code but it seems as it is only saving one value at a time. The last value that user selects:
- (IBAction)confirmPressed:(id)sender {
NSLog(#"Selected Are: %# - %#",selectedDX,selectedDesc);
for (NSString *code in selectedDX) {
if (!_dxToAddEdit) {
self.dxToAddEdit = [Diagnoses MR_createEntity];
}
[self.dxToAddEdit setCode:code];
[self.dxToAddEdit setCodeDescription:#"Sample Description"];
[self.dxToAddEdit setSuperBill:_forSuperBill];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
}
[self.navigationController popViewControllerAnimated:YES];
}
You are working only with one managed object self.dxToAddEdit. And it will contain the last code from array. If you want to save multiple objects you should do the following:
NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext];
for (NSString *code in selectedDX) {
Diagnoses *newDiagnose = [Diagnoses MR_createEntityInContext:defaultContext];
newDiagnose.code = code;
newDiagnose.codeDescription = #"Sample Description";
newDiagnose.superBill = _forSuperBill;
}
// Save recently created objects to persistent store.
[defaultContext MR_saveToPersistentStoreAndWait];
I saved my managedObjects. But my NSManagedObjectID is still temporary after saving. Why?
dispatch_async(privateQueue, ^{
__block NSMutableArray *ids = [NSMutableArray array];
[[[LPAppDelegate instance] privateContext] performBlockAndWait:^{
if ([responseObject isKindOfClass:[NSDictionary class]]) {
id arr = ((NSDictionary*)responseObject)[#"results"];
for (int i=0; i < ((NSArray *)arr).count; i++) {
LPFilm *film = [LPFilm MR_createEntityInContext:privateContext];
[ids addObject:film.objectID];
}
}
NSError *error = nil;
[privateContext save:&error];
if (error) {
NSLog(#"___fetch service error: %#", [error localizedDescription]);
}
}];
for (NSManagedObjectID *objID in ids) {
if (objID.isTemporaryID) {
NSLog(#"__tempID %#", objID);
}
}
});
When you save changes, new NSManagedObject instances get a new object ID. Previously existing instances of NSManagedObjectID are not converted in-place, they're replaced with new instances. But you have to ask the managed object for its new ID.
In your case, you're saving up an array of object IDs before saving. Those objects will never change, even if you save changes. But, if you go back to your managed objects and ask them for their object IDs again, you'll get different results, which will not be temporary.
There is an entities where I am filling data from JSON, Say it is Photographer and Photo. Both have some data which I have filled using loop and managed ObjectContex ..
Like this,
NSMutableArray *ArrPhotographer= [[self.M3Arr objectForKey:#"LifeMag"]objectForKey:#"Photographer"];
for (int i = 0; i< ArrPhotographer.count; i++) {
Photographer * photographerObj = [NSEntityDescription insertNewObjectForEntityForName:#"PhotographerData"
inManagedObjectContext:[self managedObjectContext]];
NSMutableDictionary *tpDict = [cleanerListArr objectAtIndex:i];
photographerObj.cleanerName = [tpDict objectForKey:#"photographerName"];
}
Now I have done this for both Photographer and Photo Entity and as per this picture my Magazine entity is having data which are already existed in this table . And as shown I have made to one relation to both Photo and Photographer from Magazine .
Now Question is ,
If Photographer Name is already existed in the table , How can I connect it with Magazine Entity . I need the managed object Reference of that particular place .
(For Example , There are three photographer , Ron , Harry and Sunny now For Photo Cover1 I want name of Ron . then I need the Object Reference of Ron when I pre Populate it).
How to get this object Reference?
// **************** EDIT
I am getting the object is present ...but No how to fetch object and 2) how to give it to x and y stated above ?
I am using this code to saving the data in Magazine
Magazine *magObj = [NSEntityDescription insertNewObjectForEntityForName:#"Magazine"
inManagedObjectContext:[self managedObjectContext]];
magObj .issueID=[NSNumber numberWithInt:1];
magObj .photo= x;
magObj .photographer = y;
#### Edit 2
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Cleaner"];
[request setPredicate:[NSPredicate predicateWithFormat:#"cleanerName = %#", #"Robin"]];
[request setFetchLimit:1];
NSUInteger count = [[self managedObjectContext] countForFetchRequest:request error:nil];
if (count == NSNotFound){
NSLog(#"ERROR FOund");
}
// some error occurred
else if (count == 0){
NSLog(#"no matching object");
}
// no matching object
else{
NSLog(#"Found Match");
}
You need to use an NSFetchRequest to search the context for the appropriate object to connect to. The fetch request specifies the entity type to search for and you need to add an NSPredicate to filter the results to only the particular name that you're interested in.
Note that you could run a single fetch request with a list of names so that you only hit the data store for information once and then use the returned list during your object creation / connection loop.
If you're loading all of your data in one go, then you can create a dictionary which contains the managed object instances such that you can link to them without fetching.
I'm trying to learn Core Data, and am having trouble updating the MOC after adding new objects to an existing object. I can create the original object, a training day, and I can add exercise objects to that training day, but I can't figure out how to save the context so that later in my application I can find all exercises in a training day.
Any ideas??
Here is my code:
// Data from JSON
NSArray *trainingDayData = responseData[#"training_days"];
for (NSDictionary *aTrainingDay in trainingDayData) {
// Find the specific training day and save the MOC, creating the trainingDayObject
NSNumber *idTrainingDay = [NSNumber numberWithInt:[[aTrainingDay objectForKey:kID_KEY] intValue]];
VitTrainingDay *trainingDayObject = [VitTrainingDay trainingDayCreateOrObjectWithID:idTrainingDay];
// Configure the VitTrainingDay object's fields
trainingDayObject.name = aTrainingDay[#"name"];
trainingDayObject.order = aTrainingDay[#"order"];
}
// assign exercises to each trainingDayObject(this is inside a larger for loop)
trainingDayObject.userExercise = [NSSet setWithArray:userExerciseObjects];
// Below are attempt one and two to update the MOC after assigning exercises to the trainingDayObject.
// This works to save the updated MOC, but also adds two blank trainingDayObjects, since it 'insertNewObjectForEntityName', which I don't want.
trainingDayObject = [NSEntityDescription insertNewObjectForEntityForName:#"TrainingDay" inManagedObjectContext:self.context];
//This as far as I can tell is doing nothing. It just points to the conventional MOC save method. I pull it out below.
[self.coreDataManager saveContextForManagedObjectContext:self.context];
Here is the saveContextForManagedObjectContext method I call above:
- (void)saveContextForManagedObjectContext:(NSManagedObjectContext *)moc
{
NSError *error = nil;
if (![moc save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
I am a bit confused about your loops but assuming the first one is used to get each Training Day, sets some values and then get the Exercises for that day and set the relationships try the following.
Note that this line of code below is what creates the NSManagedObject, so you need to call it to create each TrainingDay object and each Exercise object.
trainingDayObject = [NSEntityDescription insertNewObjectForEntityForName:#"TrainingDay" inManagedObjectContext:self.context];
Not sure what this line does but presumably it creates a new object or returns one if it already exists
VitTrainingDay *trainingDayObject = [VitTrainingDay trainingDayCreateOrObjectWithID:idTrainingDay];
Unless it is also calling insertNewObjectForEntityForName then it should be replaced with a call that does create the NSManagedObject or searches and returns one with a matching ID. If it is calling insertNewObjectForEntityForName then you should remove the line below because that just creates another trainingDay object in the database without setting any attribute values.
Try something like this
// Data from JSON
NSArray *trainingDayData = responseData[#"training_days"];
for (NSDictionary *aTrainingDay in trainingDayData) {
// Find the specific training day ID
NSNumber *idTrainingDay = [NSNumber numberWithInt:[[aTrainingDay objectForKey:kID_KEY] intValue]];
//Create the Core Data Object
//Assume VitTrainingDay is a NSManagedObject subclass
VitTrainingDay *trainingDayObject = [NSEntityDescription insertNewObjectForEntityForName:#"TrainingDay" inManagedObjectContext:self.context];
// Set the attributes
trainingDayObject.ID = idTrainingDay;
trainingDayObject.name = aTrainingDay[#"name"];
trainingDayObject.order = aTrainingDay[#"order"];
// assign exercises to each trainingDayObject
for (SomeSourceObject *object in SomeExercisesSource) {
ExerciseObject *exercise = [NSEntityDescription insertNewObjectForEntityForName:#"ExerciseObject" inManagedObjectContext:self.context];
// Set the exercises parent object (training day)
exercise.trainingDay = trainingDayObject;
exercise.details = object.details;
}
}
NSError *error = nil;
if (![self.context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
} else {
NSLog(#"Save successful");
}
My guess is you need to search for an existing TrainingDay object with the same ID before creating a new one so if that is what this call [VitTrainingDay trainingDayCreateOrObjectWithID:idTrainingDay]; does then use it instead.
I am trying to create a one to many relationship with some data I have.
I have a single Project and many items, I am trying to set up the controller to save them but this is the first time I have ever used a one to many relationship and my head is about to explode.
This is what my save method looks like
- (void)writeProj:(NSArray *)recivedProData ItemsData:(NSArray *)itemsData {
// WRITE TO CORE DATA
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *dict in recivedProData) {
Project *project = [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:self.managedObjectContext];
project.projectNumber = [dict valueForKey:#"ProjectNumber"];
project.projectDescription = [dict valueForKey:#"Description"];
// project.items = [dict valueForKey:#""]; // this is the relationship for project
}
for (NSDictionary *dict in itemsData) {
Items *items = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:self.managedObjectContext];
items.description = [dict valueForKey:#"Description"];
items.area = [dict valueForKey:#"Area"];
items.stage = [dict valueForKey:#"Stage"];
// items.project = [dict valueForKey:#""]; // this is the relationship for items
}
NSError *error = nil;
if (![__managedObjectContext save:&error]) {
NSLog(#"There was an error! %#", error);
}
else {
NSLog(#"created");
}
[Project addItemsObject:items];
[__managedObjectContext saveOnSuccess:^{
NSLog(#"You created a relationship");
} onFailure:^(NSError *error) {
NSLog(#"There was an error! %#", error);
}];
}
So I have one Project and many Items, I just dont know how to set up the keyfields so that they save into core data as one project and many items.
So hopefully my code is making sense. If someone could just help me figure out how to save it properly that would be greatly appreciated.
Just set items.project to be equal to the project NSManagedObject you just made
items.project = project;
EDIT: if you have only one project, you should move the Project* project declaration outside of the recivedProData for loop -- you are making one project for every dictionary, and you say you only have one project ever. That entire block of code makes no sense if you have only one project though -- why do you have an array of Project data, and not just one dictionary?
I want to discuss your Core Data model, the one you configured using the Core Data GUI editor. I'm curious about the plurality of the entity named "Items" from the following line of code:
Items *items = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:self.managedObjectContext];
Maybe it's just a matter of semantics. It's possible, however, that the plurality of that entity name indicates a problem in your Core Data model. I'll try to explain, but this is pretty abstract stuff.
Although an entity may have a relationship that represents a collection of things, the entity itself is not really a collection of things; the entity is always a single thing in the model and should be treated as such in your code (and in your naming schemes).
Here's how I would describe your model in words:
The Project entity is a single thing with a relationship called items. The items relationship is a collection (a set) of Item entities (i.e., a one-to-many relationship). But each Item entity is a single thing.
Does your model in the GUI editor reflect this description?