undo all changes made in the child view controller - iphone

There are two entities: Author and Book. Author has an attribute authorName and a to-many relationships books. Book has several attributes and a relationship author. There is a view controller (VCAuthor) to edit an Author object. The child view controller(VCBook) is to edit books of the author. There is only one managedobjectcontext. In the VCBook class i group the undomanager as following
-(void)viewDidLoad
{
NSUndoManager *anUndoManager = [[NSUndoManager alloc] init];
[self.book.managedObjectContext setUndoManager:anUndoManager];
[anUndoManager release];
[self.book.managedObjectContext.undoManager beginUndoGrouping];
}
-(void)cancelAction:(id)sender
{
NSLog(#"%#", self.author.authorName);
[self.book.managedObjectContext.undoManager endUndoGrouping];
[self.book.managedObjectContext.undoManager undoNestedGroup];
self.book.managedObjectContext.undoManager = nil;
NSLog(#"%#", self.author.authorName);
[self dismissModalViewControllerAnimated:YES];
}
the cancelAction is linked to an cancel button on the VCBook which used to undo all the changes made in the VCBook.
Problems is here: First, in the VCAuthor, I edit the authorName in an UITextfiled authorNameTextField from Obama to Big Obama, and save it to the MOC by author.authorName = authorNameTextField.text in the - (void)viewWillDisappear:(BOOL)animated{} method. Then I went into the child view controller VCBook to edit books of the author and click the cancel button to get back to the VCAuthor. I find the authorName still be Obama, that means the expected change of the authorName has been undo. The change of the authorName is not in the undo group at all, and why could this happen? ps. of course i reloadData when i get back into VCAuthor.
I just NSLog the authorName before and after the undo. Before undo the authorName is the changed one Big Obama, and after undo it become Obama

Several things to consider. First, in a scenario like this, I would use a separate MOC instead of the undo manager. Namely, I'd do something like this (assuming ARC - you can do the mapping if necessary)...
You must have some other code providing the book to the VC through a setter, since you access it in viewDidLoad. I'd change viewDidLoad to something like this...
-(void)viewDidLoad
{
self.managedObjectContext = [[NSManagedObjectContext alloc] init];
self.managedObjectContext.parentContext = self.book.managedObjectContext;
// We are in the main thread, so we can safely access the main MOC
// Basically, move our object pointer to book into our local MOC.
NSError * error = nil;
Book *book = [self.managedObjectContext existingObjectWithID:self.book.objectID error:&error];
// handle nil return and/or error
self.book = book;
// Now, your access to book will be in the local MOC, and any changes
// you make to the book or book.author will all be confined to the local MOC.
}
Now, all you have to do is call
[self.managedObjectContext save:&error];
in your saveAndDismiss action. If you don't call save, none of the changes will be saved, they will all just automatically disappear.
EDIT
Note, that the above "save" just moves the object state into the parent context. So, the "main" MOC now has the changes from the "child" but none of the changes have been saved to disk yet.

Related

Core Data returns Null when i save

here is my code,
hand is the entity, and addRounds is a textField
-(IBAction)save{
[self dismissViewControllerAnimated:YES completion:nil];
[self.hand setValue:self.addRounds.text forKey:#"rounds"];
NSError *error;
if (![self.managedObjectContext save:&error]) {
//Handle Error
}
NSLog(#"%#", self.addRounds.text);
NSLog(#"%#", self.hand.rounds);
}
the console out puts
2012-11-25 16:51:18.847 App[3187:c07] 1
2012-11-25 16:51:18.848 App[3187:c07] (null)
so, for some reason, its not saving properly. Could anyone please help me! Thank You!
-(IBAction)save{
if (self.managedObjectContext == nil)
{
self.managedObjectContext = [(RootAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
Hand *hand = [NSEntityDescription insertNewObjectForEntityForName:#"Hand" inManagedObjectContext:self.managedObjectContext];
[self dismissViewControllerAnimated:YES completion:nil];
hand.rounds = nil;
self.managedObjectContext = self.app.managedObjectContext;
NSError *error;
if (![self.managedObjectContext save:&error]) {
//Handle Error
}
}
Edit
So Basically There is a total of 4 Views.
1) view with Table view, user can press '+' button
2) This view allows the user to add a cell to the table view
3) allows the user to edit the table view cells
4) this is a completely different view that also allows the user to edit the table view cells.
I'm using the code at the top of the question to save in both views 3 & 4. It works perfectly in view 3 but not 4!!
UPDATE!!!
So, I recoded the app so that views 1 & 4 are the only two views in the app. When i push view 2 and view 3 in between view 1 and 4 it sets my managedObjectContext's rounds attribute to null.
Is the rounds attribute of your hand entity a string in your model?
First, you should set the type on your hand entity's rounds attribute to a number type. 16bit integer would probably suffice (we're talking about a card game, right?), but you could make it bigger if you like.
Next change your code to:
-(IBAction)save{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSNumber *rounds = [numberFormatter numberFromString:numberself.addRounds.text];
[self dismissViewControllerAnimated:YES completion:nil];
[self.hand setValue:rounds forKey:#"rounds"];
NSError *error;
if (![self.managedObjectContext save:&error]) {
//Handle Error
}
NSLog(#"%#", self.addRounds.text);
NSLog(#"%#", self.hand.rounds);
Also, I'm assuming you've inserted the instance of hand that you're setting properties on somewhere before this point. Maybe I shouldn't assume. Have you already initialized this managed object instance, self.hand, to point to data in the store?
For example, I like to use lazy instantiation:
- (Hand*)hand
{
if (_hand = nil) {
_hand = [NSEntityDescription insertNewObjectForEntityForName:#"Hand" inManagedObjectContext:self.managedObjectContext];
}
return _hand;
}
This assumes a purely -create oriented- design. In most cases, you'd want to edit an existing object and update its rounds count. For this, you should attempt to retrieve a Hand that you're editing first. You'd do that with an NSFetchRequest, and there a zillions of examples of that, so I won't repeat them here. If There were no matches, this getter would create one as a fall-back. Also, best practice is to create a category for Hand (maybe Hand+Edit.m) which contains methods for retrieving different Hands, creating them, and updating common properties.
I'd create worker methods inside the Hand object category like these:
+ (void)incrementRoundsOnHand:(Hand *)hand withManagedObjectContext:(NSManagedObjectContext *)context
+ (void)incrementRoundsBy:(NSUInteger)count onHand:(Hand *)hand withManagedObjectContext:(NSManagedObjectContext *)context
// or some sort of unique identifier, date, number, etc
+ (Hand *)handWithName:(NSString *)name withManagedObjectContext:(NSManagedObjectContext *)context
I'd then have handWithName: (or whatever) do an NSFetchRequest, and if nothing matches, create a new hand and return it. Either way, you get a hand back. Important is that you don't deal with manipulating the specifics of your Hand entity outside of the Hand managed object class. Note, since these are class methods, they can be called directly.

Cancel New CoreData Object?

In my app, I have my master view controller which displays all my coredata objects.
When the user adds an object this runs and the next detail view opens to enter details for the new object:
-(IBAction)addPerson:(id)sender
{
Person *p = (Person *)[NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:managedObjectContext];
PersonDetailViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"PersonDetail"];
vc.managedObjectContext = fetchedResultsController.managedObjectContext;
vc.person = p;
vc.isNewPerson = YES;
[self.navigationController pushViewController:vc animated:YES];
}
Now I have a delete button in the detail view which calls this:
[managedObjectContext deleteObject:person];
NSError *err;
if (![managedObjectContext save:&err])
{
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", err, [err userInfo]);
exit(-1); // Fail
}
This is how I currently cancel a new coredata object. However I am having issues with it.
How would you recommend I best cancel the creation of a new object? Create and delete, or never create it until confirmed? I'm unsure.
I'd actually never create the managed object until confirmed!
But in the first place I'd rewrite the PersonDetailViewController to not have a dependency to the Person entity at all (loose coupling). Instead I'd define properties in your PersonDetailViewController for the various attributes of the Person object you like to set/edit and then handle the creation of the Person managed object in a save delegate method or so. With this approach you could also cancel the creation use-case half through, without the need for creation of a new managed object etc.
For better illustration, the delegate method would look somewhat like this:
- (void) personDidSave: (PersonDetailViewController*) controller {
// Create a new Person object with the values from the controller
// and add persist it to core data
Person *p = (Person *)[NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:managedObjectContext];
p.name = controller.name;
p.surname= controller.surname;
// etc.
// save to core data
// Refresh the table or whatever view
}

NSManagedObjectContext doesn't refresh correctly

hi :) I have a similarly issue like in Working with the same NSManagedObjectContext in multiple tabs
background:
My managedObjectContext (further MOC) is initialised in my appDelegate class and passed throught to multiple tabs by
myViewController.managedObjectContext = self.managedObjectContext; or in the init method with self.managedObjectContext = pContext;
the flow is: the first view is a simple list of collections. The collections are fetched with a NSFetchedResultsController (myViewController : UITableViewController<NSFetchedResultsControllerDelegate>). By selecting one, you navigate deeper, but still passing this MOC.
In the next controller (detailsViewController) I list up some items of this collection what I can interact with (set switches for instance).
I also have an editingObjectContext:
// DetailsViewController.m
NSManagedObjectContext* editingContext = [[NSManagedObjectContext alloc] init];
[editingContext setPersistentStoreCoordinator:[managedObjectContext persistentStoreCoordinator]];
self.editingObjectContext = editingContext;
Now my issue: because my view has to rotate, I am using the folowing trick:
// DetailsViewController.m
DetailsView *localAct = [[DetailsView alloc] initWithManagedObjectContext:managedObjectContext ... ]
DetailsView *localSen = [[DetailsView alloc] initWithManagedObjectContext:managedObjectContext ... ]
UITableView *localContainerView = [[UITableView alloc] init];
self.containerView = localContainerView;
[localContainerView release];
//[...]
[containerView addSubview:actuatorView];
self.tableView = containerView;
further I have a button to manage this items (which of them shall be shown and which not). This button just reloads the table with a new fetchResult.
// DetailsView.m
- (void) manageItems{
managing = !managing;
[viewController setIsManaging:managing]; // parent
self.fetchedResultsController = nil;
NSError *error = nil;
[[self fetchedResultsController] performFetch:&error];
[self reloadData];
[self updateBarButton];
}
The method for putting the items into the context looks so:
// DetailsViewController.m
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// init + create predicate
NSSet* set = [sen filteredSetUsingPredicate:predicate];
if( [set count] > 0 )
{
for( Act* act in set )
{
[editingObjectContext deleteObject:act];
}
}
else
{
Act* act = [NSEntityDescription insertNewObjectForEntityForName:#"Act" inManagedObjectContext:editingObjectContext];
// do things
}
NSError *error = nil;
[[detailView fetchedResultsController] performFetch:&error];
[self.containerView reloadData];
[detailView reloadData];
}
but after I selected the items in the managed view and clicked save (manageItems), the view doesn't show them :/ i have to switch the tab or to navigate in an other controller (parent or deeper) to actualize it.
my ViewWillAppear method:
// DetailsViewController.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
DetailsView *detailView = se ? senView : actView;
// [do uninteresting stuff]
[detailView.fetchedResultsController performFetch:nil];
[self.tableView reloadData];
// [do uninteresting stuff]
}
and viewWillDisapper calls
- (void)saveChanges
{
if( ![editingObjectContext hasChanges] )
return;
// send save-command to server
}
In an earliert Verison where there was only 1 view it worked and I haven't changed realy much... :/ so I don't understand why the MOC is acting like it does. The "manageItems" part is nearly equal, its just a level deeper in the new version (in the DetailsView instead of the controller) ...
if someone can tell me what I can try (always saving to server when switch between managing and normal isn't a solution because the delay in the response from the server is to high for the refresh, so I have the less to flip the view. Also refreshing the views with self.tableView / detailView / self.containerView refresh brings the same result :/ ).
and a second issue: I can't call the "editingObjectContext save:" method after sending to server, because it's throwing errors and don't save at all to local database.
Error in handleChangeResponse:
Error Domain=NSCocoaErrorDomain Code=133020 "The operation couldn’t be completed. (Cocoa error 133020.)" UserInfo=0x4d8bb90 {conflictList=(
"NSMergeConflict (0x5a2fac0) for NSManagedObject (0x5a46a80) with objectID '0x5a46420 ' with oldVersion = 7 and newVersion = 8 and old object snapshot = {\n iconName = noicon;\n [...] ;\n} and new cached row = {\n iconName = noicon;\n [...] \n}"
)}
if you have questions or need some more code (i.e. of the older version) then just ask ;)
thanks in anticipation :)
It seems like I have the solution! Since IOS 5.0 there is a new method for NSManagedObjectContext :
[managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
Found on http://pauloliveira.net/tech/core-data-merging-conflicts
Setting this attribute to the top-level MOC (in my case in the appDelegate) and no-where else! clears my merging problems ;)
I found the reason why it doesn't worked... forget everything what I wrote above... the problem was in the fetchrequest - concretely: in the predicate... in the earlier versions I used
[NSComparisonPredicate predicateWithLeftExpression: ...]
in the actualy version I use
NSString * predicateFormat = [NSString stringWithFormat: ...];
NSPredicate* predicate = [NSPredicate predicateWithFormat:predicateFormat];
because I had to extend the number of options and also edited the request itself because it made problems in the predicate (comparing a complete object (of the MOC class, extracted from the database) with an entity didn't worked, so I managed the workaround in the DetailsViewController and haven't rolled back my updates in this place :/).
Never thought to waste so much time on this problem >.< but okay, as long as it's resolved :D
I will check if the second issue (with the saving problem) still exists. If not, I will update my post, otherwise this topic isn't closed :/
This may be due to manageobject context in use of object where u'r getting this. Remove all NSManagebobject at the time when you either log out or move back. say end using app. Seems like this...
[NSManagebobjectcontext setManagedObjectsDictionary:[NSMutableDictionary dictionary]];

I'm having issues inputing a name if applicable

I have a function here that upon completing a single round, if your score is higher than either a default score entry or a newly placed high score then it will swap its data with your data and push everything else down. removing the last entry from the list. currently this is just one exchange and for functions sake I'm going to hard code it and then refactor it later.
My main problem is that when I set up a text input view to capture the players name execution continues immediately without the players input and crashes the game. I commented out the line that sets the text because I have a default value in place just in case any attempt that I try to make fails. How can I get Execution to wait for a moment while input is taken? Would I have to set up a delegate method? If so I'm still a bit confused by delegates. I could set it up to work but I don't understand it, so I wouldn't be able to do any other special custom tasks with it. I've worked on it for a while and got no further...
-(void)saveData:(ScoreKeep *)stats{
NSMutableDictionary *swap = [[NSMutableDictionary alloc]init];//used for swaping entries
NSString *filePath = [self pathOfFile];
NSLog(#"Writing to %#", filePath);
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSLog(#"Loading previous dictionary to save...");
dataDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
if ([dataDictionary objectForKey:#"1"]) {
NSMutableDictionary *highScore = [dataDictionary objectForKey:#"1"];
if ([stats.score intValue] > [[highScore objectForKey:#"SCORE"] intValue]) {
NSLog(#"You Win! score: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
NSLog(#"Setting up name entry");
[self.view addSubview:stats.view]; //New view is added so that the player can input data(Assume it is complete);
//stats.nameTag = setName.nameTag;//This line is executed before the new view is dismissed causing an error to occur
[stats setupDictionary]; // It just goes down hill from here if the previous line is uncommented
[dataDictionary setObject:stats.sComponents forKey:#"1"];
}else {
NSLog(#"You Lose: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
}
NSLog(#"Got first place entry");
}else {
NSLog(#"Initilizing Score");
}
}else{
NSLog(#"Creating new dictionary to save...");
dataDictionary = [[NSMutableDictionary alloc]init];
}
[dataDictionary writeToFile:filePath atomically:YES];
}
Help would greatly be appreciated. If more information is needed I'd be happy to provide.
by the way ScoreKeep is an object that contains a dictionary and a function to create a dictionary such that it can set any values I need and package them into sComponents(the dictionary to be entered into the main savefile)
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#class omphalosUtility;
#pragma mark -
#pragma mark Saving data
#pragma mark -
static inline void poop(){
NSLog(#"POOP");
}
I'm going to try making a utility file that works independently of the app so that I Can update files and perform other universal operations such as saving when needed. Its a step in a direction that i'd like to take.
If i get it right, (The code is really nasty, man...) your problem is that you are trying to present a View Controller with the wrong way.
Correct me if i'm wrong, is ScoreKeep is a ViewController? if so, you have to name it properly. that's for a start.
Second, you cant present another view controller only by adding its "view" property to the current view controller's View Hierarchy. that way the view will not respond properly to the events.
the correct way to to what you'r trying to do is by presenting the ScoreKeep ViewController modally.
there is no other right way to do this without using delegation. you will have to acquire this technique.
Your view controller that responsible for getting the name from the user need to have a way to tell it's master view controller that the user entered a name. and that is achieved through delegation.
What you should do:
Basically you create a protocol called something like "NamePrompterViewControllerDelegate"
that will have at least one method that will be called when the user will done entering his name.
Your ScoreKeepViewController should have an instance variable that implemented the protocol (Look at the apple documentation on protocols for assistance)
Your main view controller (the one that contains the method you added) then should implement the protocol you created, and set itself as the delegate of ScoreKeep like that:
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
stats.delegate = self;
For more info on presenting and dismissing ViewControllers modally you should read the documentation at Apple Documentation
I hope i helped you, there is just a lot to cover and it hardly can be done by writing an answer.
Feel free to ask more for clearance.

NSInvalidArgumentException: Illegal attempt to establish a relationship between objects in different contexts

I have an app based on the CoreDataBooks example that uses an addingManagedObjectContext to add an Ingredient to a Cocktail in order to undo the entire add. The CocktailsDetailViewController in turn calls a BrandPickerViewController to (optionally) set a brand name for a given ingredient. Cocktail, Ingredient and Brand are all NSManagedObjects. Cocktail requires at least one Ingredient (baseLiquor) to be set, so I create it when the Cocktail is created.
If I add the Cocktail in CocktailsAddViewController : CocktailsDetailViewController (merging into the Cocktail managed object context on save) without setting baseLiquor.brand, then it works to set the Brand from a picker (also stored in the Cocktails managed context) later from the CocktailsDetailViewController.
However, if I try to set baseLiquor.brand in CocktailsAddViewController, I get:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:
'Illegal attempt to establish a
relationship 'brand' between objects
in different contexts'
From this question I understand that the issue is that Brand is stored in the app's managedObjectContext and the newly added Ingredient and Cocktail are stored in addingManagedObjectContext, and that passing the ObjectID instead would avoid the crash.
What I don't get is how to implement the picker generically so that all of the Ingredients (baseLiquor, mixer, garnish, etc.) can be set during the add, as well as one-by-one from the CocktailsDetailViewController after the Cocktail has been created. In other words, following the CoreDataBooks example, where and when would the ObjectID be turned into the NSManagedObject from the parent MOC in both add and edit cases? -IPD
UPDATE - Here's the add method:
- (IBAction)addCocktail:(id)sender {
CocktailsAddViewController *addViewController = [[CocktailsAddViewController alloc] init];
addViewController.title = #"Add Cocktail";
addViewController.delegate = self;
// Create a new managed object context for the new book -- set its persistent store coordinator to the same as that from the fetched results controller's context.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addingManagedObjectContext = addingContext;
[addingContext release];
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
Cocktail *newCocktail = (Cocktail *)[NSEntityDescription insertNewObjectForEntityForName:#"Cocktail" inManagedObjectContext:self.addingManagedObjectContext];
newCocktail.baseLiquor = (Ingredient *)[NSEntityDescription insertNewObjectForEntityForName:#"Ingredient" inManagedObjectContext:self.addingManagedObjectContext];
newCocktail.mixer = (Ingredient *)[NSEntityDescription insertNewObjectForEntityForName:#"Ingredient" inManagedObjectContext:self.addingManagedObjectContext];
newCocktail.volume = [NSNumber numberWithInt:0];
addViewController.cocktail = newCocktail;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addViewController];
[self.navigationController presentModalViewController:navController animated:YES];
[addViewController release];
[navController release];
}
and here's the site of the crash in the Brand picker (this NSFetchedResultsController is backed by the app delegate's managed object context:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
if ([delegate respondsToSelector:#selector(pickerViewController:didFinishWithBrand:forKeyPath:)])
{
[delegate pickerViewController:self
didFinishWithBrand:(Brand *)[fetchedResultsController objectAtIndexPath:indexPath]
forKeyPath:keyPath]; // 'keyPath' is #"baseLiquor.brand" in the crash
}
}
and finally the delegate implementation:
- (void)pickerViewController:(IngredientsPickerViewController *)pickerViewController
didFinishWithBrand:(Brand *)baseEntity
forKeyPath:(NSString *)keyPath
{
// set entity
[cocktail setValue:ingredient forKeyPath:keyPath];
// Save the changes.
NSError *error;
if (![cocktail.managedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
// dismiss picker
[self.navigationController popViewControllerAnimated:YES]
}
EVEN MORE
I'm making progess based on Marcus' suggestions -- I mapped the addingManagedObjectContexts to the parent managedObjectContext and wrapped everything in begin/endUndoGrouping to handle cancel vs. save.
However, the object to be created is in an NSFetchedResultsController, so when the user hits the "+" button to add the Cocktail, the (possibly-to-be-undone) entity briefly appears in the table view as the modal add view controller is presented. The MDN example is Mac-based so it doesn't touch on this UI behavior. What can I do to avoid this?
Sounds like you are creating two different Core Data stacks (NSManagedObjectContext, NSManagedObjectModel, and NSPersistentStoreCoordinator). What you want to do from the example is just create two NSManagedObjectContext instances pointing at the same NSPersistentStoreCoordinator. That will resolve this issue.
Think of the NSManagedObjectContext as a scratch pad. You can have as many as you want and if you throw it away before saving it, the data contained within it is gone. But they all save to the same place.
update
The CoreDataBooks is unfortunately a really terrible example. However, for your issue, I would suggest removing the creation of the additional context and see if the error occurs. Based on the code you posted and I assume the code you copied directly from Apple's example, the double context, while virtually useless, should work fine. Therefore I suspect there is something else at play.
Try using a single context and see if the issue persists. You may have some other interesting but subtle error that is giving you this error; perhaps a overrelease somewhere or something along those lines. But the first step is to remove the double context and see what happens.
update 2
If it is crashing even with a single MOC then your issue has nothing to do with the contexts. What is the error you are getting with a single MOC? When we solve that, then we will solve your entire issue.
As for a better solution, use NSUndoManager instead. That is what it is designed for. Apple REALLY should not be recommending multiple MOCs in their example.
I answered a question on here recently about using the NSUndoManager with Core Data but you can also look at some of my articles on the MDN for an example.
Yeah, you definitely don't want to cross context boundaries when setting relationships between objects; they both need to be in the same NSManagedObjectContext. In the old EOF, there were APIs for faulting objects into different contexts, but doesn't look like CoreData has an equivalent.