views:

2307

answers:

4

I have an app with a root view controller, a primary view controller, and a secondary view controller. I would like to be able to send a message to the primary view controller from the secondary view controller. How can I get a reference to the primary so that I can send messages to it? Is there a better way to architect this?

A: 

If the controllers are loaded from a NIB, you could define an outlet on the secondary controller and connect it to the primary controller in interface builder.

Eric Petroelje
+5  A: 

The short answer: you can get back to your application delegate like this:

YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate];

You likely already have a pointer to your root view controller in your application delegate class. And you probably have pointers to your primary and secondary view controllers in the root controller object. So, you could write code like this:

SecondaryViewController *primary = delegate.rootController.primaryController;

You can then send messages to it to your heart's content. No outlets required; just properties to each view controller.

There are many longer answers and also a discussion about why this practice might be questionable since it introduces potentially unwanted linkages between objects. In a "pure" object oriented design, you'll follow a clean design pattern with clear linkages between objects in various directions allowing you to better reuse the code.

Another option is to pass in pointers to the objects the class will need at initialization time. Implement a new initWithSomethingOrOther for your view controller classes and pass objects in as parameters. Cache these pointers you need (don't forget to retain them) for later use.

Jon Thomason
+2  A: 

The clean way to do it is to define a protocol for a delegate for the secondary controller which lists the methods it needs the primary controller to provide:

@protocol SecondaryControllerDelegate <NSObject>
- (void)secondaryController:(SecondaryController*)secondaryController 
             frobFooWithBar:(Bar*)myBar;
- (BOOL)secondaryController:(SecondaryController*)secondaryController
           shouldTwiddleBaz:(Baz*)currentBaz;
@end

Now add a delegate property to the SecondaryController:

@interface SecondaryController : UIViewController {
    id <SecondaryControllerDelegate> delegate;
    ...
}

// delegates are one of the few places you don't retain an object
@property (assign) id <SecondaryControllerDelegate> delegate;
...

In SecondaryController's implementation section, synthesize the delegate property. (Do not release it in the destructor.) When SecondaryController needs to communicate with the PrimaryController, it should call the appropriate method on the delegate.

Now make your PrimaryController implement the SecondaryControllerDelegate protocol:

@interface PrimaryController : UIViewController <SecondaryControllerDelegate> {
...

Implement the delegate methods in PrimaryController.

Finally, have your PrimaryController set itself as the SecondaryController's delegate. Exactly how you do this will depend on whether you create SecondaryController in a nib or not. If you do, make the connection there; if not, make it just after you allocate and init the SecondaryController.

Why do you do this song and dance? Well, when you have to introduce another controller between the Primary and Secondary, or use the Secondary elsewhere in the app, or even use the Secondary in another app (I have one controller that gets used in three of my four apps), you don't have to change SecondaryController at all; you just change whatever class should now be its delegate. This is an incredible time saver in the long run.

Brent Royal-Gordon
A: 

Use NSNotificationCenter for decoupled communication between objects.

Kristopher Johnson
+1 for NSNotificationCenter.
Adrian Kosmaczewski