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

6

67

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?

Katabatic answered 12/8, 2009 at 17:19 Comment(0)
R
38

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];
Raspings answered 5/1, 2010 at 8:16 Comment(5)
Not using dependency injection was definitely a bad design when I first began using Core Data. Recently, I take about the same approach as you have outlined. The primary difference is that I have put the Core Data Stack Code in a category on NSManagedObject Context, if only to logically separate the Core Data stack code from the AppDelegate. In theory, I could use the category like a singleton, but I choose not to as it introduces "application rigidity" as you have said. Additionally, I use some custom code for the Core Data stack, and this allows me to drop this code into new projects easily.Katabatic
I am with you on using the App Delegate for creating the Core Data stack. I am using a UITabBarController as my root view controller, and I'm not sure how to propagate the context to that controller object, as it lives in MainWindow.xib and I'm not sure how to assign it a pointer to a ManagedObjectContext.. I think I'm posting a separate question for this.Gluteal
That Apple document says, "When you create a view controller, you pass it the context it should use." but I'm not seeing HOW this is done. The main view controller is created via the storyboard, if using a storyboard, right? So how to pass it the context?Friable
@VictorEngel So did you find how the context should be passed if the view controller is created by the storyboard? IT is so frustrating that everywhere people say not to get it from app delegate, but not say a word about HOW THEN SHOULD YOU GET THE CONTEXT?Octavla
If you look at Apple's templates, the view controllers have a mutable property for the context. The first view controller that can use a context has it set in didFinishLaunchingWithOptions. From then on it's passed along to each subsequent view controller. This is also covered in the documentation.Goudy
C
28

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

Cabbagehead answered 12/8, 2009 at 17:26 Comment(6)
Sounds practical to me. I'm surprised apple includes it the app delegate.Katabatic
they probably do that because they want to show how to do it and thats where they though it would be convienient place to put it since the app delegate is sort of a singleton alreadyCabbagehead
having a singleton core data controller object makes totally sense. we have it abstracted so that it can be reused in every project. +1Finnic
I also use a singleton class for the Core Data stack right now. I see it as acting like the notification center or shared user defaults, where you can call [[DatabaseController sharedDatabaseController] writableManagedObjectContext] to grab a specific context when needed. It seems clunky to call back to the application delegate to grab the stack.Dependent
I was thinking of using a singleton although I try to avoid them. Of course the app delegate is basically a singleton anyways, it just has a clunky method call. I also thought it would be convenient to have a class I can just drop in a any app when I want core data. Seems more OOP-like than adding methods/ivars to the app delegate. Good answers from "both sides of the street" so far.Katabatic
I agree with (a) having a generic core data management class that is much easier to drop into projects (especially ones already existing) and (b) that the reason it's always in the AppDelegate for examples is that they are trying to minimize as much non-example code as possible - so why make a whole singleton when the AppDelegate behaves that way for free (in terms of code length). I'd put it in a singleton so that only the classes dealing with Core Data had any contact with the singleton, and it also means fewer classes have to include the App Delegate header too.Attest
B
11

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.

Brainpan answered 12/8, 2009 at 18:7 Comment(3)
I usually see the Core Data stack in the App Delegate as well. However, the code I look at is usually created for illustrative purposes. The practical way to implement something sometimes differs from such examples. I didn't want to blindly follow Apples sample code without good reason. I tend to think you are correct in assuming it will just be a matter of personal taste with a few advantages either way.Katabatic
I also think that arguments 2 and 3, are because in tutorials or examples you are trying to minimize as much as possible any code not related to what you are trying to present - so implementing the mechanics of a Singleton is adding too much overhead to what is supposed to be a simple example. The thing I dislike about keeping these things in the App Delegate, is that it increases the number of things that then must know about the App Delegate...Attest
" 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;" No, UIApplication is not delegating any responsibility for Core Data functionality to it's delegate. You may decide that a persistent store is an application level concern, but it is not part of UIApplicationDelegate.Goudy
D
10

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.

Denazify answered 12/8, 2009 at 18:30 Comment(4)
Thats funny. That is exactly the piece of code I don't like. I dislike running to the App Delegate for file storage information. It felt "wrong". That made me question how other developers handled this situation.Katabatic
The reason the first code snippet feels wrong is because its a code smell. Wrapping that up in a handy function is just deodorant. Its a lot more straightforward to simply pass the context around to objects that need it (using property injection, mostly).Marriage
You should not get the context from the application delegate like this. You should pass a context from one view controller to the next, as shown in all Apple's examples.Raspings
More supporting evidence against using the app delegate to distribute your data model: hollance.com/2012/02/dont-abuse-the-app-delegateAldenalder
K
6

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

Katabatic answered 17/12, 2009 at 18:40 Comment(0)
H
4

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.

Hypochondria answered 12/8, 2009 at 20:21 Comment(2)
Something that has become an issue in iPhone development is using and configuring NSFetchedResultsControllers. You can also have your "Model" now how to configure and return NSFetcheResultsControllers, but it seems like the model class will get a bit bloated. I feel like NSFetchedResultsControllers blur the line between controller and model code (not necessarily in a bad way). I have recently taken this and some other ideas into my new configuration (added new answer).Katabatic
I agree with @Graham and this is how I do it. Your UIViewControllers shouldn't need to mess with the NSManagedObjectContext, they should just talk to the model and ask for what they need. The mechanics of getting that information is of no concern to my view controllers.Caleb

© 2022 - 2024 — McMap. All rights reserved.