Best Application Delegate Practice - iphone

I have been making a few apps here and there, and I know my way around. What has always confused me is accessing global attributes, and where the best place to set them are. I have a few questions about accessing things and how to access them.
Do you have to include your application delegates header file into any other other file you want to access it in? Say I have a view controller, that I would like to use, do I need to include the .h inside my view controller's .h? Or can I set the:
#class AppDelegate;
Can you only access the delegate by typing out:
[UIApplication sharedApplication].delegate
EACH and every time? Is that something I just have to get used to? Or could I set the following in my implementation in each .h:
AppDelegate *delegate;
And inside the init function, put the singleton instance to that variable?
Sorry if this was off structure, but I think it's a logical question people have a problem encountering and solving.

Maybe you need to reconsider how you are using the App Delegate? It sounds to me like perhaps you are not making a very good class design.
Regardless, here's a way to make it easy. Don't put this in init just use it when you need it.
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
Naturally, replace MyAppDelegate with the actual class name of your app delegate.

Another possibility is to add the code to use a properly casted app delegate reference as a #define in the app delegate header file, so after including it you can do something like:
MYAPPDELEGATE.customProperty = blah;
However I tend to favor just writing out the line that John presented, as use of #defines confuses code completion which I find more annoying than just typing the line.
As also mentioned, if you have a ton of references to the app delegate you may want to restructure to keep some of those references closer to home.

Related

Using the AppDelegate to share data

I've found a couple of sources that explain how to use the AppDelegate to share data between objects in an iOS application. I've implemented it quite painlessly and it looks like a good approach in my situation. Thinking about what could be done using the AppDelegate, I am wondering where the line should be drawn.
Obviously there are other ways to share data across view controllers, use Singleton objects, and NSUserDefaults. When is it appropriate to use the AppDelegate to share data? In my current situation, I use this approach to store the appleDeviceToken used for push notifications. I use that token when users login or logout of the app.
In MyAppDelegate.h I declare the property:
#property (nonatomic, retain) NSString *appleDeviceToken;
In MyAppDelegate.m I synthesize appleDeviceToken and then set it:
#synthesize appleDeviceToken;
------------------------------------------------------
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *devToken = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
appleDeviceToken = devToken;
}
Then, in my LoginViewController.m I retrieve it and post it to my server:
NSString *urlForDeviceTokenPost = [NSString stringWithFormat: #"/api/users/%i/appleDeviceToken", userId];
MyAppDelegate *appDelegate = (MyAppDelegate*) [UIApplication sharedApplication].delegate;
NSString *appleDeviceTokenStr = appDelegate.appleDeviceToken;
AppleDeviceToken *appleDeviceToken = [[AppleDeviceToken alloc] init];
appleDeviceToken.deviceToken = appleDeviceTokenStr;
[[RKObjectManager sharedManager] postObject:appleDeviceToken delegate:self];
This works great so far, but is this the ideal approach? What else should I know?
When the data and objects are truly global and/or cannot be pushed further down the graph. Storage at this high level is usually not required. As well, your implementations should usually have little to no knowledge about the app delegate -- What's worse than a Singleton? The God-Singleton :) If the app delegate is complex, something's gone wrong. If the app delegate's interface is visible (via #import) to many of your implementations or they message it directly, then something is wrong.
There is no need for an (idiomatic ObjC) singleton -- there is one instance of the app delegate.
NSUserDefaults is for persistence of small values (as the name implies) -- the ability to share is a side effect.
Since the data is already sent into the app delegate by UIKit in this case, that may be a fine place to store the data, or an object representation of. You might also consider forwarding those messages to an appropriate handler. The important point -- In most cases, you would want initialization to flow down the object graph, and to flow from the lowest points possible (e.g. as opposed to many objects referring back to the app delegate). So you might see the app delegate set a top-level view controller's model, but the view controller can then set the models of the view controllers it pushes. This way you will reduce dependencies and control flow, cause and effect will be easier to trace, and you will be able to test it more easily -- free of the context of a massive global state.
The following line always indicates that you've done something wrong:
MyAppDelegate *appDelegate = (MyAppDelegate*) [UIApplication sharedApplication].delegate;
The application delegate is the delegate of the UIApplication. It is called that for a reason. It is not called the ApplicationDataStore or even the ApplicationCoordinator. The fact that you're asking the application for its delegate and then treating it as something other than id<UIApplicationDelegate> means that you've asked it to do something it isn't tasked with doing. It's tasked with managing the things UIApplication needs (which doesn't mean "everything the 'app' needs).
It appears that you've already built a place to store this information: RKObjectManager. I would have the app delegate pass the token there, and I'd have the login view controller just note that it is time to push it. I wouldn't even put the string #"/api/users/%i/appleDeviceToken" in the view controller. That's not related to displaying the view. That's something for you networking stack (which you seem to have housed in RKObjectManager). "ViewController" means "controller for helping display the view" not "processor of the operation that the view represents."
That seems like an appropriate use. The application delegate is tempting to misuse because it's an easily-accessible object that's already present in every app. It has a real purpose, though, which is, as its title indicates, to make decisions for the application object, just as a table view delegate does for its table view object.
In this case, you're storing information that was passed to the delegate from the application itself. I'd say that's where the line is drawn.
Storing this token seems to be in accordance with the app delegate's purpose, unless you had another controller object which was focussed on dealing with remote notifications. In that case, you would probably just pass the token right on to that controller.
I'm more pragmatic. Since the appDelegate in my app knows how the tabBarController was populated, and all the navigation controllers, I have several methods there that let some arbitrary class communicate with some other class - usually these are single instances of some class (but not singletons). That said, if what you want to put there does not have a compelling reason to be in the appDelegate, well, it probably doesn't belong there!

Main AppDelegate

Sorry for questioning so dummy question, but I swear I used Google before doing it :)
So, the question is - how do I set the main AppDelegate class for my application? I mean, if I don't want to use the one generated by Xcode but to work with my own class for that.
I'm newbie in iOS programming, so probably I misunderstood some part of theory.
I will highly appreciate any of your help!
I can't imagine why you would want to reset the app delegate that your template sets up for you, but to it you'd take a .h interface and have it conform to the <UIApplicationDelegate> protocol, alloc & init that object (for the sake of arguments, we'd call it yourNewlyCreatedDelegateInstance) and then you could set your app delegate to that via [[UIApplication sharedApplication] setDelegate: yourNewlyCreatedDelegateInstance].

Sharing an object throughout an app

I am currently developing an application for the iPhone. The appdelegate shows a splash-screen while I'm caching data (e.g. NSDictionary) for use in a certain view. What is the best way to call this data from the view I need it in? I don't think passing it along as a variable from view to view until it reaches the view is a correct way to do this.
App Delegate (with Splashscreen that should cache the data to NSDictionary)
|
View A
|
SubView
|
Final View (here I want to use the cached data)
Thanks :-)
If the NSDictionary that you're caching the data in is an ivar of your App Delegate you can access it from anywhere in your app using the following lines:
myAppDelegate *delegate = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
NSDictionary *myData = [delegate cachedData];
Hope that answers your question.
If you have an object that will never be released throughout the entire life of the app, and really want it to be accessible from absolutely anywhere in the app (say, so that a simple debug NSLog from absolutely anywhere in the code can print it's state), then that's what global variables are for. Just assign a global variable with a reference to the object. If you don't mind generating nearly equivalent but microscopically slower and larger code, then assign it to an instance variable in the app delegate with a suitable getter.
Note that using global variables is a violation of encapsulation that won't be very scalable, maintainable or suitable for unit testing, but is perfectly suitable for a small app which is not much larger than most objects would encapsulate anyway.

How do you pass an object between ViewControllers on iOS?

Using the "Tab Bar" template in Xcode 4, each ViewController is created automatically, so I don't have the chance to set any properties on a ViewController as it's created. I want each ViewController to have access to an object (an instance of FMDB's FMDatabase).
There are a number of questions on StackOverflow relating to this already, but some are assuming that you are creating the VC's by hand, and others recommend using the AppDelegate as a mediator:
MyAppDelegateClass *appDelegate = [[UIApplication sharedApplicaton] delegate];
myLocalProperty = appDelegate.someDataModelProperty;
The above seems rather "hacky" to me. Is there a better way to access a single object from multiple ViewControllers?
Most probable implementation of FMDB's Data base would be to implement it as a Singleton and access it through out the application. Is there any reason for not doing so ?
This link should help you in doing so:
How do I make FMDB's database a singleton
To expand on KKK4SO's answer, you could implement the Database as a singleton. Then, have all of your UIViewControllers be a subclassed UIViewController that contains methods to access and modify the Database singleton.
MyAppDelegateClass *appDelegate = [[UIApplication sharedApplicaton] delegate];
myLocalProperty = appDelegate.someDataModelProperty;
There is a spelling error in this code 'sharedApplicaton' is incorrectly spelled it should be 'sharedApplication'.
Shame on me for copy and pasting! Anyway this will work for a quick and dirty way of passing data between ViewControllers etc.

Macros for appdelegate access using multiple targets

I was reading a post on accessing the app delegate using shorthand with a macro here
Short hand for [[UIApplication sharedApplication] delegate]?.
The solution to the problem provided was to define the following macro in the app delegate header.
#define AppDelegate (YourAppDelegate *)[[UIApplication sharedApplication] delegate]
My question is how can I do this with a project that has multiple targets seeing as the appdelegate name would be different for each one?
T he #define macro is just a string substitution facility in the pre-processor, so I don't think there is much you can do short of defining another macro that has the name for each target's app delegate.
The savings in typing to abbreviate the app delegate is not going to be very much, and imo probably not worth the time to do it.
And I will editorialize further to say that if you're making enough references to your app delegate to justify abbreviating, I think you need to move some code out of the appDelegate into better factored (possibly singleton) classes. My 2 cents.