Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 09-19-2011, 06:31 PM   #1 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default Core Data and Undo-able Changes

I'm still relatively new to Core Data, so I'm plodding through a variety of scenarios that are causing stumbling blocks. I believe I have something figured out for this particular case, so this is mostly seeking confirmation of the approach.

Sample from SlickShopper:

1. List of Products
2. ---> tap to see Product detail. Products have a Category relationship.
3. -------> tap to see Category list

When the user goes to the Product view, I want everything that they do to the Product to be undo-able. And by this, I simply mean that they can hit cancel, and nothing is changed. However, I want anything they do to Categories - add, delete, etc - to be permanent.

Pre-Core Data solution:

Make a copy of the Product object, and keep a reference to the original. User goes on about their business making changes to the copy. When it is time to save the Product detail, I manually map the various properties back onto the original object. If the user cancels, I simply ditch the copy. The Categories view does it's own thing, saving as necessary. This works just fine.

NSCopying takes care of the first part, but the saving step is manual. So there are a couple opportunities for me to forget to include a new property, which means the saving may not catch everything if I'm not careful. But otherwise, downstream VCs are none the wiser. They get a Product object, and that's all they care about; they don't care if it is the original or the copy.

Core Data Solution #1:

I'm not proud of this one, but it works. I make a dummy Product object, with a flag that keeps it from appearing in any Product lists. This dummy Product takes the place of what I was doing with the copy before. So I manually map properties from the original to the dummy, the detail view is driven off the dummy, and then if they save I manually map back from the dummy to the original.

Same idea as the previous solution, but a smidge less elegant since NSCopying and NSManagedObjects don't seem to play too nicely.

Core Data Solution #2:

When you initWithEntity:insertIntoManagedObjectContext:, you can provide nil for the context parameter. This will keep it out of the context until it is time to save, at which point it must be inserted into the context. This might have been a decent alternative to my dummy object from the previous solution, with one key exception:

You cannot create relationships between items in different contexts.

So my brand new Product entity has a context of nil, the Category entity I'd like to link to has a context that is non-nil, thus they are different, and this will crash when making the attempt.

So this one mostly isn't a solution in this particular case, though there will be occasions where it is handy.

Core Data Solution #3

Pretty much everything you read about Core Data describes the MOC as a "scratch pad". Do whatever you want. If you want to keep it, save the MOC. If you don't, ditch it. And although I'm not looking at a multi-threading case where a 2nd MOC would be required, it seems like a reasonable enough approach.

So the basic approach I'm doing is to create a 2nd MOC in the product detail view. Let's call it "scratchObjectContext". I pass in my original Product object to this view controller. I then need to get ahold of this object via my scratchObjectContext, like so:

Code:
[[self scratchObjectContext] existingObjectWithID:[product objectID] error:nil]
In order to keep everything straight, I've adopted a naming convention. Regular names (product) for stuff in the primary MOC, prefixed names (scratchProduct) for stuff in the secondary MOC. So far so good, in concept I can make any changes that I want, and the save the 2nd MOC if I want to keep them.

This works great until I want to mess with Categories. If the user adds 20 Categories, I want those all saved, even if the user does ultimately cancel the changes to the Product. So I need Category stuff to be on the main MOC, and I'll leave the Product stuff on the 2nd MOC. But I may need to create a relationship between the Product and one of the Categories. Ok, a little more annoying, but I'll just do the existingObjectWithID: whenever necessary.

The situation I found myself in was with a new Product, and while viewing that new Product, the user also created a new Category. For some reason, in this scenario, the new Product wound up not being saved. I finally narrowed it down to situations involving saving the main MOC. If the user created a Category, I saved. If they deleted or renamed one, I saved. And every time that happened, it was like the Product would disappear. I couldn't quite put my finger on when or where it was happening. But if I didn't save the primary MOC, there were other issues.

On a whim, I decided to apply what is used in the multi-threaded case. There, you save the 2nd MOC, and the process of doing so posts a notification. You listen for the notification, and when you get it, you merge the changes back in to the 1st MOC. So I decided to do this in reverse: listen for the notification of the 1st MOC being saved, and merge those changes into the 2nd one. And by golly that seems to be working. (I could be misinterpreting my results here, but nothing was working until I added this listener)

-----

So doing this same thing in Core Data has required me to add:
1. A 2nd MOC and then I must carry that object reference through to any downstream VCs
2. A notification listener
3. Numerous spots where I must reacquire references through the 2nd MOC
4. A goofy naming convention to help keep straight which objects belong to which MOC
5. Headache and stress while I figured all of this out, and I'm not certain I've actually solved it yet

Whenever I have to work this hard, I start to get suspicious that I'm doing something dumb, and fighting the framework. But I so far have not been able to identify exactly where I'm being stupid. So have I indeed landed on the correct solution and it's just harder than I think it should be, or am I missing an easier alternative?
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Reply

Bookmarks

Tags
core data, nsmanagedobjectcontext

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 401
16 members and 385 guests
7twenty7, eski, EvilElf, fiftysixty, HemiMG, iOS.Lover, jarv, n00b, pbart, Pudding, sacha1996, Sami Gh, teebee74, UMAD, VinceYuan, yuncarl28
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,672
Threads: 94,121
Posts: 402,905
Top Poster: BrianSlick (7,990)
Welcome to our newest member, yuncarl28
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 05:14 AM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0