First Steps with Cocoa Bindings


Most programmers are familiar with the model-view-controller pattern, or MVC for short. Model objects provide the core program logic; view objects provide a visual representation of the data and controls for the user to manipulate; and controller objects comprise the mess between, coordinating the model with the view. The controller layer updates the view when the model changes, and updates the model in response to the user's input. With the addition of Cocoa bindings in OS X 10.3, Apple has provided developers with a technology that can minimize --- and in simple cases even eliminate --- writing controller code...



I'd read some of Apple's documentation on Cocoa Bindings as well as a great article by Michael Beam titled The Cocoa Controller Layer. I worked through a preferences example, but I still didn't really have a feel for this; I needed to go exploring on my own. I had a small app coded up using Cocoa's target/action scheme, and thought I'd see what it'd look like coded with bindings instead.

In this very simple app, the controller and model code were munged together in the same class. I'm no purist, at least not for little one-off apps that no one but me will ever see. The basic approach with Cocoa's target/action isn't fundamentally different than what we'd see in MFC or even .NET WinForms: you end up with some class members that hold references to controls in the view, such as buttons and text fields, allowing the controller code to gray out the buttons or initialize the text fields. You also have controller code to handle events from the view, such as button clicks or a key presses. Setting this stuff up is very different between Cocoa and MFC and .NET, but strip it down to the basics and that's what you have.

For my small app, I was able to dispose of my controller code almost entirely using Cocoa bindings.

I created an instance of NSObjectController in Interface Builder and associated it with my model object. Since my model object was also instantiated in Interface Builder, all I needed to do to associate the controller with the model was to Ctrl-drag between their icons, and click a button to set the controller object's content member to my model object. After that I used the Bindings tab in Interface Builder's inspector to bind each control to data members in my model object.

The code that was left didn't have NSTextField pointers to the text fields, nor a NSProgressIndicator pointer for that control. A little code remained to animate an on-the-fly change in the window size, but beyond that it was just my model and its data. At runtime, changes to the data result in changes to the controls, and vice-versa; all with no visible code whatsoever.

Besides the obvious benefit of not having to write and debug the controller code, this also left me with a class far easier to unit test. Of course, had I designed properly in the first place, the controller layer would've been off in its own class, but in practice I've seldom seen GUI controller code separated this well from model code. I'm used to tools that let me draw a dialog and create a class pre-populated with members to access its controls, and too often I've yielded to the temptation to do too much work in that class.

The downside of bindings is that it's difficult to troubleshoot what you can't trace. My first attempt didn't work because I forgot to set the controller object's content member to my model object, and it took some poking around to figure out what I did wrong. Another problem is that bindings are fairly new to OS X; if you use them, you're limited to OS X 10.3 and up.

It still makes sense to have the target/action pattern available for some tasks, such as reacting to button clicks. But for moving data between the model and view, Cocoa bindings look pretty slick.

Posted: Thu - March 24, 2005 at 05:24 PM          


©