Coming from the .NET world, it seems strange to me that for whatever reason, bi-directional data binding isn’t supported out of the box in Cocoa.
The various provided standard controllers effectively implement two-way data binding. Take for example NSArrayController's contentArray. When binding NSArrayController's contentArray to a collection in your own data model, the NSArrayController uses Cocoa’s KVO based binding technology to observe changes to your data model. Coming to Cocoa with experience from other data binding technologies, this is pretty straightforward and expected behavior.
NSArrayController also pushes updates to your data model, but not through Cocoa’s binding technology. In the reverse direction, NSArrayController caches the information passed to it when a data binding is established. This cached information is used to force feed updated values to the source of the data binding via Cocoa’s KVC technology. It wasn’t initially clear to me that this was being accomplished “manually” by the NSArrayController and that this bi-directional behavior wasn’t something I could expect to happen automatically when implementing my own controllers.
Apple’s documentation doesn’t touch on strategies to implement the kind of two way communication that their standard controllers evidence that I could find. In fact, Cocoa Bindings Programming Topics doesn’t really have a lot to say on the subject of implementing your own controllers at all. As usual, I found solace via Google and the Cocoa community. In particular I found this clear and brief CocoaHeads México presentation to be a really helpful addition to Apple’s documentation when getting up to speed on implementing custom controllers.
Fortunately, Apple’s sample code does a great job of showing an example of implementing a custom controller that performs this kind of two way communication. The BindingsJoysticks sample demonstrates the pattern of overriding your custom controller’s bind implementation and caching the source object and source key path of the bind.
An advantage of the “manual” implementation of the back flow of data from controller to source in Apple’s sample code is a high degree of flexibility and control over exactly when the source of the data bind is updated by your controller. In fact, you can choose to have your source object and your controller be out of sync from each other for a period of time (or forever) if that floats your boat. This can be pretty useful in various situations (interactive mousing with undo/redo comes to mind), but can also be harder to maintain and downright cryptic to a consumer of the controller.
A fairly obvious downside to “manually” implementing the back flow of data from controller to source is, well, the manual part. The extra typing isn’t so bad if you are implementing your one true controller to rule them all, but if you have a number of properties you want to bind, it can be pretty tedious. Consider for example, implementing in the style of Model-View-View/Model (MVVM).
In MVVM, it’s typical to use data binding to glue your view to your view model without either being the wiser. Having not built a large scale Windows Presentation Framework (WPF) application (whose API is architected around the MVVM pattern) I never really got the Kool-Aid pumping through my veins. Proponents of MVVM believe it makes it easier to iterate on user interface, easier to perform headless automated testing of UI functionality, and provides overall more scalable and maintainable implementations. If you are interested in learning more about MVVM, some links to check out are:
- Martin Fowler’s original Model-View-Presenter (MVP) manifesto that inspired MVVM.
- John Gossman’s original blog post outlining Microsoft’s adoption of a revised version of Fowler’s MVP pattern renamed “MVVM”.
- A Microsoft overview article on MVVM and how it fits into WPF.
In MVVM, there are often a relatively large number of properties across a number of objects that need to be bound to each other. Manually implementing two-way data binding for each such object can be kind of a drag. For that reason, I threw together a little utility class to handle the back flow of values from controller to source (or View to View-Model) for me. The sample code is available at Cocoa Convert on Google Code.
A few caveats before I go on: this is definitely not production code and I use garbage collection. In other words, no effort has been put into retaining objects appropriately and it’s probably a very bad idea to drop this into your own project as is.
An instance of the TwoWayBindingManager class is owned by your custom controller:
#import "TwoWayBindingManager.h"
@interface MyController : NSObject
{
@private
...
TwoWayBindingManager* _twoWayBindingManager;
It is instantiated when your controller is initialized, and should be disposed of when your controller is disposed of (either in a custom dispose method, or when not using garbage collection in a dealloc):
- (id)init
{
if([super init] == nil)
return nil;
_twoWayBindingManager =
[[TwoWayBindingManager alloc] initWithObject:self];
return self;
}
- (void)dispose
{
[_twoWayBindingManager dispose];
_twoWayBindingManager = nil;
}
Your custom controller then needs to override bind and unbind and give the TwoWayBindingManager instance a chance to hook in:
- (void)bind:(NSString *)bindingName
toObject:(id)observableController
withKeyPath:(NSString *)keyPath
options:(NSDictionary *)options
{
[super bind:bindingName
toObject:observableController
withKeyPath:keyPath
options:options];
[_twoWayBindingManager bind:bindingName
toObject:observableController
withKeyPath:keyPath
options:options];
}
- (void)unbind:bindingName
{
[super unbind:bindingName];
[_twoWayBindingManager unbind:bindingName];
}
From the controller side, that’s it! As far as usage goes, you just need to pass in the extra option kWantsTwoWayBinding for any bindings that you want to be bi-directional:
[cont bind:@"someControllerProperty" toObject:self withKeyPath:@"someSelfProperty" options:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], kWantsTwoWayBinding, nil]];
The major trade-off of the TwoWayBindingController approach is less flexibility and control over how closely synchronized the properties of your custom controller are kept to your data source. For situations where you are willing to trade that kind of control for the ease of bi-directional data binding, an approach like the TwoWayBindingController may just save you a lot of typing and bug hunting!
The sample code accompanying this article is available at Cocoa Convert on Google Code.

