Core data parent child relationship - core-data

I have a basic question regarding Core Data.
I have 2 tables one to many.
I have setup the app to add children to the parent, but I cannot understand how I set the relationship so that when I add a new child via a view controller that It adds the child to the correct parent.
I have generated the entity subclasses and have managed to get the app to add a child (but it adds it to index 0), but I cant seem to work a fetchrequest that finds the correct parent.
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
Child *newChild = [NSEntityDescription insertNewObjectForEntityForName:#"Child" inManagedObjectContext:context];
[newChild setValue:self.childName.text forKey:#"childName"];
[newChild setValue:self.born.text forKey:#"born"];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ParentList" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
ParentList *parent = [fetchedObjects objectAtIndex:0]; //this adds it to the first parentList in list at index 0 not to the correct parent
NSLog(#"parent: %# created", league);
[parent addChildObject: newChild];
//
///////////////////////////////////////////
//////index path is wrong//////////////////
///////////////////////////////////////////
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}

You need to pass the parent's objectID to the second view controller (if I understood your setup correctly).
Fetch the parent (using existingObjectWithID:error: of the NSManagedObjectContext) in the other view context.
Set the child parent as the fetched object.
should look something like:
NSError* error = nil;
NSManagedObjectID* parentID = //the parent object id you selected
Parent* parent = [context existingObjectWithID:parentID error:&error];
if (parent) { //parent exists
Child *newChild = [NSEntityDescription insertNewObjectForEntityForName:#"Child"
inManagedObjectContext:context];
[newChild setValue:self.childName.text forKey:#"childName"];
[newChild setValue:self.born.text forKey:#"born"];
[newChild setValue:parent forKey:#"parent"];//Set the parent
} else {
NSLog(#"ERROR:: error fetching parent: %#",error);
}
Edit:
To get the selected object id (assuming you are using a NSFetchedReaultsController):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
//Use object.objectID as the selected object id to pass to the other view controller
//what ever you need to do with the object
}

Related

Saving array of images in core data as transformable

I want to add my imageArray into coredata as transformable but this is not storing properly.
My save button coding.
- (IBAction)saveButton:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newEntry = [NSEntityDescription insertNewObjectForEntityForName:#"FoodInfo" inManagedObjectContext:context];
NSAttributeDescription *messageType = [[NSAttributeDescription alloc] init];
[messageType setName:#"photos"];
[messageType setAttributeType:NSTransformableAttributeType];
[imagesForShow addObject:messageType];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unable to save context for class" );
} else {
NSLog(#"saved all records!");
[context save:nil];
}
//[newEntry setValue:imagesForShow forKey:#"images"];
}
Here 'imagesForShow' is my array of images.
When iam going to fetch this image array , this showing nil
- (void)viewDidLoad {
[super viewDidLoad];
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"FoodInfo"];
[request setReturnsObjectsAsFaults:NO];
arrayForPhotos = [[NSMutableArray alloc]initWithArray:[context executeFetchRequest:request error:nil]];
// Do any additional setup after loading the view.
}
What I am doing wrong with this code. Thanks.
In your save code:
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newEntry = [NSEntityDescription
insertNewObjectForEntityForName:#"FoodInfo"
inManagedObjectContext:context];
NSAttributeDescription *messageType = [[NSAttributeDescription alloc] init];
[messageType setName:#"photos"];
[messageType setAttributeType:NSTransformableAttributeType];
[imagesForShow addObject:messageType];
I can't even figure out what this is supposed to do. It's completely wrong. You should never be allocating an instance of NSAttributeDescription unless you are constructing a Core Data model on the fly-- which you are not doing and which almost nobody ever does. Creating the new entry is OK. The rest, I don't know. You said that imagesForShow is your array of images, so I don't know why you're also adding an attribute description to the array.
In a more general sense, if newEntry has a transformable attribute named photos and imagesForShow is an NSArray of UIImage objects, then you could do this:
[newEntry setValue:imagesForShow forKey:#"photos"];
This is similar to a line that you have commented out, though it's not clear why it's commented out.
But whatever you do get rid of the code creating the NSAttributeDescription.

Core Data input fetches into labels

I want to store data from a text field. And then use this data to populate the text in a label inside a view controller. Is this possible? Ive messed around with it, but nothing seems to work. Any thoughts? Here are my two methods...
- (IBAction)saveButton:(id)sender {
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSManagedObject *noteEntry = [NSEntityDescription insertNewObjectForEntityForName:#"Notes" inManagedObjectContext:coreDataStack.managedObjectContext];
[noteEntry setValue:_notesField.text forKey:#"notes"];
NSError *error = nil;
// Save the object to persistent store
if (![coreDataStack.managedObjectContext save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[coreDataStack saveContext];
}
Here is my view did load method:
- (void)viewDidLoad
{
[super viewDidLoad];
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Notes"];
Notes *entry = [NSEntityDescription insertNewObjectForEntityForName:#"Notes" inManagedObjectContext:coreDataStack.managedObjectContext];
_outputLabel.text = entry.notes;
}
Your code in the saveButton: method seems OK. But your code in the viewDidLoad method seems wrong. Are you trying to do the following?
- (void)viewDidLoad {
[super viewDidLoad];
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
//First way to init your fetchRequest:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Notes"];
//Second way to init your fetchRequest:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Notes" inManagedObjectContext:coreDataStack];
[fetchRequest setEntity:entity];
//Having a NSEntityDescription object can be usefull if you need to group your data, for example
//Set some predicates, if necessary
//Set some sortdescriptors, if necessary
NSError *error;
NSArray *resultsArray = [coreDataStack executeFetchRequest:request error:&error];
NSLog(#"%#", resultsArray);
_outputLabel.text = [[resultsArray firstObject] notes]; //if your array can only have one object
}
The NSLog will give you the structure of your fetch array (I can't guess it). You will then be able to set _outputLabel.text.
Answer by #user1966109 is exactly correct answer what you are looking for. Since you need to show the text in a label and for that you need to set predicate to get a single row. Set the predicate as below
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"value == abc"];//considerin abc is the word you are looking for.
request.predicate = predicate;
//then execute the query.

Core Data retrieving relationship data

I am really having trouble retrieving items that have been created through the Menu entity. This is the code I used to add an item to a specific Menu object
-(void)additem:(NSString *)entity :(NSDictionary *)aDictionary :(NSString *)menu
{
NSLog(#"additem");
NSError *error = nil;
Menu *menuItem = nil;
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Menu" inManagedObjectContext:managedObjectContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"mname=%# and msection =%#",#"Parents",#"Keydates"]];
menuItem = [[managedObjectContext executeFetchRequest:request error:&error] lastObject];
if (error) {
//Handle any errors
}
if (!menuItem) {
//Nothing there to update
NSLog(#"This class doesn't exist");
}
Items *anitem = (Items *)[NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:managedObjectContext];;
anitem.type = [aDictionary objectForKey:#"type"];
anitem.title = [aDictionary objectForKey:#"title"];
anitem.image = [aDictionary objectForKey:#"image"];
anitem.subtitle = [aDictionary objectForKey:#"subtitle"];
[menuItem addItemsObject:anitem];
[managedObjectContext save:&error];
}
I want to use a predicate to retrieve all the items that were created on a specific Menu object. Here is the code I am trying to retrieve it with.
- (void) readItems: (NSString *)section {
NSLog(#"readItems");
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Menu"
inManagedObjectContext:managedObjectContext];
fetchRequest.resultType = NSDictionaryResultType;
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"mname=%# and msection = %#",#"Parents",#"Keydates"]];
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (Items *item in fetchedObjects) {
NSLog(#">>>>>>%#",item);
}
}
Can anyone point me in the right direction.I know I pass section and don't use it. I have place the actual values in.
Your readItems: method specifies the entity for your fetch as Menu yet when you execute the fetch you are expecting Item (since you start iterating through fetchedObjects and casting them as Item).
Instead what you want to do is et your entity to Item and change the predicate to
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"items.mname=%# and items.msection = %#",#"Parents",#"Keydates"]];
Furthermore you should consider renaming your relationships. The relationship field specifies exactly what you would expect to see when you follow the arrow. Thus when I look at Items and follow its arrow to Menu I would expect the name of this relationship to be menu yet you call it menuItems. You have done this correctly for the relationship from Menu to Item.
That way we would end up with a more readable predicate looking like:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"menu.mname=%# and menu.msection = %#",#"Parents",#"Keydates"]];
Happy Coding

Core Data add new entity post related to fetchRequest result

I have two Entities in my CoreData model, "Valla" and "Dagbok", these are related with a many-to-many relationship.
When i add a new post to the Dagbok-entity i want it to relate to posts from Valla-entity that i store in an NSArray from a FetchRequest.
The code right now just adds a post to Dagbok
- (void)initDagbokDBWithText:(NSString *)text header:(NSString *)header degree:(int)degree weather:(NSString *)weather farg:(NSString *)farg
{
AppDelegate *appdelegate = [[UIApplication sharedApplication]delegate];
context = [appdelegate managedObjectContext];
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Dagbok" inManagedObjectContext:context];
NSManagedObject *newDagbok = [[NSManagedObject alloc] initWithEntity:entitydesc insertIntoManagedObjectContext:context];
//NSRelationshipDescription *dagbokRelation = [[NSRelationshipDescription alloc] init];
//NSRelationshipDescription *vallaRelation = [[NSRelationshipDescription alloc] init];
[newDagbok setValue:text forKey:#"text"];
[newDagbok setValue:header forKey:#"header"];
[newDagbok setValue:[NSNumber numberWithInt:degree] forKey:#"degree"];
[newDagbok setValue:weather forKey:#"weather"];
[newDagbok setValue:farg forKey:#"colorCode"];
NSError *error;
if(![context save:&error])
{
NSLog(#"Whoops : %#", [error localizedDescription]);
}
}
How do i add related objects from an array with fetchRequest results?
Life will be much easier if you create NSManagedObject sub classes to represent your entities rather than dealing with NSManagedObject directly. Do this by adding a name for your class in the model (the "class" property of the entity), e.g. for Valla set to VallaEntity. Then XCode can autogenerate these classes which will then have properties and methods to simplify your code, e.g.
DagbokEntity *newDagbok = [[NSManagedObject alloc] initWithEntity:entitydesc insertIntoManagedObjectContext:context];
newDagbok.text = text;
newDagbok.header = header;
newDagbok.degree = [NSNumber numberWithInt:degree];
newDagbok.weather = weather;
newDagbok.colorCode = farg;
[newDagbok addVallas:[NSSet setWithArray:arrayOfRelatedVallas]];

Core Data Fetch Save relationship while inserting entity (simultaneously)

First question here and I've tried a bunch of stuff and can't figure it out.
Core Data with 2 entities with to-many relationship both ways
A<<---->>B
A entity has name as an attribute, and a relationship Bs
First Controller lists all A entities and i have a second controller to add A entities and I want to have it save a default B in its relationship.
In the prepare for segue I have this code:
if ([[segue identifier] isEqualToString:#"addAEntitySegue"]) {
AddAEntityViewController *addAEntityViewController = [segue destinationViewController];
addAEntityViewController.delegate = self;
addAEntityViewController.managedObjectContext = self.managedObjectContext;
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.addingManagedObjectContext = addingContext;
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
addAEntityViewController.A = [NSEntityDescription insertNewObjectForEntityForName:#"A" inManagedObjectContext:addingContext];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:addAEntityViewController
action:#selector(save:)];
addAEntityViewController.navigationItem.rightBarButtonItem = saveButton;
}
In addAEntityViewController i have this to save
-(IBAction)save:(id)sender
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"B" inManagedObjectContext:self.managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#",[defaults objectForKey:#"BDefault"]];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
//Set the batch size to a suitable number
[fetchRequest setFetchBatchSize:20];
NSError *error;
self.A.name = textFieldVal;
[self.A addBObject:[[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] objectAtIndex:0]];
NSLog(#"A = %#", self.A.Bs);
[self.delegate addAEntityViewController:self didFinishWithSave:YES];
}
In the addAEntityViewController everything saves correctly even the NSLog(#"A = %#", self.A.Bs); statement shows the B. But when the delegate saves in the First Controller (AEntityViewController) it only saves the A.name but not the relationship A.Bs, I can't figure out what's wrong.
Here's the delegate method:
-(void) addAEntityViewController:self didFinishWithSave:YES{
if (save) {
NSLog(#"saveworkouts");
NSError *error;
if (![addingManagedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
// release the adding managed object context
self.addingManagedObjectContext = nil;
}
Like I said it saves the A entity but not the relationship to B even though the relationship to B saved correctly in the addAEntityViewController (the second View). An NSLOg of A.Bs is null.
First I believe that this line:
addAEntityViewController = self.managedObjectContext;
should be:
addAEntityViewController.managedObjectContext = self.managedObjectContext;
but that would also be wrong.
it should be getting the addingContext you created afterwards:
addAEntityViewController.managedObjectContext = addingContext;
I'm a bit surprised that your app didn't crash, as you are mixing managed objects from 2 different contexts.

Resources