views:

1668

answers:

4

My application is a tab bar application, with a separate view controller for each tab.

I have an object in my first view controller (A) which contains all my stored application data (Please ignore NSUserDefaults for this) which needs to be accessed by the second view controller (B) when I press a button on it. How can I achieve this coupling in the best way?

A: 

Both view controllers should reference a third object (C) as their dataSource; this object (C) containing all the stored application data.

C would be, in this case, the M in the MVC.

Add to each of your ViewControllers the following declarations:

// SomeViewController.h
// Before @interface

@class MyDataSource;

// In the interface

IBOutlet MyDataSource *datasource;
@property(retain) IBOutlet MyDataSource *datasource;
Williham Totland
Exactly. So how do i do that? Where should I store my data model and how can I access in the correct way to adhere to MVC from both ViewControllers.
Brock Woolf
+2  A: 

The most common way I've seen this is to set up the thing you want to access in the app delegate and reference it in other places like this:

MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
myStuff = appDelegate.stuff;

In the app delegate, set up a stuff variable and use @property and @synthesize as usual.

Some people say that it's not a good approach, since it's the same as using global variables, but it's very common.

nevan
Using a singleton is also a common approach.
titaniumdecoy
+2  A: 

I like to create a top level Model class that is a singleton and contains all the elements I might need.

It's helpful to also give it a top level load method that populates objects with just the db keys, using the hydrate/dehydrate pattern common in the Apple examples.

Typical usage in the app delegate would be simply,

[[MyModel sharedModel] load];

And then in a view controller:

NSArray *myThing1s = [[MyModel sharedModel] thing1s];
NSArray *myThing2s = [[MyModel sharedModel] thing2s];

You can then iterate over your thing1s and thing2s and when you need details, you can just call

[myThing1 hydrate];

which will populate the object.

Of course, you probably want to use CoreData to manage the persistence from 3.0 onwards.

edoloughlin
Could you post a singleton example in Objective-C that I can use? I'm leaning towards this way, it seems slightly better than Delegate access.
Brock Woolf
+4  A: 

One option you have is to declare your date model as instance variables of your app delegate (as mentioned by other commenters).

Instead of referencing the app delegate as suggested by nevan an alternative is to add a property to your view controller classes (A and B) for your data model.

Say you wanted to share a data model object between your view controllers you can add a property to each:

@interface AViewController : UIViewController {
    MyDataModel *model;
}

@property (nonatomic, retain) MyDataModel *model;

@end

@interface BViewController : UIViewController {
    MyDataModel *model;
}

@property (nonatomic, retain) MyDataModel *model;

@end

When you initialise your view controller you can then set this property to the object context initialised previously.

You have mentioned a tab bar controller. If your view controllers are wired through IB all you have to do is to set these parameters in your application delegate applicationDidFinishLaunching: method, before the tab bar controller is displayed:

@interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate>
{

    MyDataModel *model;
    AViewController *aViewController;
    BViewController *bViewController;
    ...
}

@property (retain) IBOutlet AViewController *aViewController;
@property (retain) IBOutlet BViewController *aViewController;

@end

@implementation MyAppDelegate

...

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
...

    aViewController.model = model;

    bViewController.model = model;

    [window addSubview:tabBarController.view];
    [window makeKeyAndVisible];
}

Don't forget to release the model in your view controller's dealloc method.


The alternative is to use a singleton object. An simple singleton example:

@interface MyDataModel : NSObject
{
}

+ (MyDataModel *) sharedDataModel;

@end

@implementation MyDataModel

static MyDataModel *sharedDataModel = nil;

+ (MyDataModel *) sharedDataModel
{

    @synchronized(self)
    {
        if (sharedDataModel == nil)
        {
            sharedDataModel = [[MyDataModel alloc] init];
        }
    }
    return sharedDataModel;
}

@end

You can access this data model from all your view controllers with something similar to the following:

MyDataModel *model = [MyDataModel sharedDataModel];

See also this stack overflow discussion about singletons.

szzsolt
I really like this answer. Using pointers to reference the data model from the AppDelegate seems a lot more clean than using a Singleton. This method also seems to keep the coupling to a minimum. Thanks.
Brock Woolf
See now I'd have used one of these two methods. But just to confuse me, the Stanford Lecture No.7 says both are no-nos! Check out: http://deimos3.apple.com/WebObjects/Core.woa/Feed/itunes.stanford.edu.3124430053.03124430055 at about 30 mins through. Anyway thanks for this, I'm gonna use a singleton :P
Joe