It's a very basic question.
In AppDelegate.m,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.stationInfos = [StationDB database].stationInfos;
}
return YES;
}
and I want to be able to use stationInfos from inside my ViewController.m. What is the most conventional way to do it?
I would state that it is generally frowned upon to store such data in the AppDelegate (either the .m or .h file).
If you need to access the data from multiple places, then a Singleton object is generally the recommended approach. A singleton is an object-oriented concept where only a single instance of an object is maintained in order to allow access from multiple locations to the same data source.
Or, if [StationDB database].stationInfos is already a singleton (I'm not familiar with the object), then it's possible you might be able to just access it anywhere you need it. There's nothing that says you need to define it in the .h or the .m file - you can define it just in the method where it is needed as well.
General guidelines might be:
Define in .h >> If a limited # of other objects may need to access it.
Define in .m >> If only that class needs to access it.
Define in a specific method >> If only that method needs to access it.
Define as a Singleton >> If many objects need to access it.
Don't >> Define it in the AppDelegate. It's best to place your logic in your appropriate model, view, or controller class files.
You would set variable up in the .h instead of the .m. From there, inside your ViewController.m you should import the AppDelegate.h.
Then, when you want the variable do:
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
variableType *stationInfos = appDelegate.stationInfos;
Related
I have a variable declared in AppDelegate class(.h file) whose value gets changed from multiple ViewController classes.Also,single application-wide instance for my AppDelegate class is shared throughout my application as follows :
AppDelegate *AppD = (AppDelegate *)[[UIApplication sharedApplication] delegate];
As I could access this variable declared in AppDelegate from any ViewController class, is AppDelegate class as an example of Singleton class in this scenario?
Can anyone help to site out the usage of singleton class with real-life example ?
AppDelegate is however a singleton class but you show only use it for declaring things that applies globally in your application.For ex: If you want to change the color of navigation bar in your entire application you can use app delegate and set the color of navigation bar. Also app delegate is an object that handles different state transition in your app. So if you want to create a variable that can be changed from multiple View controllers you should create a singleton class and declare that variable in that class.
The app delegate is not supposed to be a repository for all kinds of global variables. The app delegate is supposed to be used for things that affect the whole of the application, like launch / app termination, entering the background and returning from the background, that kind of thing.
If there is state that is shared by multiple view controllers, that should exist only once, but doesn't affect the application as a whole, then you could consider creating a singleton for that state. Then again, global state that is just an artefact of how you write your code should be avoided.
AppDelegate can be used just like singleton, but I don't recommend it. It's like you can put all your classes declarations and definitions in a class.h and a class.m file. Simply import the class.h file can invoke all classes. But it will be very inconvenient to read, understand and manage.
AppDelegate is mainly used for all kinds of app itself event, through UIApplicationDelegate method. Do not recommend deal with too much logic about global data here. Such as classes named XXManager, XXService, PublicData, is proposed to manage all kinds of singleton data.
I need to pass the data from the UIViewController to AppDelegate. But should not with NSUserDefaults. I could not find an alternative for it even i don't want it.
if you need access to your delegate, I always create my singleton access in the next way :
// your imports...
#define appDelegate ((YourAppDelegate*)[UIApplication sharedApplication].delegate)
#class YourAppDelegate
//...
in the .h file of YourAppDelegate.
It´s helpful and useful, but do not abuse of the delegate to access to particular things, try to use a different way. Anyway, you can access in that way.
If some one can brief on declaring instance variable inside .h file inside #interface braces and in .m file #interface braces. like this below
#interface ViewController : UIViewController { NSString *str ; }
#interface ViewController () { NSString *anotherStr ; }
Thx
There's even a third place where you can define instance variables: at the implementation statement:
#implementation ViewController { NSString *yetAnotherString; }
AFAIK, in the olden times you could only define the instance variables in the main interface. The other two places were added later. You can also mix them (as long as they have different names).
The advantage of defining the variables at #implementation and also the class extensions #interface ViewController () level (when done inside an .m file) is that you can hide implementation details from users of your API. In other words, if someone reads the .h file (s)he doesn't know about the variables. This makes the visible API cleaner and is also a concept called "information hiding" which is quite important in object oriented programming: don't expose too much implementation details so you can change the implementation without breaking code using the class.
Note that you can also define IBOutlet variables at all three levels and Interface Builder will detect and use them!
So when you're deciding where to define the variable you can simply ask yourself: Do other people need to see the variable when they see the .h file? IMHO this is only true when you need/want to make a variable #public. For all other cases, you can define them at the class extension or implementation level to make the API cleaner.
Whatever you declare in ViewControllerA.H is public. It means that other view controllers that contain the ViewControllerA object can access use the methods or variables directly. Whatever you declare in .M is private, other view controller can not access it immediately.
As for my own practice, most of the variable (I don't use much) or properties I declare in .M to prevent other view controller to access it directly. It is just like one concept in Object Oriented Programming - Data Encapsulation.
Note: Please be reminded that this should not be confused with #public, #protected, #private like DarkDust mentioned below. It will be another different topic.
In objective-C while you declare the member in .h file, it becomes visible to the other file when .h file is imported as header.
By default all member variables are private. So, user can not use them directly. But with methods of runtime.h and setValueForKey give them an alternate way to set those variable.
To avoid the user to do such mischief, its advisable to declare your private variables in .m file. They are called extensions as well.
For example you have created a variable in your appdelegate file. Now import appdelegate.h file to other .m file. Get the instance of appdelegate by sharedApplication delegate. Now you can set value by below way.
[appdelegate setValue:your_value forKey:#"name of variable"];
Though it was private, user could do so. Its because when you check for auto suggestion window, it will list down your private variable with strike through. To avoid getting those variable inside this window, it is advisable to declare them in .m file.
Often times I initialize my model class variables in my AppDelegate so they can be used by different ViewControllers without passing their instance across class files. However, every time I import AppDelegate into a .m file to access these variable's data I feel like I'm doing some wrong.
Is this the the correct way for accessing these variables or should I be doing something differently?
EDIT:
My problem isn't how to access the variables. I currently use this line of code to get my appDelegate instance:
id appDelegate = [[UIApplication sharedApplication] delegate];
Conceptually, I want to know if this is the accepted way to interact with an application's model classes. It seems, to me, that an application's AppDelegate manages the application overall. So it seems counterintuitive to import this class into other classes further down an application's class chain.
Is this the the correct way for accessing these variables or should I
be doing something differently?
You'll find that different people have different opinions about this. The style that I prefer is to have the app delegate pass the necessary information to the first view controller, and have that view controller pass it on to whatever view controllers it creates, and so on. The main reason for this is that it prevents child view controllers from depending on things that they have no business knowing about.
If you have some detail editor, for example, you want to be able to pass that editor exactly what it needs to do its work. If you give it that information, the editor is completely flexible -- it'll edit any information that you give it. If the editor knows that it should get its data from some external object, like the app delegate, then it loses some degree of flexibility -- it can only get data from the thing that it knows about.
So, it's fine to set up your data model in the app delegate. But when it comes to providing access to the model, think: tell, don't ask. That is, have the app delegate tell the first view controller what model object to use, and have that controller tell the next one, and so on. If you have to ask, you have to know who to ask, and that's where the dependencies start heading in the wrong direction.
every time I import AppDelegate into a .m file to access these
variable's data I feel like I'm doing some wrong.
Trust that instinct. Think about why it feels wrong.
I agree that sometimes it seems like the AppDelegate is the logical place to put things that you only want implemented once but may need from several places. Creating a singleton for each one is fine, if those things are complicated, but it does create a lot of additional files and confusion to the project. I also agree with the majority of answers here, that building dependencies on the AppDelegate is a really poor design.
I think the best solution is to create a Protocol! Then put an IBOutlet to a property to do what you need to have done in each of the controllers that need the function. Protocols are the standard objective-C way to uncouple classes.
So, as an example, maybe I have a database URL that I may need from a bunch of places. Probably the best way would be to set a property with it each step along the way. But in some situations that may be cumbersome because of using a stock controller and not wanting to subclass it. Here is my solution:
Create file: MainDatabasePovider.h
#import <Foundation/Foundation.h>
#protocol MainDatabaseProvider <NSObject>
#required
#property (nonatomic, readonly) NSURL *applicationDocumentsDirectory;
#property (nonatomic, weak) NSURL *mainDatabase;
#end
Now "anyone" (i.e., any class) that says it implements the MainDatabaseProvder protocol is guaranteed to provide the two methods above. This could be the AppDelegate or ANY object.
Now if I want my AppDelegate to provide the information I change the AppDelegate.h file to have:
#import "MainDatabaseProvider.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate, MainDatabaseProvider>
#property (strong, nonatomic) UIWindow *window;
#end
(The only change is to add the MainDatabaseProvider protocol to the #inteface line, again this could be done to ANY class that you want to provide the function).
In my AppDelegate.m file I have to write the two methods...
#implementation AppDelegate
...
#synthesize mainDatabase = _mainDatabase;
...
- (NSURL *) applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject];
}
- (void) setMainDatabase: (NSURL *) mainDatabase {
if( _mainDatabase != mainDatabase ) {
_mainDatabase = mainDatabase;
}
}
- (NSURL *) mainDatabase {
if( !_mainDatabase ) {
NSURL *docURL = self.applicationDocumentsDirectory;
self.mainDatabase = [docURL URLByAppendingPathComponent: #"My Great Database"];
}
return _mainDatabase;
}
...
#end
Now in my controllers or other classes that have need of getting the MainDatabase I add the following:
In their .h files:
#import "MainDatabaseProvider.h"
...
#interface myGreatViewController: UIViewController
#property (nonatomic, weak) IBOutlet id <MainDatabaseProvider> mainDatabaseProvider;
...
#end
This property can be set in what was formerly known as InterfaceBuilder by control-dragging or can be set in code in prepareForSegue or I like to provide a custom getter that default it to the AppDelegate in case I am lazy or forgetful and don't do either of the above. In their .m file is would look like:
#implementation myGreatViewController
#synthesize mainDatabaseProvider = _mainDatabaseProvider;
...
- (id <MainDatabaseProvider>) mainDatabaseProvider {
id appDelegate = [[UIApplication sharedApplication] delegate];
if( !_mainDatabaseProvider && [appDelegate conformsToProtocol: #protocol(MainDatabaseProvider)] )
return appDelegate;
return _mainDatabaseProvider;
}
// To get the database URL you would just do something like...
- (void) viewWillAppear: (BOOL) animated {
NSLog( #"In %s the mainDatabaseProvider says the main database is \"%#\"", __func__, self.mainDatabaseProvider.mainDatabase.path );
}
Now the mainDatabaseProvider can be ANY object. I have the ability to set it in InterfaceBuilder or my StoryBoard (although I don't really think of this is a user-interface item so I typically wouldn't but it is pretty typical to do it that way), I can set it in code outside my controller before it gets loaded in prepareForSegue:sender: or tableView:didSelectRowAtIndexPath:. And if I don't set it at all it will default to the AppDelegate if I have configured it properly.
I can even put some safe guards in for when I forget to do things in my old age by changing the getter listed above to help me out with something like:
- (id <MainDatabaseProvider>) mainDatabaseProvider {
if( !_mainDatabaseProvider ) {
id appDelegate = [[UIApplication sharedApplication] delegate];
if( ![appDelegate conformsToProtocol: #protocol(MainDatabaseProvider)] ) {
NSLog( #"Hey!! The mainDatabaseProvider is not set and the AppDelegate does not conform to the MainDatabaseProvider protocol. How do you expect me to figure out where the database is!" );
} else {
return appDelegate;
}
return _mainDatabaseProvider;
}
You should seriously avoid importing the AppDelegate everywhere and you should feel like you are doing something wrong each time you do it (+1 for that). You are essentially creating a Big Ball of Mud and should reconsider your design. If for example you are using CoreData for your models consider a framework such as Magical Panda Active Record to retrieve data. I work on an enterprise application and the AppDelegate.h is only include in AppDelegate.m.
I'm importing them too and use it like this:
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[delegate variable];
Another way can be to use a Singleton.
Put this method in your AppDelegate class
+ (AppDelegate *)get {
return (AppDelegate *) [[UIApplication sharedApplication] delegate];
}
And when you need to call your AppDelegate use:
[AppDelegate get];
That is one way of doing it, yes, however it is not very elegant. Singletons are a way too, yes, however not very elegant :) - and reeealy NOT easy to test your code, if you have to mock out all your singletons.
Instead what I probably would do is to have one singleton for a service provider, and ask this service provider for an instance your model provider.
Say, your service provider class is a singleton and you need to access the model for the view user details. I would do it in the following manner:
JMUserDetailModel *myModel = [[[JMServiceProvider sharedInstance] modelProvider] userDetailModel];
This means that you would create a JMServiceProvider class for registering services, and being able to retreive these services. These services act a bit like a singleton, however if you need to unit test your code, then registering a different service that acts in the same way as the original is just a piece of cake.
Hope this answers your question.
EDIT: And read this article: http://martinfowler.com/articles/injection.html - a very good one explaining service oriented architectures as well ...
Look in to singletons. They can be a more elegant way to manage global data.
I have been using app delegate as a "global bucket" to share data between various view controllers. Typically I do something like this:
My_AppDelegate *appDelegate = (My_AppDelegate *)[[UIApplication sharedApplication] delegate];
And then, I would stick data into the appDelegate, and pick up data from the appDelegate with another view controller. Somehow, this seems clumsy and inappropriate (although it does work).
Is there a better way? Can I set-up a "listener" on some kind of a global sharing area, if somebody sticks a data element in there, another object would get a 'call-back' to let it know that somebody has data ready for it?
In Java we used to do this with Observer/Observable class - maybe there is something like this, or better in iOS?
A cleaner, although not necessarily different, way to do this is to create a singleton class, e.g. AppData, which you can access in a variety of ways, and which would be available to all your other classes. It has the benefit of separating your app-specific stuff from the app delegate stuff. You might define the class this way:
#interface AppData : NSObject
// Perhaps you'll declare some class methods here...
#end
A common thing I do is define class methods on such a class to access, for example, settings values, or app-specific constants or other singleton objects. There are a lot of possibilities.
In the end, you can get a lot done with just class methods, that you would call something like [AppData theMethod]. Just remember there's no self to access inside a class method.
Taking it one step further, you can define ivars for the AppData class, and then manage a singleton instance of AppData. Use a class method, e.g. +sharedInstance, to get a handle to the singleton on which you could then call mehods. For example, [[AppData sharedInstance] someMethod:myArgument]. Your implementation of +sharedInstance can be where you manage the actual creation of the singleton, which the method ultimately returns.
I am not sure if I'd call this approach a "best practice", but I've found this pattern quite handy.