So from what I understand the basic application structure for cocoa programs is an AppController in MainMenu.xib. The AppController then initializes the WindowControllers and ViewControllers which has all the UI logic and in turn load the xib files for the rest of the application. The AppController also creates the Model classes.
My confusion is over how to get the data from the model classes into the views. I would like to use bindings and key-value observing. Should each view controller have a pointer to the AppController and the ModelController? If so how would you do that?
[[ViewController alloc] initWithModel:ModelController];
Is the only solution I can think of and it doesnt seem very clean.
I make a controller that creates/loads and owns the model. My application delegate creates and owns this controller.
That same controller usually also owns the window or view; I rarely use dedicated window controllers, and have never used view controllers. If you do use window/view controllers, the model controller would create and own those, too.
Thus, one controller is responsible for both the model (or some specific portion of it) and the (controller for the) window that displays that (portion of the) model.
A common way to link a NSViewController to a model is to set its representedObject value. There is no need to set a pointer to the AppController, as it can always be obtained through [NSApplication sharedApplication].delegate.
Related
Part of Apples description of the MVC-pattern is: ”The controller updates the model.” I interpret this to simply mean: The controller calls methods on the model, causing its internal state to change.
But for a controller object to call a method on a model object, it needs a reference to the model object. If multiple controllers need to update a model object, we need multiple references to the model object – one for each controller.
I don’t want my controllers to reach out in to global space locating other objects. I want higher level application objects to wire up lower level domain objects.
I’m coming from other plattforms where we use IOC containers for this stuff.
Looking for best practices as how model objects gets passed around in a cocoa application.
A concrete example: If I add CoreData to the cocoa application project template in xCode, the managedObjectContext is instantiated in the app delegate. How do I pass this instance to, for instance, a view controller or a nested view controller?
I’m using Swift.
concrete example: If I add CoreData to the cocoa application project template in xCode, the managedObjectContext is instantiated in the app delegate. How do I pass this instance to, for instance, a view controller or a nested view controller?
So personally I find this an anti-pattern: stuffing 'global' data in the app delegate.
But, that is what is very common on iOS and as you have noticed, what the standard Xcode template does. Unfortunately.
To make this a bit more workable, what I usually do is make the managedObjectContext private to the app delegate. Then when another object, mostly a UIViewController, needs to access this context, I explicitly pass it to the UIViewController when I instantiate it.
And when that view controller needs to display a new view controller, like for example some detail view, I again pass the context to it.
Then at least there a clearer form of responsibility. And it makes testing simpler because view controllers simply need to be configured (or injected in IoC terms) instead of grabbing hard coded global objects.
I know this not a full answer to what you were asking but I thought this bit was the most interesting of your question.
This question may not have a straight answer, but I am curious as to what others think. I have a UIViewController and inside that I have 2 objects a UIView and and UITableView. Each object as it's own custom class. My question is about the UITableView, what object should be the datasource and the delegate. Right now I have the UIViewController set to those rolls, but is it beset practice to set the object itself to be the data source and the delegate, maybe use the method awakeFromNib to set those 2 sources. Are both ways ok? I would think that setting the data and the delegate to the class that represents the tableview would keep that code apart from the view controller, which I would think would be a good thing.. but maybe not...
Good question actually.
Look at the Controller as an example of the Mediator Pattern from the Gang of Four: it's mediating the relationship between the View and the Model. Most of the web world has gone to using binding of some sort, so you typically see model objects 'bound' directly to the view layer, e.g. a text edit field might have an expression indicating which field in the domain object it is editing, then the framework will provide the services of marshaling and unmarshaling that data.
In Cocoa, you typically don't do that: you bind to properties in the Controller, and those typically are then used to transform the underlying domain class. So for instance, if you make a storyboard and make a form and have a custom controller, now you want to edit the name of some entity, e.g. User, you would CTRL-drag from the edit box over to the source, it would make an outlet, which would then give you control not only of what appears in that box, but the control itself. Then you could add a submit button and CTRL-drag to create a method, for Save, and when that's clicked, you could update the underlying User instance.
If I am using an instance of NSArray to populate a pop-up button, where in terms of MVC does that NSArray need to be initialised? I'm guessing it would fall under Model, however if that's the case, how do I initialise the array? Do I start a new implementation file to contain the array? (Obviously don't want to use my app delegate file as that would fall under Controller, not Model.)
The "model" part of MVC is the data that the app stores, presents, and/or allows the user to manipulate. It would largely be the same whether your app was running on a Mac, an iPhone or whatever. The "view" is the UI. That is the things the user actually sees on screen. The controller is the part that goes in between these two. It's responsible for implementing the specific behavioral logic for the app as well as "gluing" the view layer to the model layer.
So, with that said, the array of items to be displayed in a popup button may or may not be part of the model. It entirely depends on the specific UI you're implementing. If the selection is between a number of objects represented in the model, the array's contents would indeed be part of the model, but it still might be that the controller pulls the items out of the model in another form and turns them into an NSArray. It might also be a way to select between e.g. a fixed list of actions to be performed, in which case it's more properly part of the controller layer itself.
In other words, there's no one answer to your question. But, the likelihood is that the controller will at least provide the array in question to the UI, and may also be entirely responsible for its content. It all depends on exactly what you're trying to accomplish.
The initialization would happen within the model object, but that initialization would likely be called from a view controller (I wish these were just called controllers--there's no ModelController class.) Possibly in viewDidLoad but really wherever the best fit for your use case would require.
The model object should initialised by the controller object, usually in the viewDidLoad method. If a model object is owned by another model object (for example, if your custom model object has an NSArray instance variable, then your custom object is the parent and the NSArray is the child), then that child model object should be initialised in the initialised method of the parent model object.
I suppose your NSArray is a model object on its own, so it should be initialised in the viewDidLoad method of the controller object.
This is just one answer and not necessarily how everyone develop applications in objective C.
If I have an app with a small data model or with models scoped to their views, I will put the models on the AppDelegate or in the viewControllers themselves, if they are limited in scope to that view.
They will be initialized closest to where it makes sense in the app for that data.
Sometimes you will see a "FAT" viewController which represents a home screen controller or main screen controller and folks will pile the models on that class. Its very common.
But if I have an application with a large data model - lots of models that have lifetimes not scoped to the life of a view - then I will create a class in my application called *myAppNameHere*AppModel, and I will centralize the storage of application models, and use service classes as necessary to request data to populate/update models.
This is just one approach. And great question!
So far I have only been updated a view from within its controller. I am now in a different situation where I need to update an element in a view (a text label) based on some event occurring in another class C of my application. I realize this is basic but I am unclear what is the proper way to handle this.
Should I be passing my view controller in the init method of C? (Seems cumbersome to be passing in the view controller whenever I init that class C. Would require a bunch of refactoring too.)
Should I retrieve the app delegate from within C by calling [[UIApplication sharedApplication] delegate] and from the app delegate retrieve the view controller that I need?
Neither approaches strike me as practical or elegant.
Is there a better way?
The two most common methods are delegate and notification.
In one case you give class C a delegate property and set that to be the view controller. To make it look good, you can define a delegate protocol similar to what's done for a lot of API classes. It could be passed in init but it's standard to just set the property directly.
The other way is to have the view controller listen for a notification that class C sends when the event occurs. The view controller then makes the update when it receives notification.
I'm developing a Mac Application. The application has a common source view on the left and a detail view on the right which is the main part of the whole window.
It's like a Master-Detail relationship, but each element in the source view require another detail view. In fact, I have designed a specific NSViewController for each element in the source view.
If I'm switching between these NSViewControllers, that means If I select another element in the source view, I remove the current view and add the view of the newly selected NSViewController. Everytime I change the NSViewController, its state will be lost. When the user comes back to that NSViewController, he has to start over.
My question now is: How can I save the state of the NSViewController, so that I can switch between these without losing its states and can continue where I have left?
Two considerations about your problem:
Keep model data in model classes. This means that you can always recreate a view controller and set its represented object provided the model classes have kept the changes made via the view controller. When you need to instantiate a view controller, set its represented object to (a representation of) a model class.
When removing a view from its superview, you do not necessarily need to release its corresponding view controller. Instead, you can keep strong references to all view controllers in your window controller/application delegate, so no state is actually lost.
Use NSArchiver. Implement archiving/unarchiving in your dealloc/init methods and store each view controller's state in a file named after the class (if you have one item per view controller policy). Otherwise think of some simple naming convention and use it.