Update NSManagedObject into Core Data - ios

So I have a tabbed application. The first tab allows a user to enter information in ~20 fields that describe a NSManagedObject. They are then able to save this into core data, and that works just fine.
The second tab is a TableView of all of the existing submissions. Now when a user clicks on a cell in the TableView, it will open up the first tab and repopulate all of the fields that were originally saved into core data. When the user clicks save again, I want the existing submission in core data to be updated, instead of a new insertion into core data.
I have found a lot of information saying that I should make a fetch request and then update it like that. But that seems redundant to me because I already have the object that was saved passed to the first tab/ViewController.
If you could point me to some code that would help my situation or describe a way you might accomplish this scenario, I would greatly appreciate it!

Since you have a reference to the NSManagedObject in the first tab, you can update its properties to the new values when the user saves. You can then save the changes to your NSManagedObject (let's call it myObject for simplicity) by calling [[myObject managedObjectContext] save:&error] where error is an NSError *.

Related

Creating a copy of a PFObject

I am in a situation where I allow the user to download a PFObject and modify it locally, and they can then either cancel the changes or hit Done, which will dismiss the editing interface but NOT upload the changes to Parse yet. They need to hit Save on the previous screen to write all changes to the database at once.
The problem is once the PFObject is modified, you cannot revert it to its prior state without refetching from the database. But I cannot always refetch the data from the database every time they hit Cancel because the prior state may not be uploaded to Parse yet (and that's a bad UX making them wait to discard changes that are only stored locally).
For example, imagine the user taps to edit the PFObject, they make changes then hit Done, then tap on it again and further edit the object, then hit Cancel. In this case, the object needs to be reverted to its prior state, but that state has not been uploaded to Parse yet. So I cannot refetch the data from the database to revert changes otherwise it would overwrite the changes they made the first time.
To solve this problem, I would simply fetch the PFObject and store a copy of it. I'd call that the transient object. I would have another property that stores the real object. The user would modify the transient object, and when they hit Cancel I would simply set that to nil, if they instead hit Done I would set the real object equal to the transient object, and once they finally hit Save I would save the real object to the database. That way I can be sure changes aren't being made to the real object until the user commits the changes. The problem is, PFObject does not adopt the NSCopying protocol (not sure why), therefore I cannot create a copy of the PFObject. Any change I make to it affects the real object.
How can this be resolved, without modifying the app's design that allows control over when the data is committed and later saved? Is there a way to extend PFObject and adopt NSCopying, has it been done before?
I did consider storing the attributes of the object in a dictionary and allow the user to edit that instead, then upon commit set each of those attributes on the PFObject. The problem with this solution arises with complex structures. In this app, I allow the user to modify multiple arrays that contain multiple PFObjects. It's just infeasible to try to recreate and later merge changes with complex structures like this beyond a single simple PFObject.
I ran into this same problem. I did not make any changes directly to the PFObject, but rather, saved the updates in an NSDictionary. When the user clicks the done button, I then update the PFObject and saveInBackground. I don't think there is a "discard local changes" option for PFObject. If you don't do this, the only option is to throw out the existing PFObject and fetch again.
Regarding the NSDictionary comment, perhaps NSArray would be better. The implementation really depends on your specific program, but I'll give a quick example. The NSArray we'll call instructionArray. Imagine there are 3 sections in a tableView. Also assume that the data source for each section is an NSArray of PFObjects. Now say you want to set the age property of each PFObject in Section 2 to 35.
Add an NSArray object (corresponding to an instruction to carry out) to the instructionArray. This instruction to carry out could have the form
Section to update
Property to update
Value to update to
So the object you'll add is #[#(2),#"age",#(35)];
Given that the user is probably carrying out a finite amount of instructions, it might not be that performance heavy to loop through the instructionArray in cellForRowAtIndexPath so when a cell uses its corresponding PFObject to figure out what to display, it can loop through the instructions after and change what is displayed as if the PFObject was updated.
When the save button is touched, loop through the instructions and actually edit the PFObjects themselves.
If you need the instructions to handle specific objects rather than sections, then you just have to update the structure of the instructionArray. Maybe you could include an identifier to indicate what type of instruction it is.

save entity into core data with wizard

i need to save data in multiple step like a wizard e.g name, family and ... in firstviewcontroller Next adress in secendviewcontroller next image in thirdviewcontroller next ... and in lastviecontroller if user click on finish whole entity save into core data, so i need something to hold temporary data before last step of wizard. i think i need a Separat class to hold temporary data but i don't know how can i implement that.
I am new in ios and i need a Full description.
help me please
No need for separate class. Just pass your partially populated managed object on to the next controller. You can even do a partial save in between with
[context save:nil];
(will work across restarts). To cancel, just delete the object with
[context deleteObject:object];

Magical Record - losing my Entity information

I have an app that had a TableView with customer information.
If a click on a cell I load the detail of this customer.
I'm using Magical Record to manage all my entities.
What I'm doing:
1) Load all the CUSTOMER entity on the viewDidLoad of my TableView.
2) User click to see the detail of the customer
3) Pass to the ViewController of the detail the object that represent the entity CUSTOMER (only one).
Everything works well.
The problem is, if the user select to reload the TableView I do this:
1) Go to the API to get all the customers
2) [Customer truncateAll]
3) Create all the entities back on CoreData
5) [[NSManagedObjectContext defaultContext] saveToPersistentStoreAndWait]
6) Reload tableview
BUT, if the user click to see the detail of the Customer during this process, the following ViewController show the customer information correctly for a while. Then after
[Customer truncateAll] occurs all the information dies.
How can I prevent this?
Many thanks!
You can make use of NSFetchedResultController to listen for your Customer entity changes. When your API call finishes and you create proper entities somewhere else in your app, you'll be notified in the controller about those changes and then you can reload the data. If you're not familiar with this concept look here.

CoreData Persistent Store Seemingly Empty

First I'll give a bit of background on what I'm working on. I'm creating a CRUD iOS application that gets information from an API we've created. Once I have pulled down the data I store it with CoreData. I then use an NSFetchedResultsController to display this data in a table view. Problem is that when I show a modal view on top of the table view and then dismiss the modal view, the table is cleared and everything short of re-downloading the data and inserting it again doesn't work.
The problem arises though when I'm pulling fresh data from the API. Using trial and error I have narrowed down the problem to where I delete all the old data in the persistent store (I have two stores, one for user created data and one for data from other users). This code snippet is from the API call where I handle fetching data:
[self deleteAllEntitiesNamed:#"Object"];
[self parseResponseObjectIntoManagedObjects:[response results] inStoreWithURL:storeURL];
success(response);
The [self deleteAllEntitiesNamed:#"Object"]; call goes into the store for other users and deletes all Object entities with the following code:
NSArray *result = [self performFetchRequestForEntityNamed:entity inStoreWithURL:url];
for(id r in result)
{
[self.managedObjectContext deleteObject:r];
}
I have traced the problem down to this function call. If I comment out the delete call in the API handler, there is no problem maintaining the table between views, though it has the bad side effect of duplicating a lot of the data already in the store. I feel I may be missing something about deleting items in the store, though it makes no sense to me as I re-insert the data immediately after deleting the old store.
I have tried deleting and re-initializing the entire store, but then no data loads at all.
Thanks ahead of time for any insight available.
EDIT: I should also add that when I have tried to pull out objects from the store after moving back to the table view, I get an empty result using the same fetch request that succeeds at getting Objects out of the store immediately after I finish the API call in my success callback.

Editing/Adding a Core Data entity with the same view?

First, let me explain what I'm trying to accomplish. I've got a master-detail application with a MasterViewController and a EditViewController. The MasterViewController contains an Add button and a table listing Core Data entities. When the user taps a table row or the Add button, the Edit view should pop up. I'm confused about how I should handle editing and adding differently.
Here's how I'm currently doing it: my app uses Storyboards, so I have editEntity and addEntity segues from Master to Edit. Both segues pass an entity to the EditViewController, but editEntity finds an existing entity based on the row tapped whereas addEntity creates a new one. Both segues set the isNew transient property on the entity.
The EditViewController doesn't know anything about Core Data--it simply edits the entity it's given. It in turn has done and cancel unwind actions. MasterViewController looks at the isNew property when considering cancel--if the entity is new, it deletes it, and if it already exists, it simply doesn't apply the changes.
This works, but it has a couple problems. Firstly, it seems a tad messy to add extra properties to the entity. Secondly, if the user closes the app on the Edit view while editing a new entity, that entity won't be deleted, which is certainly unexpected. Most of all, this seems like a problem that Core Data itself must have a solution to--I just don't know how. Thanks a bunch!
The simplest improvement would be to replace the isNew flag on the entity description with a flag on your edit view controller. The edit VC might not know anything about Core Data, but it's OK to let it know if the object it's editing is new or pre-existing. Set the flag there, and have the master VC check the value before deciding how to proceed. Don't put this in your entity description, it's not data you need to keep around.
What I've done in this situation is create the new instance but don't insert it in the managed object context yet. Something like
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Entity" inManagedObjectContext:[self managedObjectContext]];
NSManagedObject *myObj = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
Passing nil for the second argument when creating the instance gives you an instance that hasn't been inserted yet. Pass that to the edit view controller.
If the user taps on the save button, you can insert it later with something like:
if ([myObj managedObjectContext] == nil) {
[[self managedObjectContext] insertObject:myObj];
}
Since the object hasn't been inserted, it has no managed object context, so checking that property tells you whether to insert it. Don't use the isInserted property here, it won't do what you need. Save changes in either case.
If the user taps "cancel", just don't insert it. The object gets deallocated just like any other object, and never makes it to the persistent store. Since you never inserted it, you don't need to bother deleting it.

Resources