MVC theory/Cocoa: where to create model? - cocoa

I have an AppDelegate, an NSWindowController and some Views. What is the best place to define DataModel * data;, initialize [[data alloc] initWithSomething] etc.?
I suppose it must be done in WindowController (.h and .m), since model must be handled by the main controller. Am I right? I've got a small doubt about keeping a pointer to Model in AppDelegate...
Sorry if my terminology is not MVC-correct, I'm just studying...

Related

Should an NSTableView's NSTableViewDataSource and NSTableViewDelegate be in a separate NSViewController from controller for rest of NSView?

I have an NSView, "CurrentActionsView", that contains buttons, text and a NSTableView. A class, CurrentActionsViewController, which is an NSViewController, handles the initialization of all the controls -- except for the NSTableView ("myTableView"), which has its own custom class object in the .xib ("myTableViewController"). That maps to a class which is simply:
#interface ActivationTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
My question is this:
Should I just add NSTableViewDataSource and NSTableViewDelegate to the CurrentActionsViewController class, which is already an NSViewController, so there is only one controller responsible for handling all the objects in the CurrentActionsView?
Because I presently have these two separate controllers in two separate files, it's a complex path for the CurrentActionsView to reach into myTableViewController when it needs to re-initialize and reload data in the table.
I usually start by just having the view controller serve as the table view's delegate and data source; it's not a bad starting point.
As your app gets more complex you might find that your view controller's getting too many responsibilities and you might want to extract out an object that specifically deals with the NSTableView.
If you do set up your view controller as a table view delegate and data source just be careful not to too tightly couple your table view-related code to the rest of the view controller; at that point it might become difficult to extract it out.

iOS layout; I'm not getting it

Well, "not getting it" is too harsh; I've got it working in for what for me is a logical setup, but it does not seem to be what iOS deems logical. So I'm not getting something.
Suppose I've got an app that shows two pieces of information; a date and a table. According to the MVC approach I've got three MVC at work here, one for the date, one for the table and one that takes both these MCVs and makes it into a screen, wiring them up.
The master MVC knows how/where it wants to layout the two sub MVC's. Each detail MVC only takes care of its own childeren within the bounds that were specified by the master MVC. Something like:
- (void)loadView {
MVC* mvc1 = [[MVC1 alloc] initwithFrame:...]
[self.view addSubview:mvc1.view];
MVC* mvc2 = [[MVC2 alloc] initwithFrame:...]
[self.view addSubview:mvc2.view];
}
If the above is logical (which is it for me) then I would expect any MVC class to have a constructor "initWithFrame". But an MVC does not, only view have this.
Why?
How would one correctly layout nested MVCs? (Naturally I do not have just these two, but the detail MVCs have sub MVCs again.)
Thanks all for replying. I will study the links that were provided.
Let me try to explain my issue one more time, hopefully to making it more clear. Do note that I already figured out that my view does not match iOS's, since I do not like where my code is going.
Yes, I'm calling a UIViewController an "MVC", since it for me at the moment implements all aspects of a MVC; it has controller code and an embedded view, plus the controller usually also holds and provides the data (all TableView examples implement it like this).
MVC can be present on many levels; basically a UITextField could (should?) be a MVC; there is a view, but also controller logic involved that you do not want to mix with other code. Encapsulation. For example: Java's Swing JTextField has a MVC. So does a JTable, JList, ... Multiple MVC patterns nested in other MVC's to build a whole screen.
This what I expect when some platform says it uses the MVC pattern. So When I coded the table, I created a MVC and only send the loadData message with a date as the parameter to it. It needs to take care of the rest itself. I have a Detail MVC that can slide in; I then tell it the object it needs to show and it needs to take care of the rest itself. Encapsulation.
So I have a lot of UIViewControllers with embedded UIViews. And that is not the way to do it...
One more potential link is the great talk from WWDC 2010 on MVC.
http://developer.apple.com/videos/wwdc/2010/
It is Session 116 - Model-View-Controllr for iPhone OS
The session is chock full of practical advice on how MVC really works, what makes it tick, why it's good. But it also has a lot of intro stuff to help folks new to the concept to wrap their heads around it.
If I understand your sentence on Java's Swing classes above are you talking about the anonymous classes that respond to events? If so those are not "MVC's", they are what is termed 'Observers', when they observe an event from the view they take some action (usually send a message to a controller). Cocoa Touch uses the Target/Action paradigm (and delegation) to achieve this.
I'd also strongly suggest you take Matthew and Stephen's advice and write a bunch of code. If you don't build that base of intuition, asking the right question (which is most of what is needed to get a good answer) is very difficult.
I really think the WWDC 2010 talk will help.
Good Luck!
If I understand your question -- and I may not, see my comments on it -- I think you're applying the MVC design pattern far too granularly. Most commonly in the setup you describe you'll have a single Model, a single Controller, and multiple Views that are grouped/combined, as in a .xib file.
In Cocoa Touch terms you'd have one UIView that contains a UILabel with the date and a UITableView for your table. These are your Views.
You'll certainly have a Model for the table data, likely an array of data. Your date data might be from its own model if it's a date retrieved from something or calculated or whatever, something entirely separate from the array of data. If it's instead associated with the array data -- they're both pulling from a database, or the date is calculated from the array data, or what have you -- then you have a single Model.
If the data is all coming from a single Model then a single Controller is likely fine. Even if the data is coming from more than one source/Model you likely only need/want one controller in this setup. The UITableView will have a UITableViewController, and that same controller can take care of providing your date as well.
To sum, the Model View Controller design pattern doesn't call for having a bunch of nested sets of models, views, and controllers. They could be, and sufficiently complex projects may call for it. Broadly, though, you'll have a controller that's associated with a model and one or more views, and that set of objects works together to provide a piece of functionality.
Tbee,
I'll post a tiny code example here, since it seems you're not really getting it.
#interface MyView : UIView
#property (retain) IBOutlet UIButton *button1;
#property (retain) IBOutlet UIButton *button2;
#property (assign) bool myData;
-(IBAction) doButton1:(id)sender;
-(IBAction) doButton2:(id)sender;
#end;
#implementation MyView
#synthesize button1 = _button1;
#synthesize button2 = _button2;
#synthesize myData = _myData;
// I'm leaving out the initWithNib, viewDidLoad, etc.
- (IBAction) doButton1:(id)sender
{
// do something as a result of clicking button1
_myData = YES;
}
- (IBAction) doButton2:(id)sender
{
// do something as a result of clicking button2
_myData = NO;
}
#end
Connect those up in InterfaceBuilder, and you've got a working "MVC." You don't need a completely new UIViewController for each button. The one for the View takes care of it.
UITableView and it's associated Views are more complex, and may require an additional UIViewController to help encapsulate. I really don't suggest starting out by using them, but this is a good tutorial here. It's got a lot of images which will show you how to connect things up in IB and the like. It's old, so your XCode may not look like the images, but it helps.
Thanks for the links, I'll look into them.
So far I've rewritten most of my application to using views instead of viewcontrollers (except the toplevel one) and it starts to match up with the API calls that are available like layoutSubviews. What I find disturbing that I need to do this now:
[tableDataSource loadData:date];
[tableView reloadData];
Where in my previous setup all I did was:
[tableViewController loadData:date];
But apparently that is the way to do it. One thing is unclear to me ATM. Since I construct and layout the view in loadView in my AppViewController, how do they get relayouted if the orientation changes. The VC does not have a layoutSubviews, so I should use the didRotateFromInterfaceOrientation and reposition the subviews from there?
BTW, I'm not mixing registering anonymous inner classes as listeners (observers). I'm very experienced with writing Swing components and JavaFX controls. And that probably is the culprit, in Java(FX) every component has a view and a controller (not always a model).

Why do UIViewControllers have xib files and UIViews do not?

When I create a new UIViewController in xcode, it offers to make me an associated nib for the interface. However, when I create a UIView, it does not. If my understanding of MVC is correct, views should really be the parts that contain the interface elements (i.e. the nib) while view controllers are the parts that hook the functionality to the views they control.
I'm sure I'll be able to get it working together either way, so this is more of an exploratory question.
This seems like a case where I'm missing some fundamental understanding of how these two parts should be used. What am I missing?
Because UIView is usually not used in such way.
However How do I associate a nib (.xib) file with a UIView?
The answer I eventually got that satisfied my interest was roughly this:
The job of a view controller is to manage a view hierarchy. Interface Builder is an excellent tool for creating view hierarchies. Xcode offers to create a .xib when you create a new view controller because chances are high that you'll want to load the controllers' views from a .xib.
.xib files aren't necessarily meant to hold every UIView (or subclass) that goes into the view, just a general outline of views that don't change during the life of the view. The other UIViews are much easier to create programmatically since they change often.
I had a similar confusion. Basically (according to the MVC) the View is contained inside the Controller. In the iPhone this means that UIViewController has a property called 'view' which is a UIView instance.
A XIB file (and this is not mentioned often) is a serialised UIView instance. It is roughly an XML sub format which represents a UIView with all its subsequent views. So when you create a UIViewController, a UIView is created in the form of a XIB and bounded to that controller.
A new UIView therefore does not have a XIB because they are essentially the same thing...

How should a custom view update a model object?

This is a Cocoa n00b question - I've been programming GUI applications for years in other environments, but now I would like to understand what is "idiomatic Cocoa" for the following trivialized situation:
I have a simple custom NSView that allows the user to draw simple shapes within it. Its drawRect implementation is like this:
- (void)drawRect:(NSRect)rect
{
// Draw a white background.
[[NSColor whiteColor] set];
NSRect bounds = [self bounds];
[NSBezierPath fillRect:bounds];
[[NSColor blackColor] set];
// 'shapes' is a NSMutableArray instance variable
// whose elements are NSValues, each wrapping an NSRect.
for (NSValue *value in shapes)
{
NSRect someRect;
[value getValue:&someRect];
[self drawShapeForRect:someRect];
}
// In addition to drawing the shapes in the 'shapes'
// array, we draw the shape based on the user's
// current drag interaction.
[self drawShapeForRect:[self dragRect]];
}
You can see how simple this code is: the shapes array instance variable acts as the model that the drawRect method uses to draw the shapes. New NSRects are added to shapes every time the user performs a mouse-down/drag/mouse-up sequence, which I've also implemented in this custom view. Here's my question:
If this were a "real" Cocoa application, what would be the idiomatic way for my custom view to update its model?
In other words, how should the custom view notify the controller that another shape needs to be added to the list of shapes? Right now, the view tracks shapes in its own NSMutableArray, which is fine as an implementation detail, but I do not want to expose this array as part of my custom view's public API. Furthermore, I would want to put error-checking, save/load, and undo code in a centralized place like the controller rather than have it littered all over my custom views. In my past experience with other GUI programming environments, models are managed by an object in my controller layer, and the view doesn't generally update them directly - rather, the view communicates when something happens, by dispatching an event, or by calling a method on a controller it has a reference to, or using some similarly-decoupled approach.
My gut feeling is that idiomatic Cocoa code would expose a delegate property on my custom view, and then wire the MyDocument controller object (or another controller-layer object hanging off of the document controller) to the view, as its delegate, in the xib file. Then the view can call some methods like shapeAdded:(NSRect)shape on the delegate. But it seems like there are any number of other ways to do this, such as having the controller pass a reference to a model object (the list of shapes) directly to the custom view (feels wrong), or having the view dispatch a notification that the controller would listen to (feels unwieldy), and then the controller updates the model.
Having a delegate is a cromulent way to do this. The other way would be to expose an NSArray binding on the view, and bind it to an array controller's arrangedObjects binding, then bind the array controller's content binding to whatever owns the real array holding the model objects. You can then add other views on the same array controller, such as a list of objects in the active layer.
This being a custom view, you'll need to either create an IBPlugin to expose the binding in IB, or bind it programmatically by sending the view a bind:toObject:withKeyPath:options: message.
There is a very good example xcode project in your /Developer/Examples/AppKit/Sketch directory which is a more advanced version of what you are doing, but pertinent nonetheless. It has great examples of using bindings between controller and view that will shed light on the "right" way to do things. This example doesn't use IB Plugins so you'll get to see the manual calls to bind and the observe methods that are implemented.
there are several similarities between your code and an NSTableView, so I would look at maybe using a data source (similar to your delegate) or even perhaps bindings.

Shared Objects in Cocoa

I'm working with CoreData in Cocoa (not document-based).
My problem is, that I want to access the SAME NSArrayController from different NIBs.
But every NIB has an own instance of this NSArrayController.
My question is now how I could generate sharedObjects (like the NSUserDefaultsController).
It would help me a lot. Thanks for your answers. =)
You generally don't want to share an NSArrayController between nibs. It's probably better to have multiple NSArrayController (one per NIB) which are all bound to the same underlying model. If you want this model (e.g. an NSArray) to be application global, you can expose it via the NSApplication's delegate (e.g. instantiate your custom MyAppDelegate class in MainMenu.nib and connect the NSApplication's delegate outlet to the instance of your MyAppDelegate class). In other NIBs, you can then bind an NSArrayController's contentArray binding to Shared Application.delegate.myArray (assuming MyAppDelegate exposes—via KVC-compliant methods—an NSArray binding called myArray). You are essentially using IB and the MainMenu.nib to create your singleton instance of MyAppDelegate.
Keep in mind that this approach makes unit testing your application difficult, since there are now singletons in the object graph that you can't mock or stub out during testing. It would be much better to create an NSWindowController or NSViewController for each secondary (non MainMenu.nib) NIB and bind the NSArrayControllers in those nibs to File Owner.myArray. You can then instantiate the NSWindowController or NSViewController, passing it an array (or array KVC-compliant object) before loading the secondary NIB. In this way, you can test the functionality of the nibs in isolation (using a mock or stub for the array).
I'm not really sure trying to reuse NSArrayController is the best choice (I'd need to know more about your project, but I've never ran into a situation where I'd do something like that), but you can use a static variable inside a class method like so:
+ (id)sharedObject;
{
static id object = nil;
if ( object == nil )
{
object = [[self alloc] init];
}
return object;
}
Keep in mind that this is not a true singleton, since you can still allocate additional objects of that class. You can use this guide if you really want to be strict.
Matt Gallagher has a good post on singletons and other ways to have "global" data over on his blog you may want to check out too. It's a little more clear than Apples documentation, and has a link to a header file that makes it nice and easy to create singletons out of almost any Cocoa class.
I'm actually using his header file in some of my projects, and it works great.

Resources