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

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.

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.

undo all changes made in the child view controller

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.

How to get NSString variable value from NSObject to ViewController

I am trying to set up an object to control all of my data so it can set things up in the background to it appears my tableviews load faster than they do now etc.
This is what I am trying to achieve.
I am setting a variable in the NSObject from the secondVC when the tableviewcell is selected like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Access selected cells content (cell.textLabel.text)
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//Parent view logic (sends info back to the correct cell in parent view)
if (parentViewSelectedIndexPath.section == 0)
{
if (parentViewSelectedIndexPath.row == 0)
{
//Predicates restrict the values that will be returned from the query
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K like %#",#"MANUFACTURER",cell.textLabel.text];
NSArray *filterArray = [myDataArray filteredArrayUsingPredicate:predicate];
//[[self delegate] setManufactureSearchFields:filterArray withIndexPath:indexPath]; //This is where I pass the value back to the mainview
//Using Object
VehicleControllerNSObject *vehicleControllerNSObject = [[VehicleControllerNSObject alloc] init];
[vehicleControllerNSObject setFirstCell:filterArray];
}
//etc
At the end there you can see the method that is getting set up in the VechicleControllerNSObject which looks like this.
-(void)setFirstCell:(NSArray *)array{
manufactureSearchObjectStringFVC = [[array valueForKey:#"MANUFACTURER"] objectAtIndex:0];
NSLog(#"%#", manufactureSearchObjectStringFVC); // this prints the correct value to the console
}
As you can see this prints the correct output fine.
however I have no idea how to call manufactureSearchObjectStringFVC and pass the value it holds into the uitableviewcell that I would like to pass it in on my firstviewcontroller.
This is what I have for testing atm.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
VehicleControllerNSObject *vehicleControllerNSObject = [[VehicleControllerNSObject alloc] init];
manufactureSearchObjectString = vehicleControllerNSObject.manufactureSearchObjectStringFVC;
NSLog(#"%#", vehicleControllerNSObject.manufactureSearchObjectStringFVC);
}
That nslog prints null..
I have three questions
1, how do I get the correct value into the first valuecontroller.
2, should I be using viewDidAppear like this?.. I think not.. how can I do this better
3, Do you think this is a good way of doing this type of thing, as in the future i would like to use the NSObjectClass to parse info, cache etc all behind the senses leaving the views to just display when the data is ready hopefully helping performance..
Any help would be hugely appreciated as I really want to learn this stuff as i know its important for me to know.
Your question is so beautifully and clearly formatted and diagrammed that it seems a shame to ask you to do a search. But here it is:
Search for Sharing Data between View Controllers
You'll find many good discussions about sharing data between view controllers.
Briefly, though, I can tell you why your code isn't working. In your tableView:didSelectRowAtIndexPath: method, you are creating (alloc/init) a new instance of your VehicleControllerNSObject class each time. Then back in your first view controller on viewDidAppear:, again you are creating (alloc/init) a whole new instance each time.
So you have multiple objects coming and going and they have nothing to do with each other. It's a bit like giving some important information to one person at a bus station and then later randomly picking some other person out and trying to retrieve that same information from her.
So one quick idea would be to create just once instance of your VehicleControllerNSObject (just an aside, that's a bit of a strange name for a class since generally all objective-c objects are descendants of NSObject anyway. I'm just going to call that VehicleController for now)
So let's say you wanted a 'sharedInstance' of VehicleController. You could add a class method to VehicleController to give you a way to easily get that one sharedInstance:
+(VehicleController*)sharedInstance {
static VehicleController *sharedInstance_ = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance_ = [[VehicleController alloc] init];
});
return sharedInstance_;
}
So to get that instance in methods in other classes you can just do something like :
VehicleController *sharedController = [VehicleController sharedInstance];
sharedController.someProperty = someValue;
// and then back in your first view controller, similarly:
VehicleController *sharedController = [VehicleController sharedInstance];
id someValue = sharedController.someProperty;
Again, check the search, many people have had good discussions on this. This is just one approach. I hope it at least makes sense why your code wasn't working.
Hope that helps.
To answer question 3. No.
I think that the best way to do something like this would be to use Core Data and it's NSManagedObject.
A combination of UITableViewController and NSFetchedResultsController that is feed from a Core Data sqlite backing store, if well set would feed and keep your UITableView updated.
It would be to long to describe all in here. So I will stop there.
If you don't want to go with that there is always the possibility to use a shared pointers to a mutable object or to use a singleton object to communicate information between UIViewController.

Can't set member variable twice (NSManagedObject)

I am getting the EXC_BAD_ACCESS error when trying to set a value inside a subclass of NSManagedObject for the second time.
I am using zombies but nothing is showing up in the console. Printing out the object using GDB I see that the object has the same memory address both times I try to set the value - not sure why though.
Situation:
I have a view (A) that, when a QR code is scanned, adds a subview (B) which in turn downloads XML that is then saved into a subclassed NSManagedObject.
Inside the subview (B) I navigate back (removeFromSuperView is called)
Back in the original view (A)
Next time, when the same QR code is scanned, it (A) finds the NSManagedObject from the database and attaches that to an instance variable on a new view (same type as B) that it then adds as a subview to the original (A).
In view B's viewDidLoad i always try to set the current date in order to track when a user "saw" that object. This is where I get the EXC_BAD_ACCESS error:
self.currentPiece.piece_last_viewed = [[NSNumber alloc] initWithDouble:[[NSDate date] timeIntervalSince1970]];
Where self.currentPiece is the instance of a subclassed NSManagedObject that was attached in A when that object existed in the database.
I know that it is being released somewhere but I don't know where since managed objects take care of much of that on their own. The error only occurs the second time around that I try to set the value.
I have tried to make this clear. Please tell me if you want me to clarify it even more.
Thanks for the help (have worked on this for some hours now)
UPDATE:
Declaring the piece_last_viewed in HubPiece.h:
#interface HubPiece : NSManagedObject {
}
// ...
#property (nonatomic, retain) NSNumber *piece_last_viewed;
HubPiece.m:
#dynamic piece_last_viewed;
//...inside init method:
self.piece_last_viewed = [[NSNumber alloc] initWithDouble:[[NSDate date] timeIntervalSince1970]];
UPDATE 2:
It is not due to the switching of subviews, that is ruled out. I then realized that I didn't save my changes either, so I introduced save: inside the subclassed NSManagedObject. I then got an earlier error the first time I try to save the entity instance (which saved during an app session, but the data vanishes if I quit the app entirely and then open it up again). So I thought using [context save:&error] would be a good idea :) ...but now that doesn't work and give me a EXC_BAD_ACCESS error.
The HubPiece itself is initialized from another class HubPieceView.m :
self.currentPiece = [[HubPiece alloc] initWithXML:pieceXML];
self.currentPiece is a class variable of type HubPiece and it first declared in .h file and then synthesized in .m file.
Then inside HubPiece.m the initializer looks like this:
-(id)initWithXML:(TBXMLElement *)pieceXML
{
// Setup the environment for dealing with Core Data and managed objects
HenryHubAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityHubPiece = [NSEntityDescription entityForName:#"HubPiece"
inManagedObjectContext:context];
// STORING values
self = [[HubPiece alloc] initWithEntity:entityHubPiece insertIntoManagedObjectContext:context];
// ...setting variables with normal assignment: self.var = value;
NSError *error;
// Save fails
if (![context save:&error] ){
NSLog(#" ERROR: %#", [error localizedDescription]);
}
return self;
}
I just realized my problem. I had been assigning values to the entity through with normal '=' assignment:
self.currentPiece.piece_last_viewed = [[NSNumber alloc] initWithDouble:[[NSDate date] timeIntervalSince1970]];
When it should have been done:
[self setCurrentPiece.piece_last_viewed:[[NSNumber alloc] initWithDouble:[[NSDate date] timeIntervalSince1970]] ];
This is because it is a managed object which creates it's own accessors at runtime through the #dynamic compiler instruction.

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.