NSManagedObjectContext doesn't update object persistently - ios

I have an NSManagedObject (User) in database. Then I'm trying to fetch that object from database and update field firstName:
NSFetchRequest *fetchR = [NSFetchRequest fetchRequestWithEntityName:#"User"];
NSError *err = nil;
NSArray *allUsers = [self.managedObjectContext executeFetchRequest:fetchR error:&err];
TMUser *profile = allUsers.firstObject;
[profile setValue:#"Username" forKey:#"firstName"];
[self.managedObjectContext save:&err];
if (err) {
NSLog(#"Error: %#", err.localizedDescription);
}
The code passes without errors. But if I relaunch my app, fetch request retunrs user without updated field "firstName". I have only 1 NSManagedObjectContext. All Core Data stack was initialized successfully. After fetch my user is:
Printing description of allUsers:
<_PFArray 0x14ed6600>(
ID:3451
firstName:Johnatan
lastName:Hike
phone:380995046960
email:igor#email.com
language:en
)
For some reason object changes wasn't registered in context(Context hasChanges = NO before save). What am I doing wrong? Please, help

I think you are not saving the master context.
Please check that you call:
[managedObjectContext save:&error];
on all child contexts that save the data,
and after that on the master context as well.
You have one global function(in AppDelegate) saveContext which saves everything and which I can call from anywhere safely.

I solved my problem. I recreated NSManagedObject subclass from xcdatamodeld scheme and it works. I found that if I add another properties(readonly etc.), not related to data model scheme or change property type from NSNumber(aka bool) to BOOL, it stops updating existed objects in database.

Related

Core Data Entity Created But Attributes Not Saving

I have a relatively simple entity. When I create it, set its attributes, and save it, it saves successfully. I can later retrieve it and it is not nil and I get a successful save message from MagicalRecord.
When I retrieve it and try to access any attribute though the attribute is nil. The entity itself is fine but the attributes are all nil. I have checked they are all definitely set correctly before I save.
I haven't encountered this problem before. Why could it be occurring?
NB: This doesn't happen every time. Most times I call the method to create and save this entity it can later be retrieved without any issues. The problem is intermittent but possible to replicate on every run.
Code:
Entity1 *entity1 = [Entity1 MR_createEntityInContext:localContext];
[entity1 setUpEntity:myobject];
EntityChild *entityChild=[EntityChild MR_createEntityInContext:localContext];
[entityChild setUpEntityChild:entity.child withContext:localContext];
[entityChild setEntity1:entity1];
[localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
}];
Update:
If I look in the sqlite database and search for the entity it actually doesn't exist at all. So MagicalRecord tells me it saves, CoreData lets me retrieve a non-nil object (albeit with nil attributes) but no record exists in the database.
I did not understand ur code standards. As I am new to IOS Development. I Used below code for retrieving.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityRef = [NSEntityDescription entityForName:#"Entity1" inManagedObjectContext:appDelegate.managedObjectContext];//localContext
[fetchRequest setEntity:entityRef];
NSError *error=nil;
NSArray *detailsArray = [appDelegate.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Unable to execute fetch request.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
Saving the data
NSManagedObjectContext *context = [appDelegate managedObjectContext];//localContext
NSManagedObject *objectRef = [NSEntityDescription
insertNewObjectForEntityForName:#"Entity1" inManagedObjectContext:context];
[objectRef setValue:#"IOS" forKey:#"Name"];
[objectRef setValue:#"positive" forKey:#"Attitude"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
Hope it helps you...!
Ok, I got to the bottom of this. It wasn't a problem with the code when I did the save. It was actually a problem with some code in another class that was retrieving the data from the wrong context. When I changed the context it worked correctly.
I'm still not sure why this only happened occasionally and not every time the code was run but it's working now.
Thanks for your help anyway everyone.

Delete all my data in my Core Data store

I have followed a variety of posts here in SO to delete all the data from an app so I can start over. I have tried:
A) Deleting all the data:
NSArray *entities = model.entities;
for (NSEntityDescription *entityDescription in entities) {
[self deleteAllObjectsWithEntityName:entityDescription.name
inContext:context];
}
if ([context save:&error]) {
...
- (void)deleteAllObjectsWithEntityName:(NSString *)entityName
inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:entityName];
fetchRequest.includesPropertyValues = NO;
fetchRequest.includesSubentities = NO;
NSError *error;
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *managedObject in items) {
[context deleteObject:managedObject];
NSLog(#"Deleted %#", entityName);
}
}
B) Delete the physical data store:
NSError *error;
NSPersistentStore *store = [[self persistentStoreCoordinator].persistentStores lastObject];
NSURL *storeURL = store.URL;
NSPersistentStoreCoordinator *storeCoordinator = store.persistentStoreCoordinator;
[self.diskManagedObjectContext reset]; // there is a local instance variable for the disk managed context
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
_diskManagedObjectContext = nil;
C) Perform step A and then step B
In all combinations it appears to run with no errors, but whenever I receive new data (via my HTTP service) and start adding it to the re-initialized data store I get all kinds of duplicate data and various data issues. I usually have to delete and reinstall the app to get the data clean enough to re-initialize.
It should be fairly straightforward. The user logs in. App data is downloaded and saved in the store. User logs out and logs in again or as different ID and new data is brought down.
Any ideas why the above methods are not working?
UPDATE:
I edited my code above to show that I am saving the context and removing the data store file. I still end up with bad leftover data. Could the problem be the multiple contexts we use? We have three contexts we use in the app: a UI-managed context, a background context and a disk-managed context. A notification listener takes care of merging changes in the background context with the disk managed context.
I have tried altering the above code to loop through the objects in all three contexts and we set them all to nil. The authentication code takes care of reinitializing the contexts. Still banging my head on what seems like a simple issue.
After
for (NSEntityDescription *entityDescription in entities) {
[self deleteAllObjectsWithEntityName:entityDescription.name
inContext:context];
}
Save your context
[context save:&error];
(B) doesn't delete the physical store, it just dissociates it from your app for the time being. No doubt you just attach it again shortly thereafter or upon next launch.
Use [[NSFileManager defaultManager] removeItemAtURL:... error:...] actually to delete the file from your disk.
As the other posters have said, you fail to NSManagedObjectContext -save: in (A) so you affect what's in that one context but not in the persistent store. Contexts are just in-memory scratch pads so as soon as you create a new context it'll be able to find everything in the persistent store again unless or until you save the one with the modifications.

CoreData gets emptied after adding data into it

I have a CoreData entity Tracker which stores the dates.
The app receives a notification and the CheckListViewController enters data in CoreData for up to 13 days, so when the CheckListViewController gets dismissed, the CoreData entity Tracker will be filled with 13 rows.
In the MainViewController (which dismisses CheckListViewController), I have the following code:
- (void)dataSaved {
self.checkListVC dismissViewControllerAnimated:YES completion:^{
// fetching all the data from 'Tracker' entity and doing NSLog on it
// all data gets logged in console without any issues
}];
}
Now, after that somewhere in my code, I fetch all the data from the entity Tracker but the return data is empty. The CoreData doesn't show any error it simply returns and empty array.
Edit:
Code to fetch results from CoreData
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:ENTITY];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:NO];
[request setSortDescriptors:#[sortDescriptor]];
request.predicate = (fromDate && toDate) ? [NSPredicate predicateWithFormat:#"date >= %# AND date <= %#", fromDate, toDate] : nil;
__block NSArray* fetchedHabits;
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
fetchedHabits = [managedObjectContext executeFetchRequest:request error:&error];
if (error) NSLog(#"Unknown error occurred while fetching results from CoreData, %# : %#", error, [error userInfo]);
}];
CoreData model:
Update 1:
So as you can see there are two entities, namely Habit and Tracker. When I fetch results from Habit it all works fine, but when I try to fetch results from Tracker it gives me an empty array. I have a common NSManagedObjectContext instance because you can manage multiple CoreData entities with single managedObjectContext.
I have checked managedObjectContext.persistentStoreCoordinator.managedObjectModel.entitiesByName and it also lists both the entities.
Update 2:
Code where I add data in to Tracker
TrackerCoreData *tracker = [NSEntityDescription insertNewObjectForEntityForName:ENTITY
inManagedObjectContext:managedObjectContext];
tracker.date = date;
tracker.habits = habits;
// saving CoreData explicitly
NSError *error = nil;
[managedObjectContext save:&error];
There could be many reasons for your failure to display the records:
data was not saved
data was not retrieved correctly
data was not displayed correctly
All of these could be potentially complicated scenarios, but you should check them in this order.
A much better approach: use NSFetchedResultsController for your main view controller and have the delegate methods take care of updating your table view. No need to fetch, no work to be done in any completion methods - just save the data and the FRC will update your table.
Edit: how to check the physical database
It is possible that your data only exists in memory but is not actually saved to the database. Find the actual database file (in the documents folder of the app from the Simulator) and check it with the sqlite3 command line utility, or with the Firefox plugin "SQLite Manager".
Edit2: more concrete recommendations
You should make sure that you call:
[managedObjectContext save:&error];
Also double-check what your ENTITY macro stands for (not a very smart name).
It seems to me that you are overusing the block methods to no apparent purpose. First try to make everything work on the main thread (one context!). Only if you get performance problems consider background threads and context and calls to performBlock etc.

I have an issue in saving individual Object, rather than saving all the objects in NSMangedObject stack ??

I am new to the IOS programming, currently learning core data, I went into running the code where i need to save only specific objects in core data. So how can i do that?
According to the scenario, i have data from server as well as local storage (core data), but when user close the app (went to background) I want to store the data in the server(if net available) if not then in the local storage (but selected only - means specific data should be stored, there are objects which came from online server which i dont want to store on local).
Please let me know any solution if possible.
Regards
Nisar Ahmed
I see two ways to achieve this:
Iterate through inserted and updated objects and revert those you do not wont to save. Inserted objects should be deleted, updated should be refreshed:
for (NSManagedObject* obj in [self.managedObjectContext insertedObjects]) {
if (/*Shouldn't be saved*/) {
[self.managedObjectContext deleteObject:obj];
}
}
for (NSManagedObject* obj in [self.managedObjectContext updatedObjects]) {
if (/*Shouldn't be saved*/) {
[self.managedObjectContext refreshObject:obj mergeChanges:NO];
}
}
Create separate managed object context. Recreate objects that you want to save in new context and then save it.
NSManagedObjectContext* newContext = [[NSManagedObjectContext alloc] init];
[newContext setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
for (NSManagedObject* obj in objectsWantToSave) {
NSEntityDescription* entity = [obj entity];
NSDictionary* valuesByKeys = [obj dictionaryWithValuesForKeys:[[entity attributesByName] allKeys]];
NSManagedObject* objCopy = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:newContext];
[objCopy setValuesForKeysWithDictionary:valuesByKeys];
}
[newContext save:NULL];
The second approach is better for my opinion.
Have a look into UIManagedDocument - http://developer.apple.com/library/ios/#documentation/uikit/reference/UIManagedDocument_Class/Reference/Reference.html
It takes care of a lot of the boilerplate involved in using core data.

Saving and Deleting NSManagedObject & NSManagedObjectContext

Three Questions but they are all related. If you like I can divide them into three questions so that you can more credits. Let me know if you'd like for me to do that.
I have the following code that allows me to access NSManagedObject
self.managedObjectContext = [(STAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext]];
NSArray *objectArray = [managedObjectContext executeFetchRequest:request error:&error];
if(objectArray.count==0){
letsMeet = (LetsMeet *) [NSEntityDescription insertNewObjectForEntityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext];
} else{
letsMeet = (LetsMeet *)[objectArray objectAtIndex:0];
}
The code above allows me to save and retrieve attributes. i.e. I can access letsMeet.attribute to save and fetch.
Question 1: How do I delete and start a brand new managedObjectContext. i.e. User has a form that he's been filling out between the scenes. Everything is saved to CoreData from each scene as the user hits the Next button on the navigation Controller. After going through several screens, the user wants to cancel the form. At this point I would like to delete everything that has been saved thus far. Code example please.
Question 2: Lets say the user gets towards to end of the form and decides to save the form for later retrieval. How do I save a copy of the entire form as one object in Core Data. Code example please.
Question 3: How do I retrieve that saved object from Core Data at a later time and display what all the user had saved? Code example please.
To delete you just need to delete letsMeet object from NSManagedObjectContext.
NSError *error;
[managedObjectContext deleteObject:letsMeet];
[managedObjectContext save:&error];
Since you always have only one object, getting the reference of letsMeet is not a problem. You can do as you did in your code.
Update:
And you don't need to delete the managed object context. It just a space to deal with your objects. More explanation at the end of question.
2. If the LetsMeet entity is modeled in a way that all the form elements are attributes of LetsMeet, when you save the managedObjectContext after creating a LetsMeet object as you have done in code, this will be saved as a single object.
3.You already know how to retrieve an object as thats what you are doing in the code. Everything becomes easy as you are only using one object.
In the case of multiple objects to get a the unique object, you should either implement a primary key,(maybe formID, i.e; add another attribute to LetsMeet) or you should know what the objectId of each object is and then set the predicate of your fetch request accordingly.
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:letsMeet];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"formId like %#", formId];
[request setPredicate:predicate];
NSArray *resultsArray =[managedObjectContext executeFetchRequest:request error:&error];
If your formId is unique, this will return you a single object array.
But if you are using core-data for only handling one object, you could've used NSUserDefaults or write to a plist File to do this. This is kind of overkill.
Update:
To get the objectId of a NSManagedObject:
[letsMeet objectId];
ManagedObjectContext is like a whiteboard. The object you have inside the array, the object inside managed object context, its all the same. You can change the objects, add object, delete object etc. Only thing is whatever is the current state of the object(s) when you do a [managedObjectContext save:] , that is written to disk.

Resources