I am trying to make an application where a user can edit attributes of a managedObject in a view, and select either Done to persist the changes, or Cancel to undo the changes.
To implement this behavior, I plan to do the following-
When the view controller is loaded to edit the managedObject, create a backupManagedObject which is a clone of the original managedObject.
This backupManagedObject is created in a separate child ManagedObjectContext.
Let the user edit the original managedObject.
If:
Done is pressed, the original managedObject is persisted, and backup is deleted
Cancel is pressed, the backupManagedObject is cloned into original managedObject and the backup is deleted
My question is, once I am done with the backupManagedObject, how can I delete the childManagedObjectContext which will have no more managed objects and I don't plan to use them anymore (for every new view controller, I plan to just create a new child managed object context and destroy it once view controller is done).
You should do this the other way around:
When you load your editing View Controller, create new Managed Object Context that is the child of your main one, let's call it the editingMOC.
Do the edits to the editingMOC, and if you want to persist them, save the editingMOC (this will propagate changes to the mainMOC), and then save the mainMOC to save the data to the persistent store.
If you wish to discard the changes done to the editingMOC, simply do not save them and let the context get dealloc'd.
In general, a managed object context is release the same way any other object in Objective-C is released and deallocated.
If you are using ARC, simply set the property to nil when you no longer need it and it will be destroyed along with any unsaved changes.
However, your approach for this problem is a bit complicated.
You could simply create a new "editing" child context, fetch the objects you like to edit in that context and make changes to the objects.
If the user decide to commit the changes, save the context (up to the store), and if not, simply destroy the context.
An easier way is to simply create the view and populate the UI controls (text fields, etc.) using data from the NSManagedObject attributes. If the user makes any edits then set a flag so you know if changes are made and then when they select Done update the NSManagedObject attributes using values from the UI controls and save the MOC. If they select Cancel then don't do anything.
See this link for a video showing an app using a similar approach for editing Core Data records on iOS. The OSX app uses standard NSPersistentDocument undo manager.
http://ossh.com.au/design-and-technology/software-development/uimanageddocument-icloud-integration/os-x-and-ios-app-integration-with-core-data-and-icloud/
Related
I have a scenario where an Entity has many relationships with other entities. I did some changes in the NSManagedObject of the entity and discarded those changes.
Right now I'm calling managedObjectContext.refresh(entity, mergeChanges: false) and then managedObjectContext.refresh(relatedEntity, mergeChanges: false) on every related entity to ensure having no dangling objects in the context.
What would be the difference if I directly call managedObjectContext.reset()? Should I still need to refresh or mark nil the related entities?
Is there any way to make this code more optimized?
If you call reset, you need to also immediately stop using every managed object you have fetched from that context. All fetches need to be redone after the reset, because it makes the context forget about everything it has already fetched.
There are various patterns for more efficiently creating discardable changes like you describe. One popular choice is:
Create a new managed object context and make it a child context of the current context.
Make your changes in that context.
If you want to save changes, save them. If not, just don't bother. The child context will be deallocated and its changes will be lost.
I have the following core data model:
A -> B (1 to 1)
A -> C (1 to n)
I created a view controller VC to edit any properties of an instance of A. In that VC I would like to add the possibility to create new instances of C so the user can add new Cs on the fly if necessary. Right now I am doing this by just adding these to the viewContext and saving it. That works but has a little hick up. Once I saved the new C to the context I cannot rollback any changes that have been made to A previously.
So I studied this tutorial and found that any changes that I consider separate should be applied in its own child context. I understand that but I still have one question: The tutorial says that any changes made to a childContext are pushed to its parent on save, but it will never be written to disk unless I save the parentContext. Now if I do not want to save the parent context because the user hit the cancel button to rollback other changes, how can I still keep the new Cs?
Don't save data directly to the ManagedObject (CoreData object) for either of the entities A, B or C, rather you should create a model which you should update according to the user input or any data source. You need to create managed objects for A, B or C and set value for respective attributes whenever user confirms to save data. That means if user hits cancel, data won't be saved to CoreData but you will be able to find the previous data in the model throught out the model object lifetime.
I am aware that it is a weird approach but I have to create and save a 'parent' entity before I can add children. The problem I am having now is that if the user decides to stop creating the new children the parent is still created.
So my question is: Is it possible to undo the last save in Breeze? Another option is deleting the saved items based on their IDs from the result.entities that's generated after the save but that would be very cumbersome.
(If its any help, I'm using AngularJS)
I am also interested in understating how iOS 5+ parent contexts work. I am using RestKit 0.2x but I think these are general Core Data questions.
Let's say we have this store coordinator/contexts hierarchy:
Persistent Store Coordinator (PSC) -> PS Managed Object Context (PS-MOC) -> Main Queue MOC (MQ-MOC).
For a request RestKit creates a temporary Private Context (P-MOC) from MQ-MOC and on success by default saves the changes back all the way to the PSC:
PSC -> PS-MOC -> MQ-MOC [-> P-MOC]
I also manually create managed objects and modify properties directly on the MQ-MOC.
My questions:
Are my unsaved MQ-MOC changes passed to child P-MOQ when created?
Should I save my MQ-MOC changes before a child P-MOQ gets created? Should I save these changes all the way to the PSC?
If P-MOQ is configured to save only to its parent MQ-MOC context, the newly introduced changes look like my MQ-MOC unsaved changes?
Are my unsaved MQ-MOC changes passed to (an existing) child P-MOQ?
Unsaved, never. Saved, no, because they aren't required there so RestKit doesn't check and merge the changes.
How about unsaved changes when the child P-MOQ is created?
Anything saved in the parent when the child is created will be available in the child.
Should I save my MQ-MOC changes before a child P-MOQ gets created?
If the child needs them, yes (though in theory RestKit should detect that an object you use in a request isn't saved and save it for you, best not to rely on that).
Should I save these changes all the way to the PSC?
Yes. Having things saved but not persisted will just lead to issues in the future.
If P-MOQ is configured to save only to its parent MQ-MOC context, the newly introduced changes look like my MQ-MOC unsaved changes?
A child will push changes up to its direct parent but no further, so in effect, yes.
Just to check, you should be aware of the RestKit added method saveToPersistentStore:.
I have a program in which I'm using CoreData to manage both permanent and temporary data.
In one instance, I abort the addition of some data by deleting the object when the user presses cancel. But when I hit the next view, the data is still there, attached to it's parent core data object.
[self.bar removeFoosObject:self.foo];//Why do I need this line?
[self.foo.managedObjectContext deleteObject:self.foo];
I eventually solved this by manually removing the child object from it's parent -- but isn't that something core data handles automatically? Why should I need the first line?
I ran some test code, and Foo is definitely deleted -- the code that it was mucking up let me check, and it's MOC has been set to nil. The memory exists, but it should be very, very dead...
You have to manually do this because you have your deletion rules set wrong. Check the Relationship Delete Rules in the following apple documentation.
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html
Or it could also be that the relationship is NOT set as optional between the parent and child object.
And also after you delete the object you should save the database to synchronize it.