populating core data through JSON receiving null entries - ios

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]);
}
}];

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..

Search of UITableView

I have a UITableView populated from a JSON data feed using an NSDictionary and NSArray and I am trying to search the UITableView data using this code, but it is not working some other related info based on how I set it up is in this question Data from JSON Parsing straight to UITableView
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.filteredCandyArray removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",searchText];
NSArray *tempArray = [airportsArray filteredArrayUsingPredicate:predicate];
NSLog(#" text %#", searchText);
filteredCandyArray = [NSMutableArray arrayWithArray:tempArray];
NSLog(#"NSLog %#", scope);
}
This is how the data is parsed
NSString* path = #"https://api.flightstats.com/flex/airports/rest/v1/json/active?appId=532ca6af&appKey=50d6d3351e5953152b2688f10d10d276";
NSMutableURLRequest* _request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:path]];
[_request setHTTPMethod:#"GET"];
NSURLResponse *response = nil;
NSError *error = nil;
NSData* _connectionData = [NSURLConnection sendSynchronousRequest:_request returningResponse:&response error:&error];
if(nil != error)
{
NSLog(#"Error: %#", error);
}
else
{
NSMutableDictionary* json = nil;
if(nil != _connectionData)
{
json = [NSJSONSerialization JSONObjectWithData:_connectionData options:NSJSONReadingMutableContainers error:&error];
}
if (error || !json)
{
NSLog(#"Could not parse loaded json with error:%#", error);
}
else
{
routeRes = [json objectForKey:#"airports"];
airportsArray = [json objectForKey:#"airports"];
}
_connectionData = nil;
NSLog(#"connection done");
}
NSLog(#" end array %#", candyArray);
// Initialize the filteredCandyArray with a capacity equal to the candyArray's capacity
filteredCandyArray = [NSMutableArray arrayWithCapacity:[airportsArray count]];
// Reload the table
[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[[self tableView] reloadData];
You can manually build the array via the build in loop methods,
NSMutableArray *filteredResults = [NSMutableArray new];
[airportsArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj.name rangeOfString: searchText].location != NSNotFound) {
[filteredResults addObject:obj];
}
}];
self.filteredCandyArray = [NSArray arrayWithArray:filteredResults];
You will need to replace the id obj with what ever object model you are using. For example if you had a plane model you might do Plane *plane in replace of id obj.
I might add how to use it via predicates later, but this should do what you request. You may view the enumerateObjectsUsingBlock: and arrayWithArray: methods in the NSArray documentation. rangeOfString: can be viewed in the NSString documentation.
-NSArray Documentation
-NSString Documentation

To-Many Relationship with iOS Core Data

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.

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.

iOS Parse Inner Json

Hi I have the following json that i need to parse, however, I'm struggling to parse the inner array. What I have currently just prints each of the inner arrays but I'd like to print say each title and add the titles to an array. Thank you for any help!
JSON
{"nodes":[{
"node":{
"nid":"1420857",
"title":"Title 1",
"votes":"182",
"popular":"True",
"teaser":"Teaser 1"
}},
{"node":{
"nid":"1186152",
"title":"Title 2",
"votes":"140",
"popular":"True",
"teaser":"Teaser 2"
}},
{"node":{
"nid":"299856",
"title":"Title 3",
"votes":"136",
"popular":"True",
"teaser":"Teaser 3"
}}
]}
Json Parser
NSError *error = nil;
NSData *jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.somefilename.json"]];
if (jsonData) {
id jsonObjects = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if (error) {
NSLog(#"error is %#", [error localizedDescription]);
return;
}
NSArray *keys = [jsonObjects allKeys];
for (NSString *key in keys) {
NSLog(#"%#", [jsonObjects objectForKey:key]);
}
} else {
// Handle Error
}
Just typecast it:
NSArray *nodes = (NSArray*)[jsonObjects objectForKey:#"nodes"];
for (NSDictionary *node in nodes){
// do stuff...
}
Methods that return id (like -[objectForKey:], and -[objectAtIndex:]) can return any objective-c object. You'll need to know ahead of time what to typecast it into to perform the appropriate operations on it. JSON is converted to the NSObject equivalents:
object -> NSDictionary
array -> NSArray
string -> NSString
number -> NSNumber
boolean -> NSNumber
float -> NSNumber
null -> NSNull
To differentiate between the various NSNumbers, you'll have to call the appropriate type method: -[intValue], -[boolValue], -[floatValue]. Check out the NSNumber docs for more info.
You can use my method for json parsing,
Parse Method:
-(void)jsonDeserialize:(NSString *)key fromDict:(id)content completionHandler:(void (^) (id parsedData, NSDictionary *fromDict))completionHandler{
if (key==nil && content ==nil) {
completionHandler(nil,nil);
}
if ([content isKindOfClass:[NSArray class]]) {
for (NSDictionary *obj in content) {
[self jsonDeserialize:key fromDict:obj completionHandler:completionHandler];
}
}
if ([content isKindOfClass:[NSDictionary class]]) {
id result = [content objectForKey:key];
if ([result isKindOfClass:[NSNull class]] || result == nil) {
NSDictionary *temp = (NSDictionary *)content;
NSArray *keys = [temp allKeys];
for (NSString *ikey in keys) {
[self jsonDeserialize:key fromDict:[content objectForKey:ikey] completionHandler:completionHandler];
}
}else{
completionHandler(result,content);
}
}
}
Method Call:
NSData *content = [NSData dataWithContentsOfFile:[[NSBundle mainBundle]pathForResource:#"Sample" ofType:#"json"]];
NSError *error;
//to get serialized json data...
id dictionary = [NSJSONSerialization JSONObjectWithData:content options:NSJSONReadingMutableContainers error:&error];
//get data for key called GetInfo
[self jsonDeserialize:#"GetInfo" fromDict:dictionary completionHandler:^(id parsedData, NSDictionary *fromDict) {
NSLog(#"%# - %#",parsedData,fromDict);
}];

Resources