To-Many Relationship with iOS Core Data - ios

I know there are many questions related to this topic on SO, but I can't seem to find one that answers my question!
I have a relationship between a FactSheet and Image, like this:
I am parsing an internal JSON file (just one time) to import the data into Core Data.
int main(int argc, const char * argv[])
{
#autoreleasepool {
// Create the managed object context
NSManagedObjectContext *context = managedObjectContext();
// Save the managed object context
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error while saving %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
exit(1);
}
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"FactSheets" ofType:#"json"];
NSArray* FactSheets = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
[FactSheets enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
FactSheet *factSheet = [NSEntityDescription
insertNewObjectForEntityForName:#"FactSheet"
inManagedObjectContext:context];
factSheet.name = [obj objectForKey:#"name"];
factSheet.details = [obj objectForKey:#"details"];
NSArray* images=[obj objectForKey:#"images"];
[images enumerateObjectsUsingBlock:^(id img, NSUInteger idx, BOOL *stop) {
Image *image = [NSEntityDescription insertNewObjectForEntityForName:#"Image"
inManagedObjectContext:factSheet.managedObjectContext];
image.path = [img objectForKey:#"path"];
if([img objectForKey:#"caption"]!=[NSNull null]) {
image.caption = [img objectForKey:#"caption"];
}
if([img objectForKey:#"label"]!=[NSNull null]) {
image.label = [img objectForKey:#"label"];
}
if([img objectForKey:#"galleryThumb"]!=[NSNull null]) {
image.galleryThumb = [img objectForKey:#"galleryThumb"];
}
[factSheet addImageObject:image];
}];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
//Test listing
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FactSheet"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (FactSheet *fs in fetchedObjects) {
NSLog(#"Name: %#", fs.name);
for (Image *i in fs.images) {
NSLog(#"Image Path: %#",i.path);
}
}
}
return 0;
}
Running this works until I try to print it out with NSLog. It breaks on the following line:
for (Image *i in fs.images) {
With an "unrecognized selector sent to instance" error. When I open the .sqlite file in SQLite Database Browser, it seems like the data has been inserted how I would think it should be (although I'm by no means a SQLite expert!).
I guess my question is: Am I inserting the objects into core data correctly and just incorrect in my attempts to print it back out --or--, should I be storing the objects into core data a different way?
Thank you!

Change the call to this:
for (Image *i in fs.image) {
Based on your model, the relationship name is image so you can either update your model or your loop code.

Try with -
for (Image *i in fs.image) {
Notice, it should be fs.image instead of fs.images.

Related

What's the best way to add records to managed objects?

I want to use a JSON file to send app data updates. The data are managed objects.
However:
I don't want to overwrite existing data (that will likely include user generated records and modifications), and
I want this operation to be as swift as possible (no pun.. I'm still in Obj-C).
Can someone point me in the right direction here..
This is how I'm starting the app now:
NSError* err = nil;
NSArray *jsonArray = [[NSArray alloc] init];
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"Words" ofType:#"json"];
jsonArray = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
NSManagedObjectContext *context = [self managedObjectContext];
[jsonArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
WordEntity *word = [NSEntityDescription
insertNewObjectForEntityForName:#"WordEntity"
inManagedObjectContext:context];
word.greekText = [obj objectForKey:#"greektext"];
word.englishText = [obj objectForKey:#"englishtext"];
word.uid = [NSString stringWithFormat:#"%#", [obj objectForKey:#"uid"]];
word.category = [obj valueForKey:#"category"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
}
Thanks for any help..

fetch request returning null after code removed that inserts data

In the code below (in ViewDidLoad), I import a JSON file into an iOS project, and then persist the data using Core Data and, at the end, perform a fetch request successfully. However, after I remove the code that imports the file and persists the data, the fetch request starts returning (null). Based on the code below, can you explain why this is happening?
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"inventorydata" ofType:#"json"];
NSArray* inventoryData = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
if(err) NSLog(#"Error %#",[err description]);
NSLog(#"Imported Data: %#", inventoryData);
[Questions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Inventory *partsInfo= [NSEntityDescription
insertNewObjectForEntityForName:#"Inventory"
inManagedObjectContext:self.managedObjectContext];
partsInfo.name = [obj objectForKey:#"name"];
partsInfo.sku = [obj objectForKey:#"sku"];
Supplier *supplierInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Supplier"
inManagedObjectContext:self.managedObjectContext];
supplierInfo.supplierId = [obj objectForKey:#"supplierId"];
[supplierInfo setValue:[NSSet setWithObject:partsInfo ] forKey:#"partData"];
}];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Supplier"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
self.fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (Supplier *info in self.fetchedObjects) {
NSLog(#"supplierId: %#", info.supplierId);
}
Try this.
[Questions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Inventory *partsInfo= [NSEntityDescription
insertNewObjectForEntityForName:#"Inventory"
inManagedObjectContext:self.managedObjectContext];
partsInfo.name = [obj objectForKey:#"name"];
partsInfo.sku = [obj objectForKey:#"sku"];
Supplier *supplierInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Supplier"
inManagedObjectContext:self.managedObjectContext];
supplierInfo.supplierId = [obj objectForKey:#"supplierId"];
[supplierInfo setValue:[NSSet setWithObject:partsInfo ] forKey:#"partData"];
}];
//Add this code : need to save database.
[delegate saveContext];
// You should add this method in AppDelegate.m
- (void)saveContext{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}

populating core data through JSON receiving null entries

Trying to populate core data structure using JSON,
The code is listed below?
NSManagedObjectContext *context = managedObjectContext();
// Save the managed object context
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error while saving %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
exit(1);
}
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"Exercises" ofType:#"json"];
NSArray* Exercises = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
NSLog(#"Imported Exercises: %#", Exercises);
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"Exercise" inManagedObjectContext:context];
NSString *theJSONString = #"{\"key\":\"value\"}";
NSError *theError = NULL;
NSDictionary *jsonDict = [NSDictionary dictionaryWithJSONString:theJSONString error:&theError];
Exercise *exercise = [NSEntityDescription insertNewObjectForEntityForName:#"Exercise"
inManagedObjectContext:context];
[Exercises enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *attributes = [[object entity] attributesByName];
for (NSString *attribute in attributes) {
id value = [jsonDict objectForKey:attribute];
if (value == nil) {
continue;
}
[exercise setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
The code compiles and after analysing the sqlite database created, all attributes are filled with null values.
NSString* secondDataPath = [[NSBundle mainBundle] pathForResource:#"Weights" ofType:#"json"];
NSArray* weightsFromJSON = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:secondDataPath]
options:kNilOptions
error:&err];
NSLog(#"Imported weightsFromJSON: %#", weightsFromJSON);
[weightsFromJSON enumerateObjectsUsingBlock:^(NSDictionary *weightDictionary, NSUInteger idx, BOOL *stop) {
Weight *weight = [NSEntityDescription insertNewObjectForEntityForName:#"Weight"
inManagedObjectContext:context];
NSDictionary *attributes = [[weight entity] attributesByName];
for (NSString *attribute in [attributes allKeys]) {
id value = [weightDictionary objectForKey:attribute];
if (value == nil) {
continue;
}
[weight setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
Edited the code above for the second entity, but fails stating data parameter is hill
You're enumerating every object loaded from the JSON data on disk that you put into Exercises (as an aside, don't capitalize the names of local variables) but then you are not using those objects as the data source, instead you're using jsonDict which is hard-coded from a string and has only one key/value pair. Try changing the line
id value = [jsonDict objectForKey:attribute];
To instead say:
id value = [(NSDictionary *)obj objectForKey:attribute];
That should actually apply the data you loaded. However, there are other problems in your code - you only insert a single entity, not one for each element in Exercises, as well as inserting an extraneous one that you assign to object. Here's some code that I think may do what you're trying to do:
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"exercisesFromJSON" ofType:#"json"];
NSArray* exercisesFromJSON = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
NSLog(#"Imported exercisesFromJSON: %#", exercisesFromJSON);
[exercisesFromJSON enumerateObjectsUsingBlock:^(NSDictionary exerciseDictionary, NSUInteger idx, BOOL *stop) {
Exercise *exercise = [NSEntityDescription insertNewObjectForEntityForName:#"Exercise"
inManagedObjectContext:context];
NSDictionary *attributes = [[exercise entity] attributesByName];
for (NSString *attribute in [attributes allKeys]) {
id value = [exerciseDictionary objectForKey:attribute];
if (value == nil) {
continue;
}
[exercise setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];

populating core data model through JSON with multiple Entities

Having an issue populating a core data model with two entities through a JSON file,
I managed to populate the first entity with no issues, but once i tried populating the second entity, received an error stating the data parameter is nil,
Ive checked that I've used the correct json file name (Weights.json).
Below is the code:
// Create the managed object context
NSManagedObjectContext *context = managedObjectContext();
// Save the managed object context
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error while saving %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
exit(1);
}
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"Exercises" ofType:#"json"];
NSArray* exercisesFromJSON = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
//NSLog(#"Imported exercisesFromJSON: %#", exercisesFromJSON);
[exercisesFromJSON enumerateObjectsUsingBlock:^(NSDictionary *exerciseDictionary, NSUInteger idx, BOOL *stop) {
Exercise *exercise = [NSEntityDescription insertNewObjectForEntityForName:#"Exercise"
inManagedObjectContext:context];
NSDictionary *attributes = [[exercise entity] attributesByName];
for (NSString *attribute in [attributes allKeys]) {
id value = [exerciseDictionary objectForKey:attribute];
if (value == nil) {
continue;
}
[exercise setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
NSString* secondDataPath = [[NSBundle mainBundle] pathForResource:#"Weights" ofType:#"json"];
NSArray* weightsFromJSON = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:secondDataPath]
options:kNilOptions
error:&err];
///***Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil'****//////
NSLog(#"Imported weightsFromJSON: %#", weightsFromJSON);
[weightsFromJSON enumerateObjectsUsingBlock:^(NSDictionary *weightDictionary, NSUInteger idx, BOOL *stop) {
Weight *weight = [NSEntityDescription insertNewObjectForEntityForName:#"Weight"
inManagedObjectContext:context];
NSDictionary *attributes = [[weight entity] attributesByName];
for (NSString *attribute in [attributes allKeys]) {
id value = [weightDictionary objectForKey:attribute];
if (value == nil) {
continue;
}
[weight setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
thank you in advance for any help.

Core data only returning null

I have a number of objects (NSManagedObject Subclass) which 'seem' to be created and saving correctly, however on reloading i can only get null from any of the variables within the objects.
The right number of objects are being saved, but none of the variables.
I've gone through the code several times, and I cant figure out if the problem is on the saving side or the loading side.
Would very much appreciate any thoughts!
Thanks in advance
//Create a route
- (CNSRoute *) createRoute
{
CNSRoute *p = [NSEntityDescription insertNewObjectForEntityForName:#"CNSRoute" inManagedObjectContext:context];
[allRoutes addObject:p];
//NSLog(#"Route: %#", p);
return p;
}
//Load all routes from core data
- (void) loadAllRoutes
{
NSLog(#"All Routes: %#", allRoutes);
if (!allRoutes) {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *e = [[model entitiesByName] objectForKey:#"CNSRoute"];
[request setEntity:e];
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sd]];
NSError *error;
NSArray *result = [context executeFetchRequest:request error:&error];
if (!result) {
[NSException raise:#"Fetch Failed" format:#"Reason: %#", [error localizedDescription]];
}
NSLog(#"LOAD RESULT: %#", result);
for (CNSRoute *route in result) {
NSLog(#"Loading Route... %#", [route name]);
}
allRoutes = [[NSMutableArray alloc] init];
[allRoutes setArray:result];
//NSLog(#"%#", allLocations);
}
}
//Save context
- (BOOL)saveChanges
{
NSError *err = nil;
BOOL successful = [context save:&err];
if (!successful) {
NSLog(#"ERROR SAVING: %#", [err localizedDescription]);
}
return successful;
}
//Save when application enters background
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[CNSTravelController sharedStore] saveChanges];
}

Resources