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 in the best way?
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.
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.
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.
I always create a special object called DataModel
and use it's singleton sharedInstance
.
And this object then holds all the app-related-data. No need for accessing the dreaded appDelegate
.
DataModel.h
#import <Foundation/Foundation.h>
@class MyClass1, MyClass2;
@interface DataModel : NSObject
@property (copy, nonatomic) NSString *aString;
@property (assign) BOOL aBool;
@property (strong) MyClass1 *myObject1;
@property (strong) MyClass2 *myObject2;
+ (DataModel *)sharedModel;
@end
DataModel.m
#import "DataModel.h"
#import "Class1.h"
#import "Class2.h"
@implementation DataModel
- (id) init
{
self = [super init];
if (self)
{
_myObject1 = [[MyClass1 alloc] init];
_myObject2 = [[MyClass2 alloc] init];
aBool = NO;
aString = nil;
}
return self;
}
+ (DataModel *)sharedModel
{
static DataModel *_sharedModel = nil;
static dispatch_once_t onceSecurePredicate;
dispatch_once(&onceSecurePredicate,^
{
_sharedModel = [[self alloc] init];
});
return _sharedModel;
}
@end
And (bacause I'm lazy) i put DataModel.h
in application-prefix.pch
.
That way i can access my data from anywhere in the application simply by calling
[DataModel sharedModel]
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;