NSUserDefaults IOS Accessible Everywhere? - objective-c

I am wondering if the NSUserDefaults object is shared and can be accessed from within the app delegate as well as within several of my view controllers. Basically I need to pass data from the app delegate back and forth to the view controllers. I don't want to use a singleton. I was wondering if the NSUserDefauflts object was a way to do this.
If this is possible, how would I initialize the object so that is possible?
Thanks!

If you just use [NSUserDefaults standardUserDefaults], the same instance will be returned every time. Different classes can then use it to store data that is persistent across sessions.
If you're just trying to pass data between parts of the app, but not store it, user defaults are not the appropriate way to do so. You should expose methods or properties on your classes that take as input the data you need to pass.

Well, it is but that's not really what it's designed for. The normal design pattern is to pass the objects back and forth between your view controllers "manually." You want your view controllers to be as independent -- reusable -- from the rest of your application as possible. Tying them to NSUserDefaults isn't a good way to do that!

You should not be doing any processing in your app delegate. Ideally, you should initialise your window, root view controller (if not doing it by storyboard) and model and that's it. All processing should be done elsewhere (mostly in view controllers talking to the model classes).
Make your root model class a singleton so that all your view controllers can talk to it via an interface of your choosing.
Making a singleton is not hard:
#interface MyModel: NSObject
+ (MyModel *)sharedModel;
#end
and the implementation:
#implementation MyModel
+ (MyModel *)sharedModel
{
static MyModel* modelSingleton = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
modelSingleton = [[MyModel alloc] init];
});
return modelSingleton;
}
#end
And then you just use:
[MyModel sharedModel]
to access it.

Related

where to store "global" objects in iOS [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How or where should I store object instances that I require globally within my iOS app?
I have some global object (uses in almost all Application Screens) and mostly they are created right after application starts. I want to have access to this objects from all my ViewControllers (nothing else, only ViewControllers). Where to store it?
I think about #property in AppDelegate but i think (but I can be wrong) this is a ugly solution.
Objects can be quite complex, this is not a simple types.
You can make global objects accessible by placing them in a class with class methods for accessing global objects, implementing +(void)load to prepare these objects, and storing them in static variables.
Header:
#interface GlobalObjects
+(void)load;
+(MyObject1*)myObject1;
#end
Implementation:
#import "GlobalObjects.h"
static MyObject1* _myObject1 = nil;
#implementation GlobalObjects
+(void)load {
_myObject1 = [[MyObject1 alloc] init];
}
+(MyObject1*)myObject1 {
return myObject1;
}
#end
Usage:
MyObject1 *shared = [GlobalObjects myObject1];
You could also make the variable static inside its method for lazy initialization.
Yeah I use properties of the App Delegate, then access them by casting the sharedApplication delegate property.
__weak AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
Hope this helps,
Jonathan
#property in AppDelegate is a good solution. You could also use a singleton.
You app delegate is fine if you just have a bunch of objects.
Otherwise you might build a sort of "model object" containing all of your global data.
Or, you might store them with Core Data, if they have any structure at all.
But, as I said, if you have just a couple of objects, the app delegate will just do fine.
if it's only used among view controllers, you might consider storing it in the highest level view controller which actually needs access to the shared object (when creating/pushing new controllers, set that reference counted property).
in this way, you might think of the master view controller populating the detail view controllers with their content/models.
that's really stepping away from qualification as (and burdens of) a global.

Execute NSFetchRequest on application startup

In another question ( Accessing an NSApplications delegate in another class? ) I asked about calling the Application's delegate because I needed it's managedObjectContext for a fetch request. However, when I try to let all values be displayed in an NSTableView on application startup, I'm running into problems. DataController, my NSTableViewDataSource, calls it's init-method before my application delegate calls it's applicationWillFinishStartup or any other method to initialize the managedObjectContext. What am I doing wrong? How else can I fill an NSTableView with already existing objects?
You should access managedObjectContext only via its getter, even from DataController, as in [appDelegate managedObjectContext] or appDelegate.managedObjectContext.
Your managedObjectContext method should automatically set up the managed object context; you shouldn't write an explicit moc setup routines in your applicationDidFinishLaunching, etc. And the standard core-data template is written that way.
Now, for this to work, the app delegate needs to be properly set up from the point of view of DataController. However, init is called before all the IBOutlet is set up, so that's the wrong place to perform setup operations of objects inside the nib. Instead, use awakeFromNib to do these things. awakeFromNib is sent to every object after the IBOutlet etc. are all set up.
That said, writing your own DataController is a total waste of time. Just instantiate the standard NSArrayController in the nib file, and use it in the Core Data mode via binding. There's absolutely no need for you to write the fetch request by yourself. Study Apple's own CoreData sample codes and then google "Binding CoreData Tutorial" for many tutorials available on-line.

What are NSManagedObjectContext best practices?

I'm working with a Navigation Controller based iOS app. There are multiple tableView screens that pull and save data from a Core Data persistent store. Most of the data for the different table views comes from NSFetchedResultsController instances or NSFetchRequests.
The app works as intended but I have been getting a few random crashes and glitches that seem to be related to Core Data. For example sometimes when I save the context the app will crash but not always. Another thing I've been seeing is the very first tableView doesn't always update the reflect the data that was modified in it's detail view.
Currently I'm passing around a single Managed Object Context that was created in the app delegate to each of the different view controllers by setting the context property of the view controller just before I push it onto the navigation stack.
This seems like a clunky, hacky way of getting the job done. Is there a better design pattern to use?
I noticed in one of the WWDC sessions using delegation but I've never used creating my own delegates before and haven't been able to puzzle it out of the WWDC session.
Thanks.
=)
Use singleton NSManagedObjectContext for all Controllers isn't a best practice.
Each Controller should have your own Context to manage specific, sometimes atomic, operations at document store.
Think if you can edit a NSManagedObject attached to Controller that pass the same Context to other Controller that will select another instance to delete or edit.. you can lost the controll about modified states.
When you create a view controller, you pass it a context. You pass an
existing context, or (in a situation where you want the new controller
to manage a discrete set of edits) a new context that you create for
it. It’s typically the responsibility of the application delegate to
create a context to pass to the first view controller that’s
displayed.
http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html
1)
Use a singleton for your CoreData setup (NSPesistentStoreCoordinator, NSManagedObjectModel & NSManagedObjectContext). You can use this singleton to execute the fetch requests you created in your Models and to add or delete Entities to your Context.
2)
Delegates are not that hard. Following is a sample:
#class SomeClass
#protocol SomeClassDelegate <NSObject> //Implements the NSObject protocol
- (void) someClassInstance:(SomeClass *)obj givesAStringObject:(NSString *)theString;
- (BOOL) someClassInstanceWantsToKnowABooleanValue:(SomeClass *)obj //Call to delegate to get a boolean value
#optional
- (NSString *) thisMethodIsOptional;
#end
#interface SomeClass : NSObject {
id<SomeClassDelegate> delegate;
//Other instance variables omitted.
}
#property (assign) id<SomeClassDelegate> delegate;
#end
#implementation SomeClass
#synthesize delegate;
- (void) someMethodThatShouldNotifyTheDelegate {
NSString *hello = #"Hello";
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(someClassInstance:givesAStringObject:)]) {
[self.delegate someClassInstance:self givesAStringObject:hello];
}
}
#end
Option 1 could be something like this, you will have to setup the variables in the init of the object (and implement the singleton ofcourse):
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface CoreDataUtility : NSObject {
#private
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
+ (CoreDataUtility *)sharedCoreDataUtility;
- (NSEntityDescription *) entityDesctiptionForName:(NSString *)name;
- (NSMutableArray *) executeRequest:(NSFetchRequest *)request;
- (id) getInsertedObjectForEntity:(NSString *)entity;
- (void) deleteAllObjects:(NSString *) entityName;
- (void) deleteManagedObject:(NSManagedObject *)object;
- (void) saveContext;
#end
Currently I'm passing around a single Managed Object Context that was
created in the app delegate to each of the different view
controllers...This seems like a clunky, hacky way of getting the job
done. Is there a better design pattern to use?
There's nothing particularly special about a managed object context in this respect, it's just another object that your view controller may need to do its job. Whenever you're setting up an object to perform a task, there are at least three strategies that you can use:
Give the object everything it needs to get the job done.
Give the object a helper that it can use to make decisions or get additional information.
Build enough knowledge about other parts of the application into the object that it can go get the information it needs.
What you're doing right now sounds like the first strategy, and I'd argue that it's often the best because it makes your view controllers more flexible, less dependant on other parts of the app. By providing the MOC to your view controllers, you leave open the possibility that you might someday use that same view controller with a different context.
Jayallengator makes the helpful observation that every managed object has a reference to its context, and if you're passing around specific managed objects you don't also need to pass along the context. I'd take that a step further: if you're passing specific managed objects to your view controller, the view controller often won't need to know about the context at all. For example, you might keep Game objects in your data store, but a GameBoardViewController will probably only care about the one Game that's being played, and can use that object's interface to get any related objects (Player, Level, etc.). Perhaps these observations can help you streamline your code.
The second strategy is delegation. You'll usually use a protocol when you use delegation, so that your object knows what messages it can send its helper without knowing anything else about the helper. Delegation is a way to introduce a necessary dependency into your code in a limited, well-defined way. For example, UITableView knows that it can send any of the messages defined in the UITableViewDelegate protocol to its delegate, but it doesn't need to know anything else about the delegate. The delegate could be a view controller, or it could be some other kind of object; the table doesn't care. The table's delegate and data source are often the same object, but they don't have to be; again, the table doesn't care.
The third strategy is to use global variables or shared objects (which is what people usually mean when they talk about singletons). Having a shared object that you can access from anywhere in your code is certainly easy, and you don't have that "klunky" extra line of code that configures your object, but it generally means that you're locking your view controllers in to using that shared object and no other. It's a lot like gluing a hammer to your hand because you know for certain that that hammer is the tool you need. Works great for pounding nails, but it can be painful if you later discover that you'd like to use the same hand for driving screws or eating dinner.
The singleton approach seems to be best-practice, but another trick I found useful was that in cases where you're passing a NSManagedObject from one view controller to the next anyway (usually as an instance variable), you don't need to also pass the NSManagedObjectContext since you can get the context from the object you passed in by invoking [myManagedObject managedObjectContext]. This can be a handy shortcut when there's maybe only one or two methods where you need the context and you don't want the overhead of creating yet another NSManagedObjectContext ivar/property.

How to refer to the calling class in Objective-C

Can you refer to the sender of a message without passing the sender as a parameter?
This is simplified code for the sake of discussion:
// mainTableViewController.m
[dataModel loadData]; //Table is requesting data based on user input
// dataModel.m
-(void) loadData{
// I want to store the sender for later reference
sendingTableViewController = ???? ;
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
// Web data is loaded. Ask the sending tableViewController to
// reload it's data.
[sendingTableViewController.tableView reloadData];
}
I'm still getting used to how to refer to methods and properties that are the responsibility of another object. I want to send a message to dataModel to load some data using NSURLConnection. But I don't just want to return the data because I don't want to sit around waiting for the data to load. I want to send a message to the mainTableViewController once connectionDidFinishLoading is called.
Since the loadData method may be called from any number of tableViewControllers I can't just say [mainTableViewController reloadData].
Follow-Up Question
Great Information! I love the no-judgement nature of StackOverflow.
So the mainTableViewController would be the Delegate of the dataModel?
Would it be correct to say that the dataModel class defines the informal protocol?
I currently instantiate my dataModel class from within my mainTableViewController. So I could change my code like this:
// mainTableViewController.m
dataModel *myDataModel = [[dataModel alloc] initWithDelegate:self ];
// Does this method need to be defined in the mainTableViewController header file
// since I will already have defined it in the dataModel header file?
-(void) dataDidFinishLoading {
[self.tableView reloadData];
}
// dataModel.m
-(id) initWithDelegate:(id)aDelegate{
self.delegate = aDelegate;
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection {
[self.delegate dataDidFinishLoading];
}
Is it bad that my TableViewController is instantiating my dataModel, cause then my dataModel is owned by the TableViewController? Should I really instantiate the dataModel from the AppDelegate instead?
Thank You!
I daresay this isn't the correct way to think about the problem. Architecturally, by giving the data model knowledge of the table view controller, you are coupling your model layer to your controller layer. Violating separation of concerns is a bad thing.
In Cocoa, the use of delegate objects is used all over the place. A delegate object is an object that implements a particular protocol with callback methods that can be called when things or events (such as data loading from a remote location, in your case) occur. I recommend that you create a delegate property in your data model, have an interface that mainTableViewController (or any other class, really) implements, and assign that class as the delegate. Then, when the data is finished loading, call the appropriate method on self.delegate. In that callback method, you could then call [tableView reloadData].
Again, you do not want your data model to be coupled (meaning aware of) the existence of your controller classes.
Edit
I just re-read the last part of your question, about having multiple table controllers needing to listen for notification of the data being finished loading. For that, I suggest you use the Observer pattern in Cocoa by using NSNotificationCenter. You use use the notification center in the data model to send notifications to observers (you don't care who is observing; the notification center handles those details) and you'd also use it in your table controllers to subscribe to the notification. Delegates are a nice, simple solution if you only need one object to be directly called when something happens. Notifications are more complex and have more overhead, but give you the flexibility to have an arbitrary number of objects "listening" for a notification to be posted.
Follow-Up Response
A class doesn't define an informal protocol; the developer does. You could also define a formal protocol in a separate .h file and have the controller implement it if you want an enforceable contract. With a formal protocol, you can also use #optional on methods that don't have to be implemented by a class conforming to the protocol.
It is also not at all bad to instantiate the data model from within the table view controller. In fact, this is one very correct way to do it. Since the data model exists to encapsulate data that (presumably) a controller will want to display later, you can think of the controller as owning the data model. You may even consider making an instance variable (and perhaps a property, too) to store your data model. Besides that, your rewritten code looks good to me!
Do you mean the keyword self?
-(void)canHazCheeseburger:(BOOL)canHaz
{
if (canHaz) {
self.cheeseBurger = [[[CheeseBurger alloc] init] autorelease];
[cheeseBurger onNomNom];
}
}

Override Interface Builder instantiation of objects?

I'm developing for the iPhone, and I have a class DataManager, that is used to maintain my application data. When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB, and those classes have an outlet to it. The DataManager is being created using the standard init: method (or maybe initWithCoder:?), but since IB doesn't have the proper file (or NSData from the file) to instantiate the object, it has no initial contents.
So, is there any way to tell IB not to instantiate the class automatically? This will instead be performed using my application delegate, something like:
AppDelegate.h
IBOutlet DataContext *context;
AppDelegate.m
context = [NSKeyedUnarchiver unarchiveObjectWithData:dataLoadedFromFile];
As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
I would like to prevent maintaining the context as an ivar in the delegate, since that seems to stray from the MVC paradigm, and leans toward the singleton pattern instead. (The controller should not be responsible for the data in my mind. It can maintain a reference to it obviously, but should not be responsible for offering it to other classes.)
When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB … As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
Yup.
First, you should think about whether this is a controller or a model object. It sounds to me like it's a controller.
If it is, then you should move the model to a separate object or objects, and make those NSCoding-compliant, and make the data manager load and save those objects. A bonus of this solution is that you could tell the data manager to save the objects and purge them when you get a low-memory warning, not just at quit time.