UA-17470720-3

Jump to content


Photo
- - - - -

Can't submit to Core Data database


  • Please log in to reply
13 replies to this topic

#1 selch

selch

    Starting Out

  • STV 3.0 Std
  • Pip
  • 8 posts
Reputation: 2
Good

Posted 28 May 2013 - 11:18 AM

I'm having a hard time understanding why this method won't insert my new record into Core Data. When I log the postObject at the end it does have the correct data. This method is being called from a UIButton in a SCViewController. Ideas please?

 

Thanks!

 

Log output:

2013-05-28 14:13:25.891 appTest[85609:c07] post object: <NSManagedObject: 0x8462900> (entity: Post; id: 0x8462950 <x-coredata:///Post/t52DAEDC6-B787-407A-969E-24001C5B16A22> ; data: {

    text = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamc";

    timeStamp = "2013-05-28 19:13:25 +0000";

})

 

 

 

-(void)post:(id)sender {    
        // Get managedObjectContext from application delegate
    NSManagedObjectContext *context = [(id)[UIApplication
                                            sharedApplication].delegate managedObjectContext];
        // let STV know about Post Entity
    SCEntityDefinition *postEntityDef = [SCEntityDefinition definitionWithEntityName:@"Post" managedObjectContext:context propertyNamesString:@"text;timeStamp"];
    
        // create a Core Data store to create the new post object for you
    SCCoreDataStore *postStore = [SCCoreDataStore storeWithManagedObjectContext:context defaultEntityDefinition:postEntityDef];
        
        // use the store the create and insert the object
    NSManagedObject *postObject = (NSManagedObject *)[postStore createNewObject];

    [postObject setValue:[NSDate date] forKey:@"timeStamp"];
    [postObject setValue:textView.text forKey:@"text"];
    
    [postStore insertObject: postObject];

    NSLog(@"post object: %@", postObject);
    
}
 

 

 

 


  • hoinivoniough likes this

#2 wizgod

wizgod

    I'm what you guys call a User

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 575 posts
  • LocationThe Grid
Reputation: 149
Popular

Posted 28 May 2013 - 03:14 PM

Greetings Program!

 

There's probably a SC equivalent but see if this works:

 

        [postStore insertObject: postObject];

	NSError *error = nil;
	if (![context save:&error]) 
        {
		NSLog(@"Error - could not insert post: %@", [error localizedDescription]);
	}
	else
	{
		NSLog(@"Inserted new post: %@", postObject);
	}

 

Wg


Edited by wizgod, 28 May 2013 - 03:16 PM.

  • Tarek and selch like this

P.S. I love Swift... talk Swift.. Never too old school to learn yet another programming language. LOL! ;-)


#3 selch

selch

    Starting Out

  • STV 3.0 Std
  • Pip
  • 8 posts
Reputation: 2
Good

Posted 28 May 2013 - 05:58 PM

Greetings Program!

 

There's probably a SC equivalent but see if this works:

 

        [postStore insertObject: postObject];

	NSError *error = nil;
	if (![context save:&error]) 
        {
		NSLog(@"Error - could not insert post: %@", [error localizedDescription]);
	}
	else
	{
		NSLog(@"Inserted new post: %@", postObject);
	}

 

Wg

Wow. Literally all I did was paste your logging code at the end of my method and it started magically inserting the records. I don't understand why but hats off to you sir. Thanks for your help.



#4 ozie

ozie

    ¯\_(ツ)_/¯

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 526 posts
  • LocationAustralia
Reputation: 169
Popular

Posted 28 May 2013 - 06:12 PM

it works because

[context save:&error]

is saving the database to the file, "the context is told to save".. its just in a IF test just incase something goes wrong you will know and can do something (more than just log)


Edited by ozie, 28 May 2013 - 06:18 PM.

  • Tarek likes this

P.S. I hate Swift.. don't talk Swift.. Too old school to learn yet another programming language.


#5 selch

selch

    Starting Out

  • STV 3.0 Std
  • Pip
  • 8 posts
Reputation: 2
Good

Posted 28 May 2013 - 06:15 PM

it works because

[context save:&error]

is saving the database to the file, "the context is told to save".. its just in a IF test just incase something goes wrong you will know and can do something (more than just log)

I see. I thought [postStore insertObject: postObject]; was enough to do the commit and save. But I was really missing this crucial last step. Thanks for the explanation. 



#6 ozie

ozie

    ¯\_(ツ)_/¯

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 526 posts
  • LocationAustralia
Reputation: 169
Popular

Posted 28 May 2013 - 06:21 PM

you would find in the appDelegate the code for - (void)saveContext, which is fired when the app closes.. ie the home button pressed.. and the data is saved to file

but if you are running it in xcode and you click stop.. or the app crashes, the - (void)saveContext is never fired and the data is lost

 

using [context save:&error] all the time makes sure the data is ALWAYS saved.. but does use some processing so its not good to use in like a loop  :) 


P.S. I hate Swift.. don't talk Swift.. Too old school to learn yet another programming language.


#7 wizgod

wizgod

    I'm what you guys call a User

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 575 posts
  • LocationThe Grid
Reputation: 149
Popular

Posted 28 May 2013 - 07:02 PM

Wow. Literally all I did was paste your logging code at the end of my method and it started magically inserting the records. I don't understand why but hats off to you sir. Thanks for your help.

 

Sorry about not explaining about the [context save:&error] but glad my good friend ozie was around :)


P.S. I love Swift... talk Swift.. Never too old school to learn yet another programming language. LOL! ;-)


#8 wizgod

wizgod

    I'm what you guys call a User

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 575 posts
  • LocationThe Grid
Reputation: 149
Popular

Posted 28 May 2013 - 07:15 PM

using [context save:&error] all the time makes sure the data is ALWAYS saved.. but does use some processing so its not good to use in like a loop  :) 

 

Indeed, but if you need to do it (i.e. bulk delete) loop through all the objects and make your changes and then save the context so you only do it once and it affects all records. This way you won't have the processing hit of saving the context after modifying (deleting) each record.

 

When I sync my service orders I delete the old ones before retrieving the new ones:

 

			 NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
			 [fetch setEntity:[NSEntityDescription entityForName:@"ServiceOrder" inManagedObjectContext:managedContext]];
			 [fetch setIncludesPropertyValues:NO]; //only fetch the managedObjectID
			 
			 NSError *error = nil;
			 
			 NSArray *serviceOrders = [managedContext executeFetchRequest:fetch error:&error];
			 
			 for (NSManagedObject *serviceOrder in serviceOrders) {
                                 // Mark these records for deletion.
				 [managedContext deleteObject:serviceOrder];
			 }
			 
			 error = nil;

                         // Commit the delete action.
			 if (![managedContext save:&error])
			 {
				 NSLog(@"Error - could not delete service orders: %@", [error localizedDescription]);
			 }
			 else
			 {
				 NSLog(@"Deleted existing service orders.");
			 }

 

Wg


P.S. I love Swift... talk Swift.. Never too old school to learn yet another programming language. LOL! ;-)


#9 Dave Guerin

Dave Guerin

    Forum Master

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 545 posts
  • LocationIreland
Reputation: 137
Popular

Posted 29 May 2013 - 01:54 AM

Declaring error as

NSError *error = nil;

creates a memory leak. error needs to be declared as __autoreleasing

__autoreleasing NSError *error = nil;

And when you do

[managedContext deleteObject:serviceOrder];

that's it, the object has been deleted from the managedContext.


Similarly

[postStore insertObject: postObject];
NSLog(@"post object: %@", postObject);

and the object is inserted and available to the managedContext already (hence the NSLog there works).


All that

[managedContext save:&error]

is doing is persisting that insertion/deletion (and any other changes to the managedContext since the last save) to disk.

wizgod's NSLogs are somewhat misleading. Core Data might throw an error there for something else, not necessarily the just deleted object(s).



And finally... :-)
When working with Core Data entities it's helps me a lot to have Xcode generate the NSManagedObject subclasses. Select an entity in the data modeling view and then Editor>Create NSManagedObject subclasses. Then you can do stuff like:
 

    postObject.timeStamp = [NSDate date];
    postObject.text = textView.text;

 
HTH


  • Tarek likes this
Cheers,

Dave

www.dgapps.ie

#10 wizgod

wizgod

    I'm what you guys call a User

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 575 posts
  • LocationThe Grid
Reputation: 149
Popular

Posted 29 May 2013 - 05:46 AM

Hey Dave!

Thanks for the heads up on the NSError, but doesn't [error localizedDescription] give a detailed description about the error? My NSLog text tells me what I was trying to do and my understanding was that [error localizedDescription] would provide me with the details of why it couldn't save the managedContext.

My comment about marking the records for deletion is probably misleading; as you noted the objects are added (or deleted) from the managedContext and won't persist until the managedContext is saved. So for bulk deletes like mine, it is better to delete all of the objects from the managedContext before persisting the changes to disk (only once outside the loop) then to do it after each delete (inside the loop).

Wg

P.S. I love Swift... talk Swift.. Never too old school to learn yet another programming language. LOL! ;-)


#11 Dave Guerin

Dave Guerin

    Forum Master

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 545 posts
  • LocationIreland
Reputation: 137
Popular

Posted 29 May 2013 - 06:12 AM

Hi wizgod,
 

[error localizedDescription] will give you the error, but all you were trying to do at that point was a [managedContext save:&error], you'd done the [managedContext deleteObject:serviceOrder] earlier and Core Data would have thrown up errors it self then if there had been a problem.

I have one method

 

-(void)saveManagedObjectContext {
    if ([kdgManagedObjectContext hasChanges]) {
        __autoreleasing NSError *error = nil;
        if (![kdgManagedObjectContext save:&error]) {
            NSLog(@"managedObjectContext save ERROR: %@", [error localizedDescription]);
        }
        else {
            NSLog(@"managedObjectContext saved");
        }        
    }
}

 

which I call quite requlary. I was getting quite a lot of "CoreData: error: (19) PRIMARY KEY must be unique" errors which I think in the end was because I was doing [kdgManagedObjectContext save:&error] to early and a duplicate entity was being created when i tried to access the original entity. That wasted a few days!


Cheers,

Dave

www.dgapps.ie

#12 wizgod

wizgod

    I'm what you guys call a User

  • STV 5.0 Pro
  • PipPipPipPipPipPipPip
  • 575 posts
  • LocationThe Grid
Reputation: 149
Popular

Posted 29 May 2013 - 06:48 AM

Hey Dave!

 

Thanks for that; I hadn't had an error throw on me yet so I didn't know how much detail the error would have. Being from a .NET background, I was expecting it would be equivalent  to the exceptions :D

 

Wg


P.S. I love Swift... talk Swift.. Never too old school to learn yet another programming language. LOL! ;-)


#13 Tarek

Tarek

    Forum Admin

  • Administrators
  • 3670 posts
Reputation: 452
Popular

Posted 29 May 2013 - 08:46 AM

Just wanted to add that STV (or SCCoreDataStore to be more specific) automatically calls [context save:] whenever the app is about to go to background or terminate, so you don't actually have to call this method explicitly yourself. Delaying calling [context save:] until the app is about to lose control has been the recommended approach by Apple for a long time now, mostly because it saves some resources. The whole idea is based on the fact that you wouldn't gain anything by calling [context save:] whenever an object is inserted, so better save resources and call [context save:] once for all insertions/deletions/updates. Having said that, it's probably ok to call [context save:] yourself on newer devices that are faster and have more memory. I just can't think of a reason where you'd need to do so (unless you're worried that iOS would crash and data is lost :) )

 

P.S.: when testing on the simulator, a lot of new comers make the mistake of shutting the app by shutting the simulator itself. When you shut the simulator, the app itself never gets the chance to actually shut down, and thus your Core Data changes aren't committed. The correct way to test the above is to click the home button on the simulator before quitting it. 



#14 selch

selch

    Starting Out

  • STV 3.0 Std
  • Pip
  • 8 posts
Reputation: 2
Good

Posted 30 May 2013 - 06:57 AM

P.S.: when testing on the simulator, a lot of new comers make the mistake of shutting the app by shutting the simulator itself. When you shut the simulator, the app itself never gets the chance to actually shut down, and thus your Core Data changes aren't committed. The correct way to test the above is to click the home button on the simulator before quitting it. 

 

This is the problem I was experiencing, however, I WAS hitting the keyboard shortcut for the home screen before quitting the simulator or stopping the program and my changes weren't being written. And that is why I wrote my original post. Maybe I was still doing something wrong or my code is crap or both. Anyway, I still learned a lot from this thread. :)


Edited by selch, 30 May 2013 - 06:57 AM.

  • Tarek likes this




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users