Reputation: 2528
The NSManagedObjectModel consists of the following:
#import <CoreData/CoreData.h>
@interface Event : NSManagedObject
{
}
@property (nonatomic, retain) NSString * summary;
@property (nonatomic, retain) NSString * content;
@property (nonatomic, retain) NSDate * updated;
@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSDate * created;
@property (nonatomic, retain) NSString * ID;
@end
What I'm doing is trying to insert parsed JSON into the above Core Data attributes.
In the following, newNote is the name I have given to the NSManagedObject. Basically what I'm trying to accomplish is inserting the JSON into the Core Data so that I can then display it in a UITableView. The following function is what parses the JSON and inserts it into Core Data.
-(void)pullNotes {
UIApplication *app = [UIApplication alloc];
app.networkActivityIndicatorVisible = YES;
NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:@"api_key"];
[[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){
if(response.status == 200) {
NSLog(@"Pulling the users notes \n%@", [response asString]);
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
NSArray *notes = [object valueForKey:@"result"];
for (NSDictionary *singleNote in notes){
// newNote.created = [singleNote objectForKey:@"note created"]; Need to work on parsing these properly...
// newNote.updated = [singleNote objectForKey:@"note updated"]; Need to work on parsing these properly...
NSString *notetitle = [singleNote objectForKey:@"note title"];
NSString *notesummary = [singleNote objectForKey:@"note summary"];
NSString *noteid = [singleNote objectForKey:@"note id"];
NSString *notecontent = [singleNote objectForKey:@"note content"];
NSLog(@"Note Title: %@",notetitle);
NSLog(@"Note Summary: %@",notesummary);
NSLog(@"Note ID: %@",noteid);
NSLog(@"Note Content: %@",notecontent);
[newNote setValue:notecontent forKey:@"content"];
NSError *error = nil;
if (![newNote.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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
NSLog(@"Added Core Data Value: %@",[newNote valueForKey:@"content"]);
// NSDate *createdDate =
// NSDate *updatedDate =
[tableView reloadData];
app.networkActivityIndicatorVisible = NO;
}
}
if (response.status == 404) {
NSLog(@"FAIL\n%@", [response asString]);
app.networkActivityIndicatorVisible = NO;
}
}];
}
The NSLogs of the strings are returning the correct things, so I know that it should actually be inserting them into the Core Data...
So, my issue.
When that function gets called I get the following error.
2011-02-26 21:37:30.797 Notacious[22570:207] Unresolved error (null), (null)
I don't understand what is happening and hopefully someone will be able to shed some light on my issue.
EDIT
After a few hours of playing with it, I have it somewhat functioning:
-(void)pullNotes {
UIApplication *app = [UIApplication alloc];
app.networkActivityIndicatorVisible = YES;
NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:@"api_key"];
[[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){
if(response.status == 200) {
NSLog(@"Pulling the users notes \n%@", [response asString]);
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
appDelegate =(NotaciousAppDelegate *) [[UIApplication sharedApplication] delegate];
NSManagedObject *newNote;
NSArray *notes = [object valueForKey:@"result"];
for (NSDictionary *singleNote in notes){
// newNote.created = [singleNote objectForKey:@"note created"]; Need to work on parsing these properly...
// newNote.updated = [singleNote objectForKey:@"note updated"]; Need to work on parsing these properly...
NSString *notetitle = [singleNote objectForKey:@"note title"];
NSString *notesummary = [singleNote objectForKey:@"note summary"];
NSString *noteid = [singleNote objectForKey:@"note id"];
NSString *notecontent = [singleNote objectForKey:@"note content"];
NSLog(@"Note Title: %@",notetitle);
NSLog(@"Note Summary: %@",notesummary);
NSLog(@"Note ID: %@",noteid);
NSLog(@"Note Content: %@",notecontent);
newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
[newNote setValue:notecontent forKey:@"content"];
[newNote setValue:notesummary forKey:@"summary"];
[newNote setValue:notetitle forKey:@"title"];
[newNote setValue:noteid forKey:@"ID"];
NSLog(@"Added Core Data Value: %@",[newNote valueForKey:@"content"]);
// NSDate *createdDate =
// NSDate *updatedDate =
app.networkActivityIndicatorVisible = NO;
}
NSError *error = nil;
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
if (response.status == 404) {
NSLog(@"FAIL\n%@", [response asString]);
app.networkActivityIndicatorVisible = NO;
}
}];
}
However, it adds entities regardless of whether or not they were there before. What I want it to do is to find that if a note is already there, update it. If not, create a new one.
EDIT
Currently the function is now looking like this:
-(void)pullNotes {
UIApplication *app = [UIApplication alloc];
app.networkActivityIndicatorVisible = YES;
NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:@"api_key"];
[[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){
if(response.status == 200) {
NSLog(@"Pulling the users notes \n%@", [response asString]);
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
appDelegate =(NotaciousAppDelegate *) [[UIApplication sharedApplication] delegate];
NSManagedObject *newNote;
NSEntityDescription *noteEntity=[NSEntityDescription entityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
NSFetchRequest *noteFetch;
NSArray *fetchedNotes;
NSArray *notes = [object valueForKey:@"result"];
for (NSDictionary *singleNote in notes){
noteFetch=[[NSFetchRequest alloc] init];
[noteFetch setEntity:noteEntity];
NSPredicate *pred=[NSPredicate predicateWithFormat:@"noteID==%@",[singleNote objectForKey:@"note id"]];
[noteFetch setPredicate:pred];
NSError *fetchError=nil;
fetchedNotes=[appDelegate.managedObjectContext performFetch:&fetchError];
if (fetchError!=nil) {
NSLog(@"pullNotes fetchError=%@,details=%@",fetchError,fetchError.userInfo);
}
if ([fetchedNotes count]==0) {
// newNote.created = [singleNote objectForKey:@"note created"]; Need to work on parsing these properly...
// newNote.updated = [singleNote objectForKey:@"note updated"]; Need to work on parsing these properly...
NSString *notetitle = [singleNote objectForKey:@"note title"];
NSString *notesummary = [singleNote objectForKey:@"note summary"];
NSString *noteid = [singleNote objectForKey:@"note id"];
NSString *notecontent = [singleNote objectForKey:@"note content"];
NSLog(@"Note Title: %@",notetitle);
NSLog(@"Note Summary: %@",notesummary);
NSLog(@"Note ID: %@",noteid);
NSLog(@"Note Content: %@",notecontent);
newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
[newNote setValue:notecontent forKey:@"content"];
[newNote setValue:notesummary forKey:@"summary"];
[newNote setValue:notetitle forKey:@"title"];
[newNote setValue:noteid forKey:@"ID"];
// NSDate *createdDate =
// NSDate *updatedDate =
NSError *error = nil;
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
[noteFetch release];
app.networkActivityIndicatorVisible = NO;
}
}
if (response.status == 404) {
NSLog(@"FAIL\n%@", [response asString]);
app.networkActivityIndicatorVisible = NO;
}
}];
}
It seems that, however, I'm getting an error when it comes to calling the appDelegate.managedObjectContext
and it just errors out saying the following:
2011-03-03 12:11:01.616 Notacious[182:307] -[NSManagedObjectContext performFetch:]: unrecognized selector sent to instance 0x1474e0
2011-03-03 12:11:01.781 Notacious[182:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObjectContext performFetch:]: unrecognized selector sent to instance 0x1474e0'
I have already tried setting it to appDelegate.managedObjectContext_
but to avail, it seems to return an error stating that it isn't found within the appDelegate and that it isn't a getter object. However, it is clearly allocated here: http://cl.ly/4xdX
EDIT
Thought I'd best edit this to add in my current work around and another question underneath the original title. The following code is what I have built and is working perfectly fine (except the Edited string isn't being saved and in turn resulting in the following question of mine here).
-(void)syncNotes {
UIApplication *app = [UIApplication alloc];
app.networkActivityIndicatorVisible = YES;
NSDictionary *params = [NSDictionary dictionaryWithObject:authToken forKey:@"api_key"];
[[LRResty client] get:@"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){
if(response.status == 200) {
// NSLog(@"Successful Connection \n%@", [response asString]);
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
[parser release];
NSFetchRequest *noteFetch;
NSManagedObject *newNote;
appDelegate =(NotaciousAppDelegate *) [[UIApplication sharedApplication] delegate];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:appDelegate.managedObjectContext];
NSArray *fetchedNotes;
NSArray *notes = [object valueForKey:@"result"];
for (NSDictionary *singleNote in notes){
noteFetch=[[NSFetchRequest alloc] init];
[noteFetch setEntity:entity];
NSPredicate *pred=[NSPredicate predicateWithFormat:@"ID==%@",[singleNote objectForKey:@"note id"]];
[noteFetch setPredicate:pred];
NSError *fetchError=nil;
fetchedNotes=[appDelegate.managedObjectContext executeFetchRequest:noteFetch error:&fetchError];
if (fetchError!=nil) {
NSLog(@"syncNotes fetchError=%@,details=%@",fetchError,fetchError.userInfo);
}
if ([fetchedNotes count]==0) {
NSString *notelocked = [singleNote objectForKey:@"note locked"];
NSString *notecreated = [singleNote objectForKey:@"note created"];
NSString *noteupdated = [singleNote objectForKey:@"note updated"];
NSString *notetitle = [singleNote objectForKey:@"note title"];
NSString *notesummary = [singleNote objectForKey:@"note summary"];
NSString *noteid = [singleNote objectForKey:@"note id"];
NSString *notecontent = [singleNote objectForKey:@"note content"];
NSString *formattedTitle = [NSString decodeShit:notetitle];
NSString *formattedSummary = [NSString decodeShit:notesummary];
NSString *formattedContent = [NSString decodeShit:notecontent];
NSLog(@"Note Title: %@",formattedTitle);
NSLog(@"Note Summary: %@",formattedSummary);
NSLog(@"Note ID: %@",noteid);
NSLog(@"Note Content: %@",formattedContent);
NSLog(@"Note Created: %@",notecreated);
NSLog(@"Note Updated: %@",noteupdated);
NSLog(@"Note Locked: %@",notelocked);
newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:appDelegate.managedObjectContext];
[newNote setValue:formattedContent forKey:@"content"];
[newNote setValue:formattedSummary forKey:@"summary"];
[newNote setValue:formattedTitle forKey:@"title"];
[newNote setValue:noteid forKey:@"ID"];
[newNote setValue:notecreated forKey:@"created"];
[newNote setValue:noteupdated forKey:@"updated"];
[newNote setValue:notelocked forKey:@"locked"];
NSError *error = nil;
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
[noteFetch release];
}
app.networkActivityIndicatorVisible = NO;
}
if (response.status == 404) {
NSLog(@"FAIL\n%@", [response asString]);
app.networkActivityIndicatorVisible = NO;
}
}];
}
Now, sure this works, but when the content string is different, I want to update the correct note. Is there anyone on here that is willing to detail how to do this?
I know I keep asking questions on here, but want to get everything perfect and it seems my knowledge of such simple tasks is somewhat not there. Any input would be greatly appreciated!
EDIT
Ah, another request. If there is no noteID for a currently stored note, then remove that from the Core Data? Is that even possible?
Upvotes: 1
Views: 2043
Reputation: 64428
The immediate source of your error is that you are sending the save:error:
message to a non-existant managed object context. In turn, the managed object context doesn't exist because you never created or inserted the managed object newNote
into a context to begin with.
I get the impression that you haven't quite grasp that Core Data is built around objects instead of SQL tables, columns, rows etc. Every piece of data in Core Data is stored in one or more objects or the relationships between. All those objects and relationships create and object graph and it is that graph that Core Data really manages.
In your case, every JSON message decomposes into to numerous dictionaries, each one containing the data for one note. You need one managed object configured with the Event
entity, to store each note.
If you start with one of the Core Data using templates in Xcode, you will see that you get the managed object context, managed object model, persistent store coordinator and persistent stores all setup for you and assigned to attributes of the app delegate. In the following code I will use the attributes and names from the template.
You need something like this (I didn't compile this so watch for typos):
-(void)pullNotes{
//first, get the application delegate that manages the Core Data stack
// since the app itself is a singleton, we just ask it for its delegate
MyAppDelegateClass *appDelegate=(MyAppDelegateClass *) [[UIApplication sharedApplication] delegate];
// ...all the JSON pulling code
NSManagedObject *newNote;
NSArray *notes = [object valueForKey:@"result"];
for (NSDictionary *singleNote in notes){
// create another concrete instance of NSManagedObject
// for each logical note from JSON and insert it into
// the appDelegate's managedObjectContext attribute
newNote=[NSEntityDescription insertNewObjectForEntityForName:@"Event"
inManagedObjectContext:appDelegate.managedObjectContext_];
// Assign data from the JSON note to the attributes of this specific
// NSManagedObject instance.
[newNote setValue:[singleNote objectForKey:@"note content"] forKey:@"noteContent"];
// ... repeat for all other attributes you wish to have in the data
}
// only save after you've created all the notes. It's to slow to
// save after every note.
NSError *error = nil;
if (![appDelegate.managedObjectContext_ save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
Key points:
Update:
However, it adds entities regardless of whether or not they were there before. What I want it to do is to find that if a note is already there, update it. If not, create a new one.
To check for a pre-existing note, you need to attempt to fetch existing notes matching the new data. Here's simple implementation (uncompiled):
// call this outside the loop so you
// only have to call it once.
NSEntityDescription *noteEntity=[NSEntityDescription entityForName:@"Event" inManagedObjectContext:appDelegate.managedObjectContext];
NSFetchRequest *noteFetch;
NSArray *fetchedNotes;
// inside the for loop for each new note
noteFetch=[[NSFetchRequest alloc] init];
[noteFetch setEntity:noteEntity];
NSPredicate *pred=[NSPredicate predicateWithFormat:@"noteID==%@",[singleNote objectForKey:@"note id"]];
[noteFetch setPredicate:pred];
NSError *fetchError=nil;
fetchedNotes=[appDelegate.managedObjectContext_ performFetch:&fetchError];
if (fetchError!=nil) {
NSLog(@"pullNotes fetchError=%@,details=%@",fetchError,fetchError.userInfo);
}
if ([fetchedNotes count]==0) {
// create new note
}
[noteFetch release];
// end loop
You can form a predicate using the IN
test that would test against an array of all the new ``noteID values and do just one fetch but that is a little more complex. Unless you are processing hundreds of notes at a time, this technique will look just as fast from the user's perspective.
Upvotes: 1
Reputation: 601
You're just calling insertNewObjectForEntityForName which always creates a new object. Use a unique value as the key, create a fetch request for that key, and if it returns nothing, then you create a new object.
+ (Note*)NoteWithKey: (NSString*)key
{
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:key,@"KEY",nil];
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSManagedObjectModel *model = appDelegate.managedObjectModel;
NSFetchRequest *fetchRequest = [model fetchRequestFromTemplateWithName:@"GetKey" substitutionVariables:query];
NSError *error;
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
if (results && ([results count] > 0)) {
return [results objectAtIndex:0];
}
else {
Note *newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:context];
if (nil != newNote) {
[newNote setValue:key forKey:@"Key"];
}
return newNote;
}
}
Upvotes: 1
Reputation: 12081
Your header file ( http://cl.ly/4rz2 ) has a reference for an Event object, called newNote. But you never create an object to store in that reference. I would remove the reference in your header file, and keep it local in your JSON parsing method. Create one event for each singleNote you encounter in the json:
[..]
Event *newNote = nil;
for (NSDictionary *singleNote in notes) {
// create a new autoreleased object
newNote = [[[Event alloc] initWithEntity:aDescription insertIntoManagedObjectContext:aContext] autorelease];
newNote.content = [singleNote objectForKey:@"note content"];
}
// save managedobjectcontext
[..]
Unrelated
[UIApplication alloc]
is not something you normally want to do. You probably want to use something like: [UIApplication sharedApplication].n networkActivityIndicatorVisible = YES;
Upvotes: 0