I have writing a function in AppDelegate.m, which is :
-(void)saveContext {
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
if (error) {
NSLog(#"Unable to save changes.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
}
}
However, i received an error message :
Receiver type 'NSManagedObjectContext' for instance message is a forward declaration
My question is : How I could fix it ?
Thanks in advance
Related
MY core data objects are following:
Author (name) >> Book(title) >> Notes(text, dateOfNote)
Relation boksByAuthor >> notesOnBook
+(void)persistBooksToDisk:(NSManagedObjectContext *)workerContext
subClass:(EECoreStack*)eeCoreStack
fromURL:(NSURL*)fileURL{
[workerContext performBlock:^{
NSArray * array = [AP_TextExtract componentSeperatedByBooksFromTXTFile:fileURL];
for (NSString *body in array) {
Note *note = (Note *) [NSEntityDescription insertNewObjectForEntityForName:#"Note" inManagedObjectContext:workerContext];
note.dateOfNote = [AP_TextExtract dateOfNoteFromString:body];
note.text = [AP_TextExtract noteTextFromString:body];
Book *book = (Book *) [NSEntityDescription insertNewObjectForEntityForName:#"Book" inManagedObjectContext:workerContext];
book.title = [AP_TextExtract bookNameFromString:body];
[book addNotesInBook: note];
Author *author = (Author*)[NSEntityDescription insertNewObjectForEntityForName:#"Author" inManagedObjectContext:workerContext];
author.name = [AP_TextExtract authorNameFromString:body];
[author addBooksByAuthor: book];
}];
[eeCoreStack saveTemporaryWorkerContext:workerContext];
}
saveTemporaryContext:
- (void)saveTemporaryWorkerContext:(NSManagedObjectContext *)context {
[context performBlock:^{
NSError *error;
[context save:&error];
if (error) {
NSLog(#"ERROR SAVING TEMPORARY WORKER MOC %#: %#:", [error localizedDescription], [error userInfo]);
}else {
[self saveMainContext];
}
}];
}
- (void)saveMainContext {
[self.mainContext performBlock:^{
NSError *error = nil;
[self.mainContext save:&error];
if(error){
NSLog(#"ERROR SAVING MAIN MOC %#: %#:", [error localizedDescription], [error userInfo]);
}else {
[self saveMasterContext];
}
}];
}
- (void)saveMasterContext {
[self.masterContext performBlock:^{
NSError *error = nil;
[self.masterContext save:&error];
if(error){
NSLog(#"ERROR SAVING MASTER CONTEXT %#; : %#", [error localizedDescription], [error userInfo]);
}
}];
[[NSNotificationCenter defaultCenter] postNotificationName:#"doneSavingMasterContext" object:nil];
}
The data is saved from Text File and saving follows pattern:
workerContext > mainContext > masterContext (Store)
First time data is saved successfully but later when it tries to save updated text file (with only change in note text), it throws out following error:
ERROR SAVING MASTER CONTEXT An error occurred while saving.
; : {
NSAffectedObjectsErrorKey = (
"<Note: 0x6000002d6570> (entity: Note; id: 0x600001424480 <x-coredata:///Note/t2371834C-1581-4A68-A56E-77E734B276CC1244> ; data:
text = \"quitely right );
NSUnderlyingException = "Cannot update object that was never inserted.";
}
One thing I noticed that after restarting app, the data is saved but again start to give same error after that.
Edit 1: this error only show if, I update existing attribute, not if I insert new instance of attribute.
When you are updating the data, first fetch the core data object using NSFetchRequest and then update the necessary data. Then make an attempt to save. You might be trying to update the data without fetching.
I am trying to insert values into core data object, but while saving those values am getting null response, but it's getting updated into Core data database,
Used Code:
AllAssets *allAssets1 = [NSEntityDescription insertNewObjectForEntityForName:#"AllAssets" inManagedObjectContext:context];
allAssets1.assetID = [NSNumber numberWithInt:assetID];
allAssets1.originalassetname = assetName;
[images addObject:[showrelatedAssets objectAtIndex:i]];
[products addObject:allAssets1];
[show_AddtoOffline addRel_Assets:products];
getting null from below NSLog statement,
if ([images count] !=0 ) {
if ([context save:&error])
{
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
Of course it's giving you null. Your data is saved without any error, and you are logging error and [error localizedDescription].
Since there is no error, these will be null.
The method save: of NSManagedObjectContext returns YES if save is successful, and NO if save fails. In the latter case, it also stores the error encountered in the &error address you have passed.
Your if statement should be:
if ([context save:&error]) {
NSLog(#"Save successful");
} else {
NSLog(#"Could not save: %# | %#", error, error.localizedDescription);
}
if ([context save:&error])
{
// This means your data is saved properly
}
What's wrong ? From the doc
(BOOL)save:(NSError **)error
Return Value
YES if the save succeeds, otherwise NO.
So when you enter the loop, save was successful and error is still null.
Its a common pattern to add an error output parameter when writing Objective-c methods.
As far as I know this is how you create a method that return an error if something is wrong:
- (void)doSomethingWithObj:(id)obj error:(NSError *__autoreleasing *)error {
BOOL success = NO;
// do somthing...
if (!success) {
*error = [NSError errorWithDomain:#"the.domain" code:0 userInfo:nil];
}
}
Now there are times when you just want that error parameter to reflect an error occurred in some other method you use inside your method, lets say:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:nil];
}
So I thought ok, I'll just pass the error parameter to the inside method, like this:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:error];
if (error) {
NSLog(#"error %#", error);
}
}
But this approach has two issues:
1. the if (error) check returns YES even if there is no error.
2. the log line generates this warning: Format specifies type 'id' but the argument has type 'NSError *__autoreleasing *'
So what am I doing wrong here?
There are a couple of things wrong. Firstly the NSError object should not be used to test for errors, instead use the method's return value. Therefore your first example method should return BOOL to indicate success:
- (BOOL)doSomethingWithObj:(id)obj error:(NSError *__autoreleasing *)error {
BOOL success = NO;
// do somthing...
if (!success) {
if (error) { // Check it's been passed, and if so create the error object.
*error = [NSError errorWithDomain:#"the.domain" code:0 userInfo:nil];
}
}
return success;
}
And test for results being nil, not error being non-nil:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:error];
if (!results) {
if (error && *error)
NSLog(#"error %#", [(*error) localizedDescription]); // Here is your 2. I think.
else
NSLog(#"Unknown error");
}
}
Secondly the error parameter is commonly optional (as seen in your code where you pass nil, which should be NULL actually). Therefore you need to test if it's been passed before dereferencing it (see code above).
However to answer your overall question, yes it's good to pass the error parameter along to subordinate method calls and is commonly used.
I have no idea about your 2. until you update your code... standing by. I think your 2. issue is because you need to use [error localizedDescription] with NSLog().
You are passing address of error not actual error this means &error
So you need to derefrence the error pointer.NSError *__autoreleasing * you are taking parameter as address of error.We generally do this because objective c can return only one value.But error need to be known from where we are calling the mehod so passing it as address of error will make change to error if there an error comes in calle function.
So if any error comes in below line
NSArray *results = [context executeFetchRequest:request error:error];
than it is automatically know to calle function i.e doSomethingWithObj
if (*error) {
NSLog(#"error %#", (*error).description);
}
Use
NSLog(#"error %#", (*error).description);
instead of
NSLog(#"error %#", (error).description);
you have to pass &error
When I try to save in Core Data I'm getting in the console:
Unresolved error (null), (null)
- (void)saveInManagedObjectContext:(NSManagedObjectContext *)context {
if (context == nil) {
// Use default MOC
context = self.managedObjectContext;
NSError *error = nil;
if ([context hasChanges] && ![context 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.
*/
DLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
It's kind of hard to debug this when NSError object is nil.
What can I do to get more useful information why the save operation is not successful?
At the moment I can't see any AUser objects in my sqlite3 app database.
This is the code I have to create a user. Am I missing something? There are no warnings/errors with the code.
AUser *user = [NSEntityDescription insertNewObjectForEntityForName:#"AUser" inManagedObjectContext:_managedObjectContext]; // _managedObjectContext is declared elsewhere
user.name = username; //username is some string declared elsewhere / name is an attribute of AUser
You need to perform a save on the context.
NSError* error = nil;
if(![context save:&error]) {
// something went wrong
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
When you perform a save, data in the context are saved to the persistent store coordinator (hence in your sql store).
P.S. Based on the discussion with #G. Shearer, in production if the context fails to save, you can handle the error gracefully. This means not using abort() call that causes the app to crash.
NSError* error = nil;
if(![context save:&error]) {
// save failed, perform an action like alerting the user, etc.
} else {
// save success
}
You need to call save after creating the object.
Example:
NSError *error;
if ([user.managedObjectContext save:&error]) {
//Success!
} else {
//Failure. Check error.
}
you need to call
NSError *error = nil;
[_managedObjectContext save:&error];
if(error != nil) NSLog(#"core data error: %#",[[error userInfo] description]);
before the object will be persisted to the database