is a seriously cool technology that is part of Cocoa
, the NeXTStep
derived environment that is one of the ways of developing applications on OS X
. Bindings are what made me a Cocoa convert. I am in no way trying to provide a complete reference to bindings, Apple's documentation is there for that. This is just a gentle introduction to what is hiding behind the name. Bindings are only available on OS X 10.3
MVC (Model View Controller) is one of the fundamental design patterns in Cocoa. In a nutshell, the model manages your data, implements your application's custom logic and so on. The view displays your data and lets the user interact with it. Finally the controller mediates behind the two, for example given the view access to data from the model. It helps to ensure the reusability of the model and the view because they do not need to know about each other.
So what do bindings have to do with all this? Bindings are about saving much of the work involved in writing the controller layer. The controller typically has a lot of fairly tedious "glue code" that keeps objects in sync. For example in a web browser you need to make sure that the back button is only enabled when it is actually possible to go back. If you have an address book type application, you might have a list of people in one part of the window and on the other side edit fields that allow you to modify the currently selected person. Your controller has to do things like changing the contents of the edit field when the selected person is changed or propagating changes made by the user to the model layer.
All this is fairly repetitive and often non reusable code, that gets written again and again. Bindings are about doing away with that glue code, with the use of a bit of magic and some useful controller objects.
Setting up the magic
When using bindings, one creates a 2 way relationship between what we call a binding of an object to some property of a controller. As a quick taste of what a binding can be, those of a textfield include enabled, editable, or value and those of a popup menu include content (the menu items) and selectedObject. In the previous example one could bind the enabled binding of the back button to the "can go back" property of the web browser view. The view doesn't need to know if there is a back button or not. There could be multiple objects bound to the "can go back" property (for example a button and a menu item), but to the web view this is irrelevant, whenever the "can go back" property changes, the enabled status of the button and the menu will change.
The address book example shows the 2 way nature of the relationship better. Here one would have an array of objects representing the people, and a object (provided by the Cocoa framework) known as an array controller. The edit field that is supposed to contain the selected person's name would have its value binding bound to the array controller with the key path selection.name. A key path like selection.name is a period delimited list of properties, this particular one means the name property of the selection property of the object we have bound to (i.e. the array controller). Or in plain english we have bound the value of the text field to the name of the selected person. The connection here is truly 2 way: changes to the contents of the edit field update the data held by application, and changes to the data update the edit field. The array controller also manages things like changes of selection and insertion and removal of objects, for example we can bind the enabled binding of a "delete person" button to the canRemove property of the array controller and it will automatically be enabled if a person is selected.
So how does one go about setting up bindings? It is posssible to do it through code, the method bind:toObject:withKeyPath:options: does exactly what its name suggests. However usually one sets up this sort of thing in Interface Builder. From the bindings section of the text field's info palette one would simply click on the binding we wish to establish and select the object we are binding to and type in the key path. There are also a few other controls which allows one to set various options.
Often the data we can bind to does not quite match the data we actually need. When boolean values are involved we are sometimes interested in the negated value. Sometimes we are not really interested in the value itself, just whether the value exists. In yet another case our model might store temperature in Kelvin, but it might be more convenient for the user if the value was displayed in Fahrenheit.
All of these problems can be solved by the use of a value transformer. When one creates a binding, one can optionally specify one of these, and data going from one object to the other object automatically goes through this transformer.
It would of course be possible to accomplish this without the use of transformers. Our application that displays a temperature could be written so that we can bind both to a temperature in Fahrenheit property and to a temperature in Kelvin property, but this complicates our model unnecessarily. Also we would need to add such properties to whatever objects had temperatures to display, whereas one need only write the value transformer once.
There are currently are 3 controller classes provided by Apple: NSArrayController, NSObjectController and NSUserDefaultsController.
As one might guess, NSArrayController is used for managing an array (to be quite precise, an NSArray). It makes a very good match for a table view. It also possesses what are known as operators. If our array represented a list of products, with associated costs, one might wish to have a field displaying the cost of the currently selected objects. All one would have to do is bind the value of that field to the array controller, with the key path selection.@sum.cost (assuming that cost is the property of the objects in the array giving the cost of the item). The @sum in the key path is the operator, of which there are many others.
NSUserDefaultsController is a controller which enables you to bind things to your application's preferences. NSObjectController is basically there so that you can bind to any object you want to.
Fear not gentle programmer, more controllers are on their way.
Behind the magic
So far I've glossed over the technical details. Some of you may be wondering how on earth objects can automatically notice that another object has changed. The core technologies behind this is key value observing and involves non-negligeable amounts of black magic and generally you aren't losing much if you don't know about the details of the implementation. Behind the scenes the framework is doing funky things at runtime, so that it notices calls to setFoo and thus changes to foo. This won't pick up changes that don't use the accessors, but if you aren't using accessors then you are naughty and deserve a spanking.
Key Value Binding is about creating the binding relationships between objects and things like seeing which bindings an object exposes. It is used extensively by the frameworks, but more often than not it is enough to set up bindings graphically via Interface Builder and let it worry about this.
Key value coding (KVC) however is important to know. In order for you to be able to bind to an object's property, the object must be KVC compliant for that property. What this means in practice is that you must name your accessor methods according to a simple convention: if your property is called foo, the method to get the value of foo must be called foo, getFoo or isFoo and the method to set the value of foo must be called setFoo (it is possible to tell the bindings system to access instance variables directly). And that's all one needs to do to be KVC compliant. One can note that there doesn't actually have to be an instance variable called foo. Optionally, one can also have a method called validateFoo that gives one the opportunity to check that a valid value for foo is being set and either modify it so that it is acceptable or bail out and tell the user.
Actually I am lying horribly, all you need to do is implement the methods valueForKey:, valueForKeyPath:, setValue:forKey: and setValue:forKeyPath: but NSObject's implementation of those methods will do everything for you if your accessors are named correctly and this is almost always what you want to do.
Why use bindings
The answer is fairly obvious: to write less code, as each line of code is a potential bug and an extra line of code to maintain. Furthermore, the code that bindings eliminates is rarely the interesting kind. It does take a while to get into the right frame of mind, understand what's happening and stop making stupid mistakes, but once that is done bindings can be a huge code and time saver.
There are some disadvantages. At first (and even later) bindings seem somewhat mysterious and hard to debug. As a programmer, when something goes wrong, one is used to checking the source code but although bindings can be created programmatically, it is a lot quicker to set them up via Interface Builder, so with bindings the problem will often lie with the way the bindings are configured in the nib file. One corollary is that it can make version control more difficult, one cannot usually hope to diff two revisions of a nib file and get a meaningful result.
There are many things that bindings excel at, for example managing an NSTableView is a doddle. Instead of writing a datasource and a delegate one simply takes an array controller and bind the table view to it and that's about all there is to it. To make things a little more interesting, the array controlled by an array controller doesn't have to be fixed. One of the bindings of an array controller is contentArray, so you can have a table view display the contents of an array which is selected via another table view, and you still haven't written any code.
There are however tasks that bindings are not adapted to, and one of the dangers is trying to shoehorn a non bindings problem into a bindings solution.
Use them wisely and they are a huge timesaver. Go forth and bind!
Sources: Apple's developer documentation and a few months playing with bindings. A must visit page of examples is http://homepage.mac.com/mmalc/CocoaExamples/controllers.html