Sharing an object throughout an app - iphone

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.

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!

Sharing strings and variables throughout ios app

I'm making an app for a final project for class and I need to share strings, integers and floats throughout different views in my application. I have to create an app that a waiter/waitress would use on the job. I need different views for different types of items to order (beverages, appetizers, entrées, etc.) and need to keep the items chosen, the price and the quantity of each accessible to different views to keep a running total available in each view (one view for each type of item) and finally for a view that displays an itemized list of what was ordered plus the total.
From what I understand I wouldn't want to use a tab bar application layout because typically users don't expect information to be the same between different tabbed views, so I was thinking of using a segmented controller for this. To get the strings, integers and floats shared between views could declare them in the AppDelegate? I've read that I could use singletons but we haven't covered that in my class so I think that may just add more complexity than I need. If I were to declare them in the AppDelegate would I have to synthesize them in the AppDelegateViewController.m to make them available throughout? I can't imagine I'd have to synthesize them for each different ViewController. Or would NSUserDefaults be perfect for this situation?
If I declared an instance variable in the AppDelegate would that essentially make it a global variable? I know that this is against encapsulation practices but that won't make a big difference with this app, it's not going to be in the App Store and the overhead shouldn't make a big difference considering this is going to be a relatively small app.
Thanks in advance everyone. Good luck with your finals if you still have them approaching!
Edit
I guess I should say that this is going to be a janky, terrible app off the bat, we didn't cover a lot of the more advanced topics such as the MVC paradigm for iOS so I'm pretty limited with what I can do compared to what we're supposed to do. Stupid class, I regret signing up for it when I really could have gone about this myself and gotten a better understanding of Objective-C, which we were taught nothing about, and a better understanding of the iOS framework.
Basically, if I declare the variables in the AppDelegate, even though it's a faux pas, would that work to access the strings and such from the different views? Or would the NSUserDefaults be a better solution in this case? I'm leaning towards NSUserDefaults and declaring them in the projects ViewController.
A typical Objective C MVC solution is to put all your related shared strings and variables into a Model object (create a new class for this object) as properties. Then share that Model object with all the Controllers that need to access those variables or strings.
Using the AppDelegate as a model object is "over-sharing", and clutters up the app delegate with non-delegate related stuff. A slightly cleaner solution is to have the app delegate hold a getter to a Model object that is widely used throughout the app. A solution that can cause code reuse problems, and is otherwise considered politically-incorrect, is to assign the model object, and/or the shared variables themselves, to C global variables (be careful with your retains). (But this usually generates the fastest and smallest amount of code, if you care about such things.)
As hotpaw2 said, using a distinct model class is the best way to go.
But, as this is a small app (while you are learning the basics) implementing these few variables in the app delegate wouldn't hurt and is perhaps a little easier.
If the app grows then you'll probably want to move the model out of the app delegate and into it's own class.
An even easier and simpler method would be to store this small amount of data in nsuserdefaults:
save to nsuserdefaults:
[[NSUserDefaults standardUserDefaults] setObject:howManyDrinks forKey:#"howManyDrinks"];
[[NSUserDefaults standardUserDefaults] synchronize];
retrieve from nsuserdefaults:
NSString *howManyDrinks = [[NSUserDefaults standardUserDefaults] objectForKey:#"howManyDrinks"];
You can utilize the facility of Delegation pattern using Protocols in Objective C.
You could also store these values in a plist, which can then be accessed and edited from anywhere within the app. It is easier to implement than a singleton. And since you can set the plist up visually in XCode, it's relatively easy for beginners. I even created a wrapper for my plist to make editing the data simple. Here's a tutorial for plists.
I'm not an expert in this yet, but I've been using a singleton, with an object inside. As far as I have experienced, the values inside that object is held. To create a singleton, you'd do something like this:
Create a normal Object, and add this to the header file:
+ (id)sharedManager;
Then in the .m file, add this:
+ (id)sharedManager {
static SharedInfo *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
self.myStuff = [[NSMutableDictionary alloc]init];
//This is where you initialise your variables. The example above is for a
//property that I have declared in the .h file
}
return self;
}
And then, throughout your app you'd use this:
[[[SharedInfo sharedManager] myStuff] addObject: SomethingTocreate];
Make sure you include the SharedInfo.h in the viewcontroller that you want to use it in.
I hope this helped.

MVC-friendly approach to Core Data

Ok so I am pretty new to Core Data, and I am now starting to dig into it more and I have a question about the correct way to implement it.
I have a parentViewController object that is loaded from the appDelegate. The parentView is a view that sets up a paging UIScrollView for three more viewController objects, it's children. The view loads up and I can page between the three views beautifully.
All three of the views need to have a significant amount of data exchange with Core Data. Should I pass the reference of the managedObjectContext into each different view within the parentView so that all the views can pull from and write to Core Data themselves? Or should I keep the reference in the parentView, and let the children pass the requests to the parent to handle?
Thanks
I have typically used a third option, at least for simple applications with a single persistence store that I access throughout the lifetime of the app: Make the managedObjectContext a property of your application delegate, and initialize it when the application is started. Then, you can access it from anywhere in your application using something like:
MyApplicationDelegate *delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = [delegate managedObjectContext];
This approach is not ideal for all designs and architectures, but I'm finding it works well for me and keeps my code relatively clean.
Controllers should never communicate directly with each other in MVC. Any data that needs to be shared between controllers should be shared through the model layer, of which the MOC is a part. So, ideally, you should pass the MOC to every object that needs to use it. Perfectly OK to have multiple objects reading/writing to the same MOC simultaneously; respects principles of Dependency Injection; and probably less code to write.

Best Application Delegate Practice

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.

Application Design and AppDelegate

I am developing an iPhone app for some sweet undergrad research I've been working on. Sadly, my school doesn't offer software engineering / design classes so when it comes to questions of best practices in OO Design, I do a lot of reading.
My Dilemma:
My application loads a view (v1) where, upon user's button click, v1's controller class executes an action method. This action method should fill an array with objects. After that, the user will either execute the action again or click a different tab to load another view. Other views in the application will use the array that v1 populated.
So, where should this shared array be declared? Right now, it's in the AppDelegate class from when I was testing features without a GUI. Should I grab the AppDelegate singleton and add items to it in the v1ViewController? Should it be declared as static?
Thanks for the help!
^Buffalo
EDIT:
Follow-up Question: When interacting with a singleton, which is the better way to talk to it:
[[MyAwesomeSingleton sharedInstance] gimmeSomePizza];
or
MySingleton *s = [MySingleton sharedInstance];
[s gimmeSomePizza];
I guess what I'm wondering is, do you make the sharedInstance method call every time or do you define a pointer to the sharedInstance and then reference the pointer?
Using the app delegate to store data that's shared between views and view controllers is reasonable and appropriate.
In my apps, I view the app delegate as the controller part of MVC, with UIViews and view controllers all being part of the "view". I prefer to use a variant of MVC called Passive View that keeps the model and view parts of my app strictly segregated with only the controller connecting them.
I'm assuming that the array of objects you're storing is your app's model, so storing them on your app delegate makes sense. As Daniel D said, there's no need to make it static.
The app delegate is really the heart of your program. You create and initialize your model and views in your -applicationDidFinishLaunching: method and save your model data and view state in -applicationWillTerminate:. When your view controllers receive events that changes your model, you can call methods on your app delegate to make those changes.
You could store it in an ivar in the app delegate. You don't need to make it static since the app delegate is a singleton anyways (there's never more than 1 instance).
If the app delegate is getting a bit complicated, you can factor out the data storage into a separate model object or perhaps use Core Data.