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
Related
NOTE: I've looked at similar questons, but didn't find one that describes this situation.
I'm looking at the following example code from Apple regarding Core Data concurrency (https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/Concurrency.html)
NSArray *jsonArray = …;
NSPersistentContainer *container = self.persistentContainer;
[container performBackgroundTask:^(NSManagedObjectContext *context) {
for (NSDictionary *jsonObject in jsonArray) {
AAAEmployeeMO *mo = [[AAAEmployeeMO alloc] initWithContext:context];
[mo populateFromJSON:jsonObject];
}
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Failure to save context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
}];
In my app, the save is not initiated until the user taps the save button on the screen. How do I go about it, should I use a child context instead for that situation, where the private context is a property of the VC?
NSArray *jsonArray = …; //JSON data to be imported into Core Data
NSManagedObjectContext *moc = self.persistentContainer.viewContext; //Our primary context on the main queue
NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[private setParentContext:moc];
[private performBlock:^{
for (NSDictionary *jsonObject in jsonArray) {
NSManagedObject *mo = …; // WHICH CONTEXT TO USE? <<<======
//update MO with data from the dictionary
}
NSError *error = nil;
if (![private save:&error]) {
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
}
And then once the user taps save do this:
NSManagedObjectContext *moc = self.persistentContainer.viewContext; //Our primary context on the main queue
[moc performBlockAndWait:^{
NSError *error = nil;
if (![moc save:&error]) {
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
}];
}];
Also note the question which moc to use in the example above (<<<=====)
EDIT: What I ended up doing in the end is save the child context immediately so that the table only uses viewContext to display the results. If the user then exits without saving, I delete all the results again from the viewContext. The save button is still there, but now only sets a flag indicating not to delete the results.
If you have one page of forms and you want it to save when the user presses the save button, then simply take the data from the textFields (or whatever your data input is) and put it into core data using performBackgroundTask. Since the data is only stored in the textFields while the user is editing if the user pushes back his edits will be lost.
If you have a lots of changes to a complex document with lots of different entities that the user can create or destroy or link and all of that is only saved when the user presses save then you should use a child context. You would display the data based on the values in the child context but only push those changes to the parent context if the user presses save. This is a very rare situation and I have never personally encountered the need to do this.
I strongly suspect that you are in the first case. Don't use child context. Use performBackgroundTask and save the data when the user presses save.
(also the correct context to use inside the block [private performBlock:^{ is the private context)
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.
I'm using core data to save some integer (rate) and then I call save in the context:
HeartRateBeat * beat = [HeartRateBeat heartRateWithHeartRate:rate
ofRecordTitle:self.recordTitle
inManagedObjectContext:document.managedObjectContext];
NSError * error;
[document.managedObjectContext save:&error];
Inside that convenient method I create the object using NSEntityDescription like this:
heartRateBeat = [NSEntityDescription insertNewObjectForEntityForName:#"HeartRateBeat" inManagedObjectContext:context];
(I only copied some important code, just to show what I did.)
I immediately execute a fetch request after every single heart beat inserted and managed object context saved (I save immediately), and the request shows that heart beat does appear to be stored inside Core Data (with growing array of heart beats), but if I restart my app (I'm using simulator BTW) I know things aren't actually getting saved to disk because it starts anew. Checking with SQLite3 command line shows empty tables. What am I missing here?
I get the same problem but I think its just because, I assume, like me you are just stopping the app through xcode and not actually closing it down. I use this code to force a write. Im using a UIManagedDocument, shared through appdelegate, rather than setting everything up manually.
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[[AppDelegate sharedAppDelegate].userDatabase saveToURL:[AppDelegate sharedAppDelegate].userDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
I don't know about you guys, but when I want my Core Data to save, my Core Data better save.
Here's some code that will for sure save all of your Core Datas.
-(void)forceSave {
NSManagedObjectContext * context = self.managedObjectContext; //Get your context here.
if (!context) {
NSLog(#"NO CONTEXT!");
return;
}
NSError * error;
BOOL success = [context save:&error];
if (error || !success) {
NSLog(#"success: %# - error: %#", success ? #"true" : #"false", error);
}
[context performSelectorOnMainThread:#selector(save:) withObject:nil waitUntilDone:YES];
[context performSelector:#selector(save:) withObject:nil afterDelay:1.0];
[context setStalenessInterval:6.0];
while (context) {
[context performBlock:^(){
NSError * error;
bool success = [context save:&error];
if (error || !success)
NSLog(#"success: %# - error: %#", success ? #"true" : #"false", error);
}];
context = context.parentContext;
}
NSLog(#"successful save!");
}
Note that this is BAD CODE. Among other problems, it's not thread-safe and takes too long. However, try using this and deleting some parts of it to your satisfaction :)
[request2 setEntity:entity];
NSPredicate * predicate2 = [ NSPredicate predicateWithFormat:#"logoFrameNum == %#",[NSNumber numberWithInt:7]];
[request2 setPredicate:predicate2];
NSManagedObject * collectionList2 = [[ managedObjectContext executeFetchRequest:request2 error:&error2] objectAtIndex:0];
NSLog(#"context :%#", deleteContext1);
[managedObjectContext deleteObject:collectionList2];
BOOL yesorno = [collectionList2 isDeleted];
NSLog(#"yesorno : %i", yesorno);
NSError * error10;
NSLog(#"[managedObjectContext ] : %#", deleteContext1);
[collectionList2 release];
if (![managedObjectContext save:&error10]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error10, [error userInfo]);
exit(-1); // Fail
}
There is much more source above it. Change variables or get data from coredata is well performed with the same NSManagedObjectContex I have there. However delete with that context makes me crazy. It crashes without any error message just in
if (![managedObjectContext save:&error10]) {
I tried get a new context and so on and on........a lot..
You are performing a release on an object (collectionList2) that you don't own. This may cause a crash later on (for example, during the save). Try removing the release.
Maybe you are trying to delete a nil object.
Also, you should do all this within one single NSManagedObjectContext.
Try putting your save:error: method right below the deleteObject: call.