MVC-friendly approach to Core Data - iphone

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.

Related

Core Data - Basic Questions

I would like to know how the following works in Objective-C
in my header file i have the following which is initialized from a different view controller
#interface UserLookup : UIViewController {
NSManagedObjectContext *referringObject;
}
and in my implementation file i have to pass this NSManagedObjectContext to 2 child view controller then does it make a difference which view controller is called first... and does the NSManagedObjectContext changes in any one of the child controller?
Regards
You don't really need to pass it around to every view controller where you will need Core Data access - just use
NSManagedObjectContext* moc = [(MyAppDelegateClass *)[[UIApplication sharedApplication] delegate] managedObjectContext];
managedObjectContext must be an accessible ivar of your app delegate.
It makes it conceptually similar too. There is one NSManagedObjectContext (in most uncomplicated apps, thought you can have multiples), owned by your app delegate. You don't ever retain or release it (except for when it is created in the app delegate, on first access if you are using Apple's template code, and when it is released in app delegate's dealloc.
ASFAIK it should not make a difference which viewController is used first. Think of the NSManagedObjectContext as a pointer to the physical datasource.
You can add and remove NSManagedObjects from the context. But these changes are only saved to disk when you call the save: method.
Does that help at all?
To use only one context is simple and works fine. But you can also create a new managed object context and pass it to the other view controller. Though the persistent store is only one, but you can have multiple contexts.
Each context have each undo manager, so you can control changes of managed objects in the context. You can save changes in only one context even if the other context has also some changes. After saving a context, you can merge the changes of two contexts by the following NSManagedObjectContext instance methods:
- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification
- (void)refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag
Maybe this document helps you to understand more detail.
http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html
And the CoreDataBooks Apple sample code uses the additional context.

How do I access the managedObjectContext from a controller deep in the UI?

I'm still a little fuzzy on understanding iPhone/Cocoa in general so this is probably a simple question.
I have a CoreData Window-Based App for the iPhone. The rootController is a UITabBarController. The first tab view has a UINavigationController attached to it with a table in it's main view.
When the App starts the objectContext is set up, which makes sense to have the App do that once. But now I have the managedObjectContext in the main Controller but I want to get that passed down to the Controller of the View inside the navcontroller, inside the first item in the TabBarController's tab list. How do I do this?
Would naming the one of the fields in the UI Inspector Tool allow me to do something like:
tabcontroller.navcontroller.manageObjectContext = self.managedObjectContext;
Would this only work if the controller was instantiated and 'live'. (Do the controllers not get instantiated until they are needed?) What if this was in a view that was for the most part hidden?
Anyway this is probably a simple thing I'm just not understanding things properly yet.
What is the general right way to share the manageObjectContext that is created and setup in the rootController to the many sub-controllers in the app?
I'm guessing this is the preferred method assuming the core-data initialization is done in the AppDelegate:
[[[UIApplication sharedApplication] delegate] managedObjectContext]
I usually give controllers a - (id)initWithManagedObjectContext:(NSManagedObjectContext *)context init method and a corresponding variable.
If a controller creates another controller in turn, it will, if needed, pass the NSManagedObjectContext to that controller in the same manner.
If you don't want to create an extra init method, just give the controllers a property for the NSManagedObjectContext and set that property directly after creating them.
I usually try to limit the number of controllers that directly deal with and "know about" Core Data though.
The answers to this question provide several means of accessing the Core Data stack deep within your application. As I indicate in one of the comments, I prefer to use a singleton DatabaseController that can be accessed from wherever, similar to how the NSUserDefaults' standardUserDefaults works.

Where to place the "Core Data Stack" in a Cocoa/Cocoa Touch application

In the iPhone Core Data Template, Apple places the Core Data Stack in the App Delegate.
My initial inclination however is to move this code into it's own class whose responsibility is to handle the management of the Core Data Stack.
Do you typically encapsulate this functionality within its own class or do you leave it in the App Delegate?
Summary: There is no need to create a singleton to manage the Core Data stack; indeed doing so is likely to be counter-productive.
The Core Data stack happens to be created by the application delegate. Importantly, however, as all the examples show, the stack (principally the managed object context) is not retrieved directly from the stack(*). Instead the context is passed to the first view controller, and from them on a context or a managed object is passed from one view controller to the next (as described in Accessing the Core Data Stack). This follows the basic pattern for iPhone all applications: you pass data or a model controller from one view controller to the next.
The typical role of the singleton as described here is as a model controller. With Core Data, the managed object context is already a model controller. It also gives you the ability to access other parts of the stack if needs be. Moreover, in some situations (as described in the documentation) you might want to use a different context to perform a discrete set of actions. The appropriate unit of currency for a view controller is therefore usually a managed object context, otherwise a managed object. Using and passing a singleton object that manages a stack (and from which you retrieve a context) typically at best introduces a needless level of indirection, and at worst introduces unnecessary application rigidity.
(*) No example retrieves the context using:
[[UIApplication delegate] managedObjectContext];
I have a singleton class that i let do my core data managment and i do not leave it on the app delegate. I rather not clutter the app delegate class with methods i might need for conviniece such as fetching certain objects etc
I leave the core data logic in the App delegate for the following reasons:
1) I do not see any real advantage in moving this code in other classes: the concept of delegation is perfectly fulfilled by the core data logic being handled by the App delegate since the core data model is actually a fundamental part of your application;
2) In all of the sample code I have seen, including Apple samples, the core data stuff is handled by the App delegate;
3) Even in Core Data books it is common practice to have the App delegate handle core data related code;
4) Personally I do not think that readability or anything else is actually improved by having ad hoc classes for core data, but this is a matter of personal taste and I will not argue here what approach is the best one. To me, simplicity while retaining functionality is important.
The question I'd ask myself, in your case, is "who does the Core Data stack 'belong' to?" The data itself is really province of the application, isn't it? (C.F. Core Data on the Mac, where you might have an application capable of working with multiple documents at a time, so the Core Data stack belongs to each document.)
In any Cocoa/Cocoa Touch application, the App Delegate is usually the preferred means of customizing the behavior of the application, so this is the natural place for the Core Data stack.
Now, the problem I suspect you're having is that it feels wrong to constantly write things like:
NSManagedObjectContext *context = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
What I typically do in these cases is write functions (not methods) like this:
NSManagedObjectContext *UIAppManagedObjectContext() {
return [*(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
I write a similar function for the NSPersistentStoreCoordinator and the NSManagedObjectModel. I put all of these in the App Delegate's .h/.m files, since these are application-level objects, too.
I'll just list this in a new answer. (I've scrapped my previous FJSCoreDataStack class in favor of this)
My new way of handling this has been to use a category on NSManagedObjectContext. Ive added the following class methods:
+ (NSManagedObjectContext *)defaultManagedObjectContext;
+ (NSManagedObjectContext *)scratchpadManagedObjectContext;
+ (NSManagedObjectModel *)managedObjectModel;
+ (NSPersistentStoreCoordinator *)persistentStoreCoordinator;
+ (NSString *)applicationDocumentsDirectory;
This keeps everything out of my app delegate, and gives singleton access should I choose to use it. However, I still use dependency injection from the App Delegate (as mmalc has said, it introduces inflexibility into my code). I have simply moved all of the "Core Data Stack" code into the NSManagedObjectCOntext Category.
I like passing the reference around, especially since I have a nice "scratchpad context" method. This keeps my View Controllers flexible since I have not committed them to the "defaultManagedObjectContext".
Also relevant to the conversation in the iPhone world (and may have a bearing on your architecture):
NSFetchedResultsController and constructing NSFetchRequests
I'm in favour of having the app delegate know where the model starts, and having the model know where the Managed Object Context is. The Core Data-"ness" of the model seems like an implementation detail of the model to me, the controller classes (like the app delegate) should just ask "give me this information about the model" and the model should know how to answer that question. Therefore having a Core Data object available through a controller object seems like a leaky abstraction.

Using iPhone NSNotifications in MVC app

I have an NavigationController based app where the data model is stored in the appDelegate after being fetched using CoreData. The views that display the data all have a pointer to appDelegate so that they can query the model for the required data. They then call methods in the appDelegate in response to user selections.
My question is, doesn't the MVC pattern optimally hide the data from the view? Would it be best practice for the appDelegate (in this case serving as model and controller) to supply the data to the view, and for the view to simply send a notification when there is user input? That would eliminate the need for the view to maintain a pointer to the appDelegate.
You're correct to worry about AppDelegate taking on this role. AppDelegate is an easy place to dump stuff, and it quickly becomes overwhelmed with roles. As you note, it's playing data model, data controller and application delegate. If you're getting to the AppDelegate using [[[UIApplication sharedApplication] delegate], it's especially a problem because it makes it very hard to reuse the view in other programs. It's less of a problem if your view has a delegate ivar that just happens to point to the AppDelegate, but could easily point to some other object.
The view often should have a delegate that it passes UI events to. See UITextField and UITextFieldDelegate for a good example pattern. Views generally don't post NSNotification for this kind of stuff. Conversely, model classes typically work best without delegates in my experience.
I am a huge believer in NSNotification. My programs use them for almost all data that moves up from the model layer. But for actions that move down from the view layer, delegation and direct method calls typically work best.
Its not your view that accesses the data. The controller should be the link between the data and the view. If by view you mean the controller, then that's perfectly fine and basically what MVC is all about.

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.