Binding through NSApp

Image:Binding through NSApp.png

AppleScript offers a delegate method for NSApp to handle custom keys. That's how you can ask iTunes the name of current track, even though there's no such key in NSApplication — it's made available through application:delegateHandlesKey:

Good news is, you don't have to use AppleScript to enjoy the benefits of extra keys ! NSApp can become a way of sharing instances across NIBs, or a central repository of data.

How ?
First, declare the keys we're using in an array and allocate a dictionary to store their values :

@implementation ApplicationController
- (id)init
	self = [super init];
	if (!self)
		return nil;

	// A NSMutableArray recording keys we're using
	handledKeys = [[NSMutableArray alloc] initWithObjects:@"myCustomKey", @"anotherKey", @"etc", nil];
	// A NSMutableDictionary storing our custom keys' values
	appDict = [NSMutableDictionary new];	

	return self;

Then, tell NSApp what keys we're handling :

- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key
	return [handledKeys containsObject:key];

Finally, implement get and set methods for our keys. Don't bother writing accessors, just implement those in the fallback KVC methods :

- (id)valueForUndefinedKey:(NSString *)key
	return [appDict valueForKey:key];
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
	// Notify change will happen
	[NSApp willChangeValueForKey:key];
	[appDict setValue:value forKey:key];
	// Notify change did happen
	[NSApp didChangeValueForKey:key];

Binding in Interface Builder
Once your delegate handles custom keys, bind in Interface Builder like this :

Image:Binding through NSApp in Interface Builder.png

Update the key with [NSApp setValue:@"hello" forKey:@"myCustomKey"].

Binding manually
For custom controls, call bind like this :

[myControl bind:@"controlKeyPath" toObject:NSApp withKeyPath:@"myCustomKey" options:nil];

Binding or sharing data across NIBs

Binding to NSApp using mainWindow.windowController.document will give access to the current document. To implement an inspector, load an external NIB and bind to that keyPath. Another way is to set a custom key : when selecting an object, update a key containing the selected object [NSApp setValue:selectedObject forKey:@"selectedObject"] and bind the inspector to it.

Using keypaths
To work for keypaths, that is container.values.leafValue instead of just myCustomKey, first create containers :

// Create containers for leaf keys
[NSApp setValue:[NSMutableDictionary dictionary] forKey:@"container"];
[NSApp setValue:[NSMutableDictionary dictionary] forKeyPath:@"container.values"];

Then bind and update just as you would a key :

// Bind 
[myControl bind:@"controlKeyPath" toObject:NSApp withKeyPath:@"container.values.leafValue" options:nil];

// Then update model value and watch it change everywhere it's bound
[NSApp setValue:@"Hello !" forKeyPath:@"container.values.leafValue"];

2010 07 20

I haven't tried it, but can't you just bind to NSApp with a key path of @"delegate.myCustomKey"? No containers needed that way for longer key paths.

Patrick Geiller
2010 07 20

You can. This post is an offshoot of exploring AppleScript, how it actually makes for (I think) simpler design even when not using it.

Follow me on Twitter
Planet Cocoa

2011 02 22Distance field
2010 07 202Binding through NSApp
2010 05 122Forwarding invocations
2010 02 272Core Image black fringes
2010 02 21Quickest Way to Shell
2010 02 08Who's calling ?
2009 09 2138 ways to use Blocks in Snow Leopard
2009 08 182Bracket Mess
2009 08 124Taming JavascriptCore within and without WebView
2009 04 15Debugging with Activity Monitor
2009 03 25How Core Image Color Tracking works
2009 03 1510Custom NSThemeFrame
2009 03 10Which framework is running ?
2009 03 074CoreUI can paint pretty big
2009 02 18Localization with functions
2009 01 30Did you forget to nest alloc and init?
2009 01 16JSCocoa on the iPhone
2009 01 11Mixing WebView and JavascriptCore
2009 01 09Badge overflow
2009 01 09Find your Garbage Collection leaks with Instruments

Powered by MediaWiki