Why does Apple documentation that getting ManagedObjectContext from UIApplicationDelegate is bad?
Asked Answered
I

4

5

Just curious why ManagedObjectContexts should be passed to UIViewControllers when they are created, rather than just grabbing them from a UIApplicationDelegate?

The docs say that this makes your applications more rigid, but I am failing to see the nuances of when to use which pattern.

Thanks!

Imphal answered 31/7, 2011 at 4:12 Comment(0)
B
7

Imagine that I ask you to do some task, like painting a room. If I just tell you "go paint a room," you'll need to ask me a lot of questions, like:

  • Which room?
  • Where's the paint?
  • Where are the brushes?
  • Should I use a dropcloth?

In short, you won't be able to complete the task without help from me. If you have to depend on me every time, you won't be a very flexible painter. One way to deal with that problem is for me to give you all the stuff you need at the outset. Instead of "go paint a room," I'll say "please paint room number 348 using this bucket of paint and this brush, and don't bother with a dropcloth." Now, you've got everything you need, and you can get right to work with no further help from me. You're a much more flexible worker because you no longer depend on me.

The same thing applies to view controllers (and objects generally); it's better to give them everything they need than to have them depend on a particular object like the app delegate. It's true not just for managed object contexts, but for any information they need to do their job.

Baecher answered 31/7, 2011 at 14:36 Comment(0)
C
3

This is mainly because you want to use dependency injection with your UIViewControllers instead of just grabbing everything from UIApplication, this keeps your delegate clean instead of full of reference hacks.

This is also to keep with the MVC pattern:

  1. Model

  2. View Controller (Only for view logic)

  3. Controller (For coordinating between the view and the model)

Capwell answered 31/7, 2011 at 4:16 Comment(0)
A
2

I tend not to agree with this pattern.

First of all I try to treat Core Data as an implementation detail, and as any implementation detail it should be hidden behind a good facade. The facade is the interfaces I expose for my model objects. For example if I have two model objects; Cource and Student, any cource can have a number of students. I do not want to let the controller take upon the duty to setup predicates and sort descriptors, and jump through all Core Data hoops just to get a list of students for a particular class. There is a perfectly valid way to expose this in the model:

@interface Cource (StudentAccess)
-(NSArray*)studentsStortedByName;
@end

Then implement the ugly stuff once and for all in the Model class. Hiding all the complex details of Core Data, and no need to pass around managed object contexts. But how would I find the sources, it has to start somewhere right? Yes, it does but you need not expose it to the controller. Adding methods such as these are perfectly reasonable as well:

@interface Cource (CourceAccess)
+(Cource*)caurceByID:(NSString*)courceID;
+(NSArray*)allCources;
+(NSArray*)courcesHeldByTeacher:(Teacher*)teacher;
@end

This also helps in minimizing dependencies between controllers. And reducing he dependencies between the model and controller. Assuming I have a CourceViewController and a StudenViewController is I did not hide the Core Data details behind a facade and wanted to pass around the managed object context as well, then I would end up with a designated initializer like this:

 -(id)initWithManagedObjectContext:(NSManagedObjectContext*)moc
                           student:(Student*)student;

Whereas with good a good facade I end up with this:

 -(id)initWithStudent:(Student*)student;

Minimizing dependencies behind facades, in favor of dependency injection also makes it much easier to change the internal implementations. Passing around the managed object context encourages each controller to implement their own logic for basic stuff. Take for example studentsSortedByName method. At first it might be sorter by last/first name, if later changed to last/first name sort you would have to go to each and every controller that has sorted students and make the change. Where a good facade method requires you to change in one method, and all controller automagically get the update for free.

Alan answered 31/7, 2011 at 13:9 Comment(10)
I don't see where you're disagreeing with Apple -- you're just taking it a step further and hiding the context. You wouldn't advocate having your view controller fetch the list of students or courses from the app delegate or some other singleton, would you?Baecher
@Baecher - Nope not from the app delegate, but I would not be ashamed to pull a list of courses from a class method of the Course class. Treating the class as a singleton of sort.Alan
I disagree that sorts and predicates belong in the model layer. Sorts and predicates are only required by the interface so they belong in the controller. Putting sorts and predicates in the model ties the model to a particular interface and contaminates the logical integrity of the model's simulation of the real-world objects, events or conditions that the app deals with.Botticelli
@Botticelli - Keep sort in the model for as long as your app only sorts in one way. When the app displays sorts in multiple way ad an option, but do not move the sort. Otherwise you will end up with fat controllers and lots and lots of copy-paste code.Alan
@Alan -- The model should only deal with modeling the data. It should be abstract and work with any interface. Sorts are not part of model logic but merely for display. A model can conceivable be displayed with as many sorts as it has entity attributes plus all possible permutations of multiple sorts. You can't cram that many sorts into a model and still have any abstraction, modularity and portability left.Botticelli
@Alan -- Fat controllers are perfectly fine because they make the controllers self-contained, relocatable and reusable. You need to take the long view when designing apps. The point is not to save keystrokes but to create a code that is maintainable and extendable.Botticelli
@Botticelli - There is nothing wrong with having an intelligent model, with methods capable of doing what a controller decides. Less code and single point of change is what the last decades of coding has taught me makes for the most maintainable code.Alan
@Alan -- There is nothing wrong with encapsulating actual data operation but a sort isn't a data operation, sorts are used solely for displaying data. They have nothing to do with the actual content or logic of the data model itself. You might display a model of contacts sorted by last name, first name, telephone number area code, date of entry, date of last edit or any number of other attributes. You can have combinations of sorts such as last name, then first name. All those sorts have nothing to do with the actual data model and don't belong there.Botticelli
@Botticelli - I hear you, and I agree that theoretically you are correct. Practically I have found that knowingly breaking this rule has a greater win; you have the sort rule in one place, not spread across multiple controller implementation that in 99% of the cases all use the same copy-pasted code.Alan
@Botticelli let us continue this discussion in chatAlan
B
1

The Apple Docs try to foster the most widely applicable and sustainable design patterns. Dependency injection is preferred because it allows for the most flexible, expandable, reusable and maintainable design.

As apps grow in complexity, using a quasi-singleton like parking the context in the app delegate breaks down. In more complex apps, you may have multiple context tied to multiple stores. You might want the same view-controller/view pair to display data from different context at different times or you may end up with multiple context on different threads/operations. You can't pile all those context up in the app delegate.

If you have a simple app with a single context then using the quasi-singleton with the app delegate can work well. I've used it on several smaller apps in the past without immediate issue but I did hit scalability problems on a couple of apps when the apps grew overtime.

Which pattern to use depends on your shipping constraints and you best guesses about of the evolution app over its entire lifecycle. If its a small one shot app, then the app delegate quasi-singleton will work fine. If the app is more complex, might grow more complex or might spawn other related apps that will reuse existing components, then dependency injection is the way to go.

Botticelli answered 31/7, 2011 at 15:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.