iOS Singleton For Object storage - ios

When I first created my app, I stored all my runtime custom objects and properties in my app delegate so I could share them across views. I never liked this and always wanted to change it so I did some reading today and moved all my runtime properties and objects to a singleton object like so:
#synthesize gblStr;
+(AppDataSingleton *)singleObj
{
static AppDataSingleton * single=nil;
#synchronized(self)
{
if(!single)
{
single = [[AppDataSingleton alloc] init];
}
}
return single;
}
where lets say gblStr can be accessed from any view controller that has the singleton.
This works great and I now have all my objects stored here instead of in my app delegate.
In each view controller I add the property:
AppDataSingleton *globalSingleton;
and in the viewDidLoad, I instantiate it:
globalSingleton = [AppDataSingleton singleObj];
MY QUESTION IS:
Will there ever be a case where a user will come back to the app and the singleton has been destroyed? Do I need to check for this?
Or, in the case it was destroyed, will it start the app over from scratch?

The singleton will be destroyed, if the app has crashes or stops running. Unless you write your objects to a persistent store (CoreData, .plist, SQLite, etc...), you will have to recreate your objects as well...

In my view, the singleton should persist for the life of the program run (i.e. an entire session). If you are not clearing it actively, that data should be there whenever the app returns from an inactive state. In any case, you are checking if the object is nil and reinstantiating if it is, so the data should be rehydrated.
If you have concerns about data not being there, you should be actively persisting it to CoreData or the sandbox.

Related

Casting of [UIApplication sharedApplication].delegate

I've got a test project to use the private data object on several view controller.
(I've downloaded it from web & git-hub)
- (ExampleAppDataObject*) theAppDataObject;
{
id<AppDelegateProtocol> theDelegate = (id<AppDelegateProtocol>) [UIApplication sharedApplication].delegate;
ExampleAppDataObject* theDataObject;
theDataObject = (ExampleAppDataObject*) theDelegate.theAppDataObject;
return theDataObject;
}
First question is, theDelegate was casted with AppDelegateProtocol, even this applications UIApplication delegate name was ViewControllerDataSharingAppDelegate, and there's no warning. I can't under stand why, maybe it's because that was a id type?
(AppDelegateProtocol is a custom delegate protocol he declared in the AppDelegate.)
Second, it shows this kind of code on every view controller, and it seems like just a single-ton pattern.
I don't think this is not the best way to transfer data between view controller.
Which is the best way to transfer object data type?
Thanks.
Creating a protocol decouples the code somewhat from the specific implementation. You could conceivably have several applications, each of which uses its own custom class as an app delegate, but all implementations conform to the AppDelegateProtocol.
I used to use the app delegate to hold global data and methods a lot when I first started in iOS.
However, that fills your app delegate with app-specific code.
I've shifted away from that approach recently, and use a data container singleton (and perhaps a utility methods singleton as well.) As is typical for a singleton, I define a class method that lets me fetch the singleton. I add properties to my singleton as needed to store data, and then just use the class method to get a pointer to the singleton. I write my class method to lazy load the singleton.
Its also easy to make your data container singleton persistent by making it conform to NSCoding. Then every time you get moved to the background, just save your singleton somewhere. On app launch, read it in.

How to best handle CoreData + iOS State Restoration?

I'm trying to add iOS 6 State Restoration to an app that I'm just about finished with. It's an app where the model mostly comes from CoreData.
As recommended, I'm using the "pass the baton" approach to moving managed object contexts between View Controllers - I create the MOC in my App Delegate, pass it to the first View Controller, which passes it to the second in prepareForSegue:, which passes it to the third in prepareForSegue:, etc.
This doesn't seem to jive very well with State Restoration. The only thing I can think of to do is to retrieve the MOC from my App Delegate directly in an implementation of viewControllerWithRestorationIdentifierPath:coder:. In fact, it appears that the Apple developers did something similar when watching the WWDC session.
Is this the best/only way? Does State Restoration effectively break Pass-The-Baton, at least for view controllers that are restored?
To become familiar with state restoration I highly recommend the WWDC 2013 session What's New in State Restoration. While state restoration was introduced a year earlier in iOS 6, iOS 7 brought some notable changes.
Passing it forward
Using the "pass the baton" approach, at some point a root NSManagedObjectContext is created and an NSPersistentStoreCoordinator is attached. The context is passed to a view controller and subsequent child view controllers are in turn passed that root context or a child context.
For example, when the user launches the application the root NSManagedObjectContext is created and passed in to the root view controller, which manages an NSFetchedResultsController. When the user selects an item in the view controller a new detail view controller is created and an NSManagedObjectContext instance is passed in.
Saving and Restoring State
State restoration changes this in ways that are significant to applications using Core Data in view controllers. If the user is on the detail view controller and sends the application to the background the system creates a restoration archive with information useful for reconstructing the state visible when they left. Information about the entire chain of view controllers is written out, and when the application is relaunched this is used to reconstruct the state.
When this happens it does not use any custom initializers, segues, etc. The UIStateRestoring protocol defines methods used for encoding and decoding state which allow for some degree of customization. Objects that conform to NSCoding can be stored in restoration archives and in iOS 7 state restoration was extended to model objects and data sources.
State restoration is intended to store only the information that is required to reconstruct the visible state of the application. For a Core Data application this means storing the information needed to locate the object in the correct persistent store.
On the surface, this seems simple. In the case of a view controller managing an NSFetchedResultsController this may mean storing the predicate and sort descriptors. For a detail view controller that displays or edits a single managed object the URI representation of the managed object would be added to the state restoration archive:
- (void) encodeRestorableStateWithCoder:(NSCoder *)coder {
NSManagedObjectID *objectID = [[self managedObject] objectID];
[coder encodeObject:[objectID URIRepresentation] forKey:kManagedObjectKeyPath];
[super encodeRestorableStateWithCoder:coder];
}
When the state is restored the UIStateRestoring method -decodeRestorableStateWithCoder: is called to restore the object from the archived information:
Decode the URI from the restoration archive.
Get a managed object ID for the URI from the persistent store coordinator
Get a managed object instance from the managed object context for that managed object ID
For example:
- (void) decodeRestorableStateWithCoder:(NSCoder *)coder {
NSURL *objectURI = nil;
NSManagedObjectID *objectID = nil;
NSPersistentStoreCoordinator *coordinator = [[self managedObjectContext] persistentStoreCoordinator];
objectURI = [coder decodeObjectForKey:kManagedObjectKeyPath];
objectID = [coordinator managedObjectIDForURIRepresentation:objectURI];
[[self managedObjectContext] performBlock:^{
NSManagedObject *object = [self managedObjectContext] objectWithID:objectID];
[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self setManagedObject:object];
}];
}];
}
And this is where things become more complicated. At the point in the application life cycle where -decodeRestorableStateWithCoder: is called the view controller will need the correct NSManagedObjectContext.
Pass the Baton vs. State Restoration: FIGHT!
With the "pass the baton" approach the view controller was instantiated as a result of user interaction, and a managed object context was passed in. That managed object context was connected to a parent context or persistent store coordinator.
During state restoration that does not happen. If you look at the illustrations of what happens during "pass the baton" vs. state restoration they may look very similar - and they are. During state restoration data is passed along - the NSCoder instance that represents an interface to the restoration archive.
Unfortunately the NSManagedObjectContext information we require can't be stored as part of the restoration archive. NSManagedObjectContext does conform to NSCoding, however the important parts do not. NSPersistentStoreCoordinator does not, so it will not be persisted. Curiously, the parentContext property of an NSManagedObjectContext also will not (I would strongly suggest filing a radar on this).
Storing the URLs of specific NSPersistentStore instances and recreating an NSPersistentStoreCoordinator in each view controller may seem like an attractive option but the result will be a different coordinator for each view controller - which can quickly lead to disaster.
So while state restoration can provide the information needed to locate entities in an NSManagedObjectContext, it can't directly provide what is needed to recreate the context itself.
So what next?
Ultimately what is needed in a view controller's -decodeRestorableStateWithCoder: is an instance of NSManagedObjectContext that has the same parentage that it did when state was encoded. It should have the same structure of ancestor contexts and persistent stores.
State restoration begins in the UIApplicationDelegate, where several delegate methods are invoked as part of the restoration process (-application:willFinishLaunchingWithOptions:, -application:shouldRestoreApplicationState:, -didDecodeRestorableStateWithCoder:, -application:viewControllerWithRestorationIdentifierPath:coder:). Each one of these is an opportunity to customize the restoration process from the beginning and pass information along - such as attaching an NSManagedObjectContext instance as an associated object reference to the NSCoder used for restoration.
If the application delegate object is responsible for creating the root context that object could be pushed down throughout the view controller chain once the launch process is complete (with or without state restoration). Each view controller would pass the appropriate NSManagedObjectContext instance to it's child view controllers:
#implementation UIViewController (CoreData)
- (void) setManagedObjectContext:(NSManagedObjectContext *)context {
[[self childViewControllers] makeObjectsPerformSelector:_cmd withObject:context];
}
#end
And each view controller that provided it's own implementation would create a child context of it's own. This has other advantages - any approach that has the users of a managed object context react to it changing makes it easier to create the context asynchronously. Creating a context itself is fast and lightweight, but adding the persistent stores to the root context is potentially very expensive and should not be allowed to run on the main queue. Many applications do this on the main queue in an application delegate method and end up being killed by the OS when opening the files of the store takes too long or a migration is required. Adding the persistent store on another thread and then sending the context to the objects that use it when it's ready can help prevent these kinds of problems.
Another approach may be to leverage the responder chain in the view controller. During state restoration the view controller could walk the responder chain to find the next NSManagedObjectContext up the chain, create a child context, and use that. Implementing this using an informal protocol is simple, and results in a solution that is flexible and adaptable.
The default implementation of the informal protocol would walk further up the responder chain:
#implementation UIResponder (CoreData)
- (NSManagedObjectContext *) managedObjectContext {
NSManagedObjectContext *result = nil;
if ([self nextResponder] != nil){
if ([[self nextResponder] respondsToSelector:#selector(managedObjectContext)]){
result = [[self nextResponder] managedObjectContext];
}
}
return result;
}
#end
And any object in the responder chain can implement -managedObjectContext to provide an alternate implementation. This includes the application delegate, which does participate in the responder chain. Using the informal protocol above, if a view or view controller calls -managedObjectContext the default implementation would go all the way to the application delegate to return a result unless some other object along the way provided a non-nil result.
You also have the option of using restoration class factories with state restoration to reconstruct the chain of managed object contexts during restoration.
These solutions are not appropriate for every application or situation, only you can decide what will work for you.
I think the best way to handle this would be to encode the MOC in:
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
and then decode when it's restored via:
- (void)decodeRestorableStateWithCoder:(NSCoder *)coder
This should retain the pass the baton approach between state restores.
Bear in mind, every VC that uses a MOC should implement this if you're going with this approach.
To expand slightly, utilize the + (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder method to initialize your VC, then the MOC should be decoded via the method mentioned above and you should be all set.
This should hopefully provide enough information to get you going as far as encoding and decoding any information you want to recover when restoring.
I have not done a ton with state restore but I would think along these lines:
Does the app delegate get woken up first? Is there an opportunity for the app delegate to walk the view controllers?
Can the view controller pause while it waits for the AppDelegate to give it the context?
Sounds like State Restoration might be a special case but I would explore the option of making the view controllers smart enough to wait for the MOC to appear before asking for data. Maybe even having a roll back state in the view controllers where they step back to a place where the view controller can wait for the context.
Subclass NSPersistentContainer (as the docs state); adopt the UIStateRestoring protocol so it can be registered with state restoration which allows the pointer to be accessible during restoration methods, the object itself isn't actually encoded in the archive. Call UIApplication's registerObjectForStateRestoration inside the lazy persistentContainer getter. Also as the docs state pass the persistentContainer to the view controllers in application:willFinishLaunching and prepareForSegue, not just the context. For view controllers that cannot get the container passed at willFinishLaunching, encode the persistentContainer and the object's URI in the VC's encodeRestorableStateWithCoder. For show (i.e. push) VCs that are pushed on the master's navigation stack, use the restorationClass technique and UIViewController class method viewControllerWithRestorationIdentifierPath and decode the persistentContainer and then using existingObjectWithID return nil if the object no longer exists which prevents the VC from being created, if it exists then init the VC using the encoded storyboard and the object. In the case of show detail VCs that are always created irrespective of the object existing there is no need to encode the persistentContainer and no need for the restoration class design, just implement the app delegate's application:viewControllerWithRestorationIdentifierPath: and use the app delegate's persistentContainer and set the object on the detail view controller from the initial storyboard (capture it in a property in application:willFinishedLaunching and clear it in finished restoring, the reason is the split can collapse before application:viewControllerWithRestorationIdentifierPath: is called, meaning it can't be retrieved via the window).
The reason we do not decode the object in the decodeRestorableStateWithCoder method is because that is called after viewDidLoad which is too late to set the things we require.
I learned one very clean way of setting up the Core Data stack from NSScreencast. Basically, you start your Xcode project without choosing the "Use Core Data" option. Then you add a singleton class which is your data model. So to get the main MOC, you'd do [[DataModel sharedModel] mainContext]. I find that much cleaner that dumping everything in the App Delegate.
I've never used it this way, but I guess in your case you could also do this in your view controllers:
-(NSManagedObjectContext*)moc
{
if (_moc != nil) return _moc;
_moc = [[DataModel sharedModel] mainContext];
return _moc;
}
My solution to this has been to make the view controllers to default to use a global shared MOC. This context will be the intended one in most cases, and if you need to pass any other MOC it is perfectly possible to do so.
This approach confirms to Apple's "pass the baton"-approach as well as being both convenient and compatible with state restoration.

iOS: Singleton class for storage in UITableView app

I got a app that uses navigation controller and tableViews and I want to create a class to do some simple storage of information that stays persistent while navigating through the different views without saving to disk.
I can either create an singleton with only class methods, but in this case I´d need to create
the collection class holding the data as an instance variable (as #properties don´t work with class methods). I only ever see objects declared in properties in iOS, so is this frowned upon?
The class would look something like this
header:
+ (BOOL) addObject: (id) object;
+ (BOOL) removeObject: (id) object;
+ (NSInteger) count;
and privately I´ll have an NSArray for storage
NSArray *cache;
But is this a good way of achieving the task? or would it be possible to have a non-singelton class with instance methods and use that same instance of the class in the different table views? if so, how would I do that?
First, ALL readwrite properties auto-synthesize instance variables (unless you implement BOTH setter AND getter).
Second, if that information is global to the entire (or most of the) App, a singleton is just what you need. You don't need to keep it as a property (or an ivar). It's a singleton, it keeps its own pointer.
If you still want to go with a property, you will have to pass it some how to every VC in your App (prepareForSegue:sender: probably if you're using storyboards).
First figure out what global information you need. Then figure out what objects you already have that have a lifetime consistent with that global information, and which are logically associated with the info. Eg, if you need an array of info to "back up" a UITableView, store the pointer to that array in the table view data source instance.
It is rarely necessary to create a "singleton", and having lots of singletons is usually a sign of poor programming.

how to get managedObjectContext to work in multiple views

I am having trouble accessing my coredata objects from multiple views and have read that you have to declare it in your app delegate differently.. But I havent found an example that makes sense to me yet.
This is how I am currently declaring it in my app delegate.
//try setting up context for coredata
EResponses *eResponses = [EResponses sharedManager];
eResponses.managedObjectContext = self.managedObjectContext;
I am wondering how I could get this to work for multiple views? Any help would be appreciated.
Either you can reference it from the application delegate (like you are doing).
Or you can setup a singleton class that holds the managed object context. That's how I'm doing it. I'm having a singleton class with all the Core Data functionality adn whenever I need to do some database manipulation I use my Core Data singleton class.

How to control data flow between ViewControllers

I'll start by saying I'm new to Objective-C and iPhone but I have history in C++, Java, AS3, ...
I'm trying to build some sort of RSS Reader, and I have an array for all my feeds. How is the best approach to save new feeds to this array? I have a navigation based project, and I have an add button which pushes a viewController on top to enter new feed URL.
But how do I save this back to the array in my other ViewController? Do I need to research more into SQLLite? Or set some delegates? Or Core Data?
I prefer the singleton method myself but Apple recommends dependency injection i.e. passing a data model object from view controller to view controller as needed.
If you look at a Core Data utilizing template navigation project in Xcode, you can see how this works. The managedObject context is the data model and it is initialized and held by the app delegate. You can then access it two ways:
(1) Since the Application instances itself is a singleton, you can ask it for its delegate and then ask the delegate for its managedObjectContest property. So, in a view controller you would have a property managedObjectContext with a custom getter defined like:
(NSManagedObjectContext *) managedObjectContext{
if (managedObjectContext !=nil){
return managedObjectContext;
}
// this is basically applicationObject.delegate.managedObjectContext
self.managedObjectContext=[[[NSApplication sharedApplication] delegate] managedObjectContext];
return managedObjectContext
}
(2) Alternatively, whenever a view opens another view, it just sets the next view's managedObjectContext property to it's own. So that every view opens with a context. This is useful if you actually have multiple data objects for some reason.
If your just using an array or a custom data model class just substitute its name for the managedObjectContext in the code above.
Check out this question. I recommend using a singleton class and creating some listener pattern to signal when the data has changed (or just reload, always, before your view becomes visible).
You might want to store your feed items in memory by using the a singleton
Something similar to what is being used: Singleton shared data source in Objective-C

Resources