0

I have this code to jump to a second storyboard

UIStoryboard *secondStoryBoard = [UIStoryboard storyboardWithName:@"SpaceView" bundle:nil];

// Load the initial view controller from the storyboard.
NRGSpacesNavController *nav = [secondStoryBoard instantiateInitialViewController];
nav.managedObjectContext = self.managedObjectContext;
NRGSpacesViewController *spacesController = [nav.viewControllers firstObject];
spacesController.space = space;
[self presentViewController:nav animated:YES completion:nil];

the problem is the controller being instantiated on that second storyboard needs the managedObejectContext for it's container views. The only thing that loads before prepareForSegue is the actual init method. But I can seem to add the context on the init method. ViewDidLoad and anything else is actually too late. I would like to keep the storyboards if I can, but would be ok doing everything programmatically

I tried adding the context to the nag controller as you can did it doesn't work. The context is nil.

Thanks for the help any ideas welcome.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
krisnrg
  • 19
  • 2

4 Answers4

1

Create a property on the UIViewController you are moving to, and set that property in prepareForSegue:

In the UIViewController that powers the transition:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"YourVCSegue"]) {
         NextViewController *nextVC = segue.destinationViewController;
         nextVC.myManagedObjectContext = self.managedObjectContext;
    }
}

In the UIViewController interface being transitioned to:

@interface NextViewController : UIViewController

@property (nonatomic,strong) NSManagedObjectContext *myManagedObjectContext;

@end 

This will set the property after the init method is called (which has to be called otherwise you're setting on a nil object) and before viewDidLoad is called.

Another option (not preferable) is to create a Singleton helper class that holds the NSManagedObjectContext and you can access that throughout the application. E.g:

[[MyCoreDataHelperClass sharedInstance] managedObjectContext];

For my information regarding accessing the NSManagedObjectContext in your app, please see the documentation.

Tim
  • 8,932
  • 4
  • 43
  • 64
  • Passing the context to the next view controller *is* the preferred way to do this, not a singleton or the application delegate. From the iOS 5 Core Data release notes: "important than ever that you adopt the “pass the baton” approach of accessing a context (by passing a context from one view controller to the next) rather than retrieving it directly from the application delegate." This is also covered elsewhere in the documentation, and is what Apple's template code does. – quellish Aug 06 '14 at 09:05
  • Updated accordingly with a link to up-to-date documentation. – Tim Aug 06 '14 at 09:10
  • From the same document: "Sometimes, though, it’s easier or more appropriate to retrieve the context from somewhere other than application or the document, or the view controller." – Swapnil Luktuke Aug 06 '14 at 09:37
  • I have infact always found easier to retrieve the context from somewhere else. Passing the context, if I am not wrong, is related to the child contexts. – Leonardo Aug 06 '14 at 09:43
  • @SwapnilLuktuke You have taken the sentence out of context. Clearly states afterwards that this can be considered when you are using an NSFetchedResultsController or NSObjectController or similar. – Tim Aug 06 '14 at 12:19
  • @Jeff , just to speak: by using NSFetchedResultsController to retrieve the context from it means that you are passing the NSFRC from controller to controller, well of course Apple engineers know much more than me, but I still have to find an app with such architecture. Also, by mentioning **object controllers** I think it's perfectly legal to think about a proper custom CoreDataController, on which I based many of my App :-) – Leonardo Aug 06 '14 at 13:38
  • @Jeff, the Core Data Snippets documentation has not been updated in at least 4 years. It's not exactly up to date. Leonardo, passing the context has always been the preferred pattern. When the Xcode core data templates were updated for storyboards they continued to pass the context into first view controller, and on from there. This is also what the templates were doing before storyboards. – quellish Aug 06 '14 at 14:54
  • That's part of the problem. Since the nextViewController has embedded controllers and they also need the context it screws everything up. In the nextViewController I need to call a DataSetup method to pass the required data to the container controllers. If I call DataSetup on viewDidLoad(NextViewController) it's too late. Apparently right after init the prepareForSegue gets called and won't return unless the containers are loaded meaning I need the context on init. – krisnrg Aug 06 '14 at 23:50
0

You can assign stuff to upcoming view controller in prepareForSegue method. But a view controller is actually a bad place to store the context. (navigation controller IS a view controller).

Better way would be to either create the managed object context as a singleton or have a separate class to handle all core data interactions and create the context in that class. Write a getter to get the context.

Swapnil Luktuke
  • 10,385
  • 2
  • 35
  • 58
0

Ok I got a fix here.

  // Load the initial view controller from the storyboard.
NRGSpacesNavController *nav = [secondStoryBoard instantiateInitialViewController];
NRGSpacesViewController *spacesController = [nav.viewControllers firstObject];
spacesController.space = space;
spacesController.managedObjectContext = self.managedObjectContext;
[self presentViewController:nav animated:YES completion:nil];

I appears I was mistaken in the order things load. Also I moved some stuff around to get the context property set. This code runs first, then the containers'(within spacesController) viewDidLoad, then after that the spacesController viewDidLoad. So I load my stuff in those viewDidLoads and I am using a delegate to let the container know when the contained will load their views.

krisnrg
  • 19
  • 2
-1

Altough some may find this anti-pattern, I tend to have a unique CoreDataController, which manage the creation and sharing of contexts. I never liked the idea of passing contexts, especially in a multithread environment.

Having said that, I would use one of this solutions:

i) create a protocol with a NSManagedObjectContext property and have all the relevant controllers implementing it.

@protocol MyProtocol

@property(nonatomic,strong) NSManagedObjectContext mySharedContext;

@end

ii) rather than passing context, if the app is a drill-down interface and no heavy multithreading is involved, i.e. list and detail I would use the same approach of protocol, but declaring a generic NSManagedObject from where I could retrieve the context.

@protocol MyProtocol

@property(nonatomic,strong) NSManagedObject myObject;

@end

then in the destination controller

-(void)myMethod {
   // optional casting
   MyParticularManagedObject *obj = (MyParticularManagedObject*)self.myObject;
   NSManagedObjectContext *ctx = obj.managedObjectContext;

   // rest of the code....      

}

iii) As a final note, see if this may be your case:

prepareForSegue is not called after performSegue:withIdentifier: with popover style

Community
  • 1
  • 1
Leonardo
  • 9,607
  • 17
  • 49
  • 89
  • Thanks for the comment I am actually using a CoreDataTableViewController which is actually presenting the NextController via loading of another storyboard. The problem is as a posted below. The NextController has container controllers that need the context. I don't see the point in getting another context. It doesn't seem clean to me. I will have to look into it by using the ObjectID I can refetch an object and modify it using a second context in the NextViewController. By the way I also have a class that sets up the coreDataStack so it's easy to instantiate another context. – krisnrg Aug 06 '14 at 23:54
  • Ironically it can all be fixed by having one navigation controller but I was having some issues with animation. I wanted it to not show the nav bar on the first view, then bring in a second drill down and show it. But when I went back it was really award how it would hide the Nav bar again. Maybe custom transition? – krisnrg Aug 06 '14 at 23:58