How to pass the model to a Controller in Core Data - objective-c

Say I have a Core Data class called RecipeBook which has a property (relationship) called recipes, a NSSet of Recipe Objects.
I display the RecipeBooks on a UITableView and when the user taps on a cell, it should display the Recipes on another UITableViewController.
What should I pass as the model to this last UITableViewController:
a context and a fetchRequest
or the NSSet of Recipe objects?
If there's a change to the db will the NSSet "automagically" update?

From your description I'd probably pass the selected RecipeBook instance. From that I can (presumably) get all the recipes contained in the book and display them in the table. That assumes that a relationship exists from RecipeBook to Recipe which-- based on your description-- should be true. If I need to do any other work with the data store, I could ask the RecipeBook for its managed object context and work with that.
No NSSet of fetched objects is going to update automatically. But the relationship from a RecipeBook to its Recipes will update any time a recipe is added or removed from the recipe book.
And finally-- passing any of this directly to a UITableView doesn't make a lot of sense. Apple's iOS frameworks are designed with MVC in mind, and going against that will make things a lot harder than they need to be. If you have a UITableViewController, you could pass your model objects to that.

Generally, you shouldn't pass "model" objects to view objects.
My favorite way to think about it is that views are actually another form of a model (think of them both as simply representations of data). The controller's job is to ensure that neither representation needs to know anything about how the other stores it's representation.
So basically, your controller will be your data delegate, and is responsible for properly populating table cells with it's own references to your core data models.
The automagically question depends on your core data setup, but usually the answer is yes.
By default, CD uses key-value coding, which simply lets you access properties with valueForKey:. More advanced setups involve having Xcode generate classes for you, in which case a few mouse clicks get you "dot notation" accessor methods regenerated from an updated model.

Related

Correct way of dealing with Core Data NSManagedObjects in an iOS application

When I have worked with Java applications involving a database and ORM (object relationship manager) the architecture was usually separated so when working with database entities in the database layer you worked directly with the entities. But if you needed to access the entities in the GUI you would create a data transfer object (DTO) of the entity and then use it in the GUI layer.
What is the preferred approach in iOS applications?
One example would be that I do a fetch from Core Data to retrieve some objects in a ViewController and then display the results in a TableView in the ViewController. Is it necessary to convert the NSManagedObjects to DTOs before showing the results?
What is the preferred approach in iOS applications?
Is it necessary to convert the NSManagedObjects to DTOs before showing the results?
No, it is not neccessary. The managed object is already a (generic) object wrapping the database entity. You can access the managed object's properties (ie the entitys attributes) using -valueForKey: calls.
But the preferred approach is to subclass NSManagedObject to fit your entities. In Xcode there is an option to generate subclasses automatically (editor-> generate NSManagedObject subclass). That way you get your own subclass object for your entities and can access their properties using dot notation.
On fetching objects, just let the result be of type of that subclass.
If you want to add custom methods to your entity objects, you should create a category on your managed object subclass. This is because if you change your db scheme and have to let xcode recreate your subclasses, it completely overwrites the and your custom methods would be lost.

Inserting controller logic between model and view when using Interface Builder, NSObjectController sub-classes, and bindings

I've been struggling to understand the best way to insert controller logic when using IB, NSObjectController sub-classes and bindings.
I need to insert controller logic between the model and the view, and I'm struggling to find an elegant way to do so. Yes, you can send actions to the file owner and handle controller logic in there, but when some core data models can extend across fifty entities or more with deep relationship structures, this starts to mount up to an incredible amount of boiler-plate code.
A very simplified example would be this; imagine you have an entity with four string attributes myTextWinter, myTextSpring, myTextSummer, myTextAutumn. You have a view which connects to this in IB via an NSObjectController. Now, say the user can select which 'Season' they wish to view by choosing Spring, Summer, Autumn, Winter from a Menu somewhere - when that season is selected, I would like to display the appropriate season's text.
In this simplified example I could probably fetch the object in the NSDocument sub-class, create a property called mySeasonText which I bind to in my view, and then check my NSUserDefaults for the appropriate season and route the requests to the appropriate attribute in the model.
The problem comes when I have fifty entities, some with relationships some two, three or more deep, each with their own set of season specific text attributes that I wish to switch between when selecting from the Season menu. Or if I have a bunch of nsarraycontrollers chained together to access objects at a deeper, and deeper level.
To date, I've been doing the following; adding a property in each of my model objects called 'mySeasonText', then grabbing the setting from my controller setting, and routing to the appropriate season. I refresh these objects whenever a new item in the menu is selected.
Whilst this works and eliminates an absolute ton of boiler-plate code, my controller logic is now in my model.
There must be a better way! Could someone please point me in the correct direction?
This is a tricky topic. Apple even mentions these challenges in its own documentation:
By using the bindings technology, you can easily create a Cocoa MVC application whose views directly observe model objects to receive notifications of state changes. However, there is a theoretical problem with this design. View objects and model objects should be the most reusable objects in an application. […] Design-wise, it's best to keep model and view objects separate from each other, because that enhances their reusability.
The design pattern you are looking for is a Mediating Controller - a way to use the cocoa-bindings classes to insert controller logic:
Mediating controllers are typically ready-made objects that you drag from the Interface Builder library. You can configure [Mediating controllers] to establish the bindings between properties of view objects and properties of the controller object, and then between those controller properties and specific properties of a model object. As a result, when users change a value displayed in a view object, the new value is automatically communicated to a model object for storage—via the mediating controller; and when a property of a model changes its value, that change is communicated to a view for display.
Here is how I like to think of them: Have you ever seen a movie or TV show where two characters need to talk, but they don't speak any of the same languages? They find someone else (or in a comedy 5 other people) who each have one language in common, and they communicate by playing a giant game of translation telephone.
Mediating controllers are kind of like that.
As your application grows they learn all the super specific rules about where to look for this one thing on this one view. This is the kind of code that an app needs to run, but you rightly feel that it is nasty when put in your model.
For several specific and detailed examples, Apple provides this amazingly detailed document: Bindings Message Flow.
For some really good discussions about this and related MVC + Bindings please see:
MVC and cocoa bindings best practices question
Why use NSObjectController?
Replacing model objects using an NSArrayController

Objective-C undo manager questions

I'm reading a book on Objective-c and learning about the undo manager. The concept seems very simple but the provided example seems overly complex. Basically, I have a table view connected to an NSArrayController and I add or remove people to an array and I can edit their names and stuff. Because the example uses NSArrayController and bindings, add and remove are automatic and all of the editing is automatic.
To use the undo manager, from what I understand, I need to implement my own methods to add/remove/edit.
These methods I've implemented to do the adding and removing and get called automatically due to key value coding:
- (void)removeObjectFromEmployeesAtIndex:(int)index;
- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index;
Then for editing, I had to register the class as an observer and observe changes to edit:
- (void)changeKeyPath:(NSString *)keyPath
ofObject:(id)obj
toValue:(id)newValue
Here are my questions:
Why do I have to do so much? My understanding was that using the NSArrayController and bindings was supposed to make things like adding/removing/editing items easier and more automatic. But if I have to implement all of these methods manually anyway just to add undo support, why use NSArrayController or bindings at all?
What's going on behind the scenes? In Interface Builder, the add button is connected to the add method on the NSArrayController. How then does my insertObject method get called? I know it's through key value coding but what makes the NSArrayController's add method get overridden just b/c my document implements this method?
The solution is asymmetric. I use one concept to handle undoing add/remove and another concept to handle undoing edits. Couldn't I also just observe changes to the array? I suppose it would complicate the observeValueForKeyPath method, but would that make more sense?
1) Nearly, but not quite. If you think of your application code being divided into three overall areas: Model, View and Controller (as documented here) then the Cocoa/XCode environment provides you with a 'code-free' way of handling the basics of each: IB for the view, Core Data for the model, and Bindings / Object Controllers for the controller.
Undo management is primarily a concern of the model, not the view or controller. So it's not really Bindings or the Object controller's job to manage this stuff. It looks like your problem is that you're using arrays as your data objects, which are too lightweight to handle this stuff. If you want undo support, you'll want to use core data to handle the model and give you this stuff for free, or hand-roll your own model objects, (which will probably contain arrays) which handle this logic.
FWIW, once you've done this, bindings will indirectly make your life much easier, as when an undo command reverts your data to its previous state, the view will automatically reflect the changes.
Also, NSArrayController's name is slightly misleading -- it isn't there to 'control arrays'. It's really for controlling data objects which have to-many relationships to other data objects. Which brings me on to...
2) KVC allows you to treat a to-many relationship between an object and other objects as an array or set, regardless of how the relationship is actually implemented. It does so by requiring you to implement methods fitting a naming convention, which very closely match the primitive methods of arrays and sets. KVC-compliant objects will return a proxy array or set when you call mutableArrayValueForKey: or mutableSetValueForKey:, which exposes those methods as an array. Roughly, that's how NSArrayController knows what to call --- KVC maps between the primitive objects of an array and some methods whose manes it generates from the key. Since you don't want to use arrays as your data objects, it's generally very useful to be able to treat any to-many relationship as if it were just an ordinary collection.
3) I think this is related to you handling undo in the wrong place. Implement KVC-compliant methods to get/set properties in your data objects, have them update the undoManger at the same time as setting the data. You'll need a special method for the undomanager to revert changes, as you don't want undos to be recorded as undoable. Or you could just use Core Data and get all this stuff for free...

Core Data - Managed object question

I have 2 basic questions regarding Core Data;
What exactly is a managed object -- Is it equivalent to 1 instance of a class. For example, if there is an entity called Shape which has attributes like no of sides & color and if there are 5 entries displayed in a table, does it mean there are 5 managed objects each with attributes no. of sides & color. I am a bit confused about this basic concept.
What exactly is the relationship between Fetched Results Controller (FRC) and a table view? I know the delegate methods, but how exactly is the table view impacted/related to FRC ?
Any basic examples will be really useful. Thank you.
Q1. A managed object is a representation of some entity that has been persisted by an application. It is simply a generic abstraction away from the actual persisted type. So you are right in saying that the managed object will have the same keys/properties as the concrete type. As the NSManagedbject class implements the key-value coding pattern you can query its key/values at runtime...
- (id)valueForKey:(NSString *)key
For full documentation on NSManagedObject see here
Q2. The fetched results controller is what your UIViewController is to your UIView. It contains the logic that controls persistence for your table view. Its sole purpose in life is to keep database handling logic out of your UITableView. It does so by allowing you to define the behaviour you want to execute in the context of your UITableView. What I mean by this is that its delegate provides methods with signatures that explicitly imply an effect on a UITableView.
Q1. Yes, there are 5 managed objects out there and available to you. You could put all five in an array if you wanted to. Managed Object is simply a term that means you have code to manage the Insert, Change, and Delete actions into the database - in otherwords, some code manages it through its life cycle. You get at it through an FRC, the FRC ^fetches^ instances of the object from the database, and allows your code to ^control^ what happens to the ^result^. Hence the name.

Elegantly add object to arraycontroller, which itself is linked to another arraycontroller?

I'm new to OS X programming but generally liking it. I have the following problem:
I have two core data entities linked through a one-to-many with their respective arraycontrollers (Stock Controller and Price History Controller, where the latter controller is bound to the Stock Controller, with Controller Key = selection and Model Key Path = priceHistory, which is the relationship that links the stock entity to the PriceDataPoint entity, controlled by the Price History Controller.
This all works like magic in my UI, where I can select stocks and add/remove price points to each one when it is selected. However, I need to be able to do this programmatically as well.
If I simply call [stockController add:self] the UI updates with new objects with the correct default values, linked to the selected stock -- even though 'self' is not the correct class/entity. This is one point of confusion for me, which I don't understand (I understand that the Stock Controller knows about the selected stock through the KVO binding and would likely set the relationship as required, but I don't get how it 'casts' 'self', which is a fairly random class into the object type required (a plain NSManagedObject)? Secondly, if I do this, how do I get a reference back to that object so I can edit its values?
More importantly, however, if I then follow the Apple examples, create a new NSManagedObject through NSEntityDescription:insertNewObjectForEntity: and use [stockController addObject:Newly created Object] I can write the values I want before adding it but the relationship to the 'parent' stock is not set by the addObjects: method. I am sure I can figure out how to write this, too but with everything else in Cocoa being so elegant this just feels odd so I am hoping that someone here can clear this up very quickly and point me to an elegant way of doing it.
First, let's dispel the magic by stating Core Data is built to work with the Cocoa Bindings mechanism (which gives you array controllers, among other things) and is built atop Key Value Coding / Key Value Observing mechanisms. The Core Data documentation clearly states you should have a good background in these technologies to understand how / why things are working.
For your first issue, you said you're confused by the -add: method of NSArrayController. Have you looked at the signature/prototype? The argument (where you pass self) is (id)sender, which is the hallmark of a basic action (see "target/action" in the docs). You can pass self, some other object, or even nil if you want. The argument is NOT the object you wish to add to the collection managed by the array controller.
For your second issue, you're on the right track if you want to customize things or keep a reference to the newly-inserted object. You can use the -addObject: method (which does take the object to add as its argument) but you'll have to tell the array controller to -fetch: (another action w/sender as argument) in order to refresh the controller AND anything bound to its contents (such as a table/outline).