Always use notification name globals, not literal strings!
userpic
[info]chanson
What's wrong with this code?
- (void)registerForNotificationsFromTask:(NSTask *)task ( {
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(taskDidTerminateNotification:)
               name:@"NSTaskDidTerminateNotification"
             object:task];
}
If you didn't notice anything wrong, look again.

What's bad about this is that it's passing a string literal instead of a global variable for the notification name. The code should really look like this:
- (void)registerForNotificationsFromTask:(NSTask *)task ( {
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(taskDidTerminateNotification:)
               name:NSTaskDidTerminateNotification
             object:task];
}
Isn't that better? (Among other things, Xcode will offer to complete the NSTaskDidTerminateNotification global variable for you — unlike the contents of a string literal.)

This is a bug that often results from copying & pasting from documentation into code. "I need this notification, it needs to be a string, so I'll just put @"" around it." The type of a notification name is, in fact, NSString but you don't have to pass a string literal for that. Instead, pass the global variable that exists for each notification name and you're guaranteed that the right thing will happen.

If you're creating and using your own notifications, be sure to follow the Cocoa pattern and create your own global variables containing the notification name. Otherwise you're at the mercy of typos within string literals.

Update: Sanjay Samani helpfully pointed out that by constant string I meant string literal. Thanks, Sanjay! I've updated my post with this correction. (Not sure where my memory was…)

No NSCoder night for me tonight!
userpic
[info]chanson
I'd like to make it to NSCoder Night tonight, but I just have too much else to do today — again.

I won't be seeing anyone at NSCoder Night next week, either, as I'll be in San Francisco for the Apple Worldwide Developers Conference! If you're attending, be sure to find me and say hi!

No NSCoder night for me tonight!
userpic
[info]chanson
I'd like to make it to NSCoder Night tonight, but I just have too much else to do today. Hopefully I'll see everyone there next week!

CocoaHeads Silicon Valley at Apple on Thursday, May 15, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, May 15, 2008 — that's tonight! — at 7:30 in the Garage 1 meeting room at Infinite Loop 4 on Apple's main campus. That's inside and upstairs at Apple's Infinite Loop campus in Cupertino. See the web site for directions.

This month's main presentation is on the Best of Both Worlds — an introduction to Cocoa development by Scott Stevenson.
This talk is a combination of an introduction to Cocoa, as well as a series of advanced tips and tricks that even relatively experienced Mac programmers may not know about.

The idea here is that we want to give all of the people who are new to Mac and iPhone development a chance to get started, but we also want to do something special for our advanced programmers. So rather than choosing one, we're just going to go ahead and do both.
Joel Norvell will also be presenting on how to edit PDF forms using Cocoa — he's done a lot of work with PDFKit and Cocoa, and I'm looking forward to learning from him.

Thanks a ton to Scott Stevenson, Steve Zyszkiewicz, Michael Jurewitz and Joar Wingfors for organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion until it's time to go at 9:30. Often a subset of the meeting moves to BJ's Brewhouse in Cupertino, which is right in front of the Apple Infinite Loop campus on De Anza Boulevard.

No NSCoder Night for me tonight!
userpic
[info]chanson
I'd like to make it to NSCoder Night tonight, but I won't be able to as I'll be looking at a new place. Wish me luck, and have fun everybody!

Build LLVM and clang!
userpic
[info]chanson
I've talked about the LLVM Compiler Infrastructure in the past, but what I haven't talked about yet is just how easy and quickly you can build it on your own Mac running Leopard! This is a great way to get into hacking on compiler lexical analyzers and parsers, code generators, optimizers, and so on.

What's more, you can build both LLVM and the new C front-end clang very easily and in five to ten minutes.

First, create a work area to check them out into, wherever you normally create your projects.
[~]% cd /Projects
[/Projects]% mkdir LLVM
[/Projects]% cd LLVM
[/Projects/LLVM]%
Then check out LLVM itself and clang from the LLVM Subversion repository.
[/Projects/LLVM]% svn checkout http://llvm.org/svn/llvm-project/llvm/trunk llvm
[/Projects/LLVM]% cd llvm/tools
[/Projects/LLVM/llvm/tools]% svn checkout http://llvm.org/svn/llvm-project/cfe/trunk clang
[/Projects/LLVM/llvm/tools]% cd ../..
[/Projects/LLVM]%
Then edit the PARALLEL_DIRS definition in llvm/tools/Makefile to tell it about clang. Just add clang onto the end, like this:
PARALLEL_DIRS := llvm-config  \
                 opt llvm-as llvm-dis \
                 llc llvm-ranlib llvm-ar llvm-nm \
                 llvm-ld llvm-prof llvm-link \
                 lli gccas gccld llvm-extract llvm-db \
                 bugpoint llvm-bcanalyzer llvm-stub llvmc2 \
                 clang
Now create a directory to build into, next to your llvm directory, and change into it.
[/Projects/LLVM]% mkdir build
[/Projects/LLVM]% cd build
[/Projects/LLVM/build]%
This is where you'll actually run configure. This will ensure your source tree isn't polluted with build products, and that everything stays self-contained while you hack.
[/Projects/LLVM/build]% ../llvm/configure --enable-targets=host-only
# lots of logging
[/Projects/LLVM/build]%
You'll note that above I passed an argument to configure. This ensures that LLVM is only built to target the architecture I'm running on, to speed up the build process; this is generally fine for simple front-end development.

Now, to build LLVM as well as clang all I have to do is invoke make. LLVM is set up to correctly do parallel builds, so I'll pass the number of CPUs I have in my machine via make -j 4.
[/Projects/LLVM/build]% make -j 4
# lots of logging
[/Projects/LLVM/build]%
That's it! LLVM is now (hopefully) successfully built. All of the pieces are in the build directory under Debug/bin and Debug/lib and so on; see the LLVM web site for details about what the various components are.

CocoaHeads Silicon Valley at Apple on Thursday, April 17, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, April 17, 2008 — that's tonight! — at 7:30 in the De Anza 3 auditorium at Apple. That's just inside the south side of De Anza 3, right across Mariani Avenue from Apple's Infinite Loop campus in Cupertino. See the web site for directions.

This month's presentation is all about designing and implementing your human interface. User experience and human interface design are critical for Mac OS X software to get right. To that end, there's even going to be a UI makeover as Scott describes in his post on the meeting!

Thanks a ton to Scott Stevenson, Steve Zyszkiewicz, Michael Jurewitz and Joar Wingfors for organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion until it's time to go at 9:30. Often a subset of the meeting moves to BJ's Brewhouse in Cupertino, which is right in front of the Apple Infinite Loop campus on De Anza Boulevard.

Xcode unit testing articles updated
userpic
[info]chanson
I've updated my posts on creating and debugging unit tests for for Cocoa applications and frameworks for Xcode 3.0. If you have any additional questions about how unit testing works in Xcode, don't hesitate to ask!

Here are pointers to the updated articles:

The most significant change is in debugging Cocoa application unit tests. There's an extra environment variable you'll need to add to your executable, DYLD_FALLBACK_FRAMEWORK_PATH, before you'll be able to debug it with Xcode 3.0. This is necessary because Xcode takes advantage of a new linker feature — runpath-relative install names — in order to support moving and renaming your Developer folder and having multiple versions of Xcode installed.

CocoaHeads Silicon Valley at Apple on Thursday, February 7, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, February 7, 2008 — that's tonight! — at 7:30 in the De Anza 3 auditorium at Apple. That's just inside the south side of De Anza 3, right across Mariani Avenue from Apple's Infinite Loop campus in Cupertino. See the web site for directions.

This month's presentation is by Scott Stevenson about Core Animation, one of the great new technologies available to Cocoa developers in Mac OS X 10.5 Leopard.

Thanks a ton to Scott Stevenson and Steve Zyszkiewicz for doing the organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion until it's time to go at 9:30. Often a subset of the meeting moves to BJ's Brewhouse in Cupertino, which is right in front of the Apple Infinite Loop campus on De Anza Boulevard.

CocoaHeads Silicon Valley at Apple on Thursday, January 10, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, January 10, 2008 — that's tonight! — at 7:30 in the Garage 1 conference room at Apple. That's just upstairs from the Town Hall auditorium in Infinite Loop 4, right around the loop at Apple's campus in Cupertino. See the web site for directions.

This month's presentation is by Ted Bonkenburg of Google, who is going to be talking about the MacFUSE project for creating user-space filesystems for Mac OS X!

Thanks a ton to Scott Stevenson and Steve Zyszkiewicz for doing the organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion — especially at BJ's Brewhouse, which is right along De Anza Blvd. in front of Apple's Infinite Loop 1 headquarters building.

Reminder: NSCoder Night tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night is tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

And finally, cocoaCodingNight in St. Louis, Missouri is tonight at the Kaldi's Coffee in Kirkwood, MO from 4:30PM through 10PM. It's tonight rather than Thursday due to the Thanksgiving holiday.

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode.

Reminder: NSCoder Night tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night is tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode.

CocoaHeads Silicon Valley at Apple on Thursday, November 8, 2007
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, November 8, 2007 — that's tonight! — at 7:30 in the Town Hall auditorium at Apple. That's just inside Infinite Loop 4, right around the loop at Apple's campus in Cupertino. See the web site for directions.

This month's presentation is by Deric Horn, Application Frameworks Evangelist at Apple. He's going to take us on a tour through what's new in Cocoa in Leopard!

Thanks a ton to Scott Stevenson and Steve Zyszkiewicz for doing the organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion.

When we haven't had a presentation or two lined up, we've also had some great "unmeetings" (in the spirit of "unconferences") where we came up with an agenda for the core of the meeting on the fly by writing down topics and questions on our room's whiteboard and talking about each one of them for a few minutes. It worked really well.

Update: Every CocoaHeads gets bigger. Tonight's meeting has 59 people so far, which is nearly a 20% increase over last month's meeting!

Reminder: NSCoder Night tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night is tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode.

Reminder: NSCoder Night is tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night starts tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

And, thanks to the magic of time zones, NSCoder Night Washington DC will actually be the first NSCoder Night get-together!

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode. (And also consider joining some of the other channels there, such as #macdev and #macsb, where you can meet other fellow Mac developers!)

New in Leopard: Objective-C Garbage Collection!
userpic
[info]chanson
That's right, Objective-C has garbage collection now in Leopard! And it's not just a conservative collector like the Boehm collector GNUstep supports, either; it's a full-fledged multi-threaded, generational garbage collector! (Though due to the limitations imposed by C semantics, it isn't a copying collector.)

Practically speaking, this means that you don't have to worry about memory management in Cocoa applications the way you used to, if you can require Leopard. You're not entirely freed from worrying about memory management, of course: For one thing, you need to be careful about multithreading, as any -finalize methods you implement will be run in a collector thread. For another, you can still easily over-root objects that should be collectable; this is a common cause of memory leaks in garbage-collected applications, and should be familiar to Java developers.

To address the over-rooting problem, zeroing weak references have been introduced to Objective-C. By prefixing an instance variable declaration with __weak, you tell the garbage collector that if it's the only reference to an object that the object should be considered collectable. And when the object is collected, the instance variable will be set to nil for you!

This works for any number of weak references to a particular collectable object. So long as there are no strong references to an object — which are the regular kind of non-weak references you're used to — an object is eligible for collection no matter how many weak references there are to it. Thus I can create an object, assign it to a thousand different __weak instance variables, and then not assign it to any regular instance variables, and it will eventually be collected and all of those __weak instance variables will be "simultaneously" changed to nil.

Because objects are commonly stored in collections — for example, in static NSMutableDictionary instances that are used as caches — the Foundation framework also has some additional classes that can support weak references. There's NSPointerArray, NSHashTable, and NSHashMap:
  • NSPointerArray is a lot like NSMutableArray, but it can contain arbitrary pointers (not just objects) and also supports nil elements and can hold zeroing weak references.
  • NSHashTable is a lot like NSMutableSet, but it can hold zeroing weak references.
  • NSHashMap is a lot like NSMutableDictionary, but it can hold zeroing weak references for either its keys or its values, and doesn't have to copy its keys.
And most importantly, the behavior of an instance of any of these classes can be customized by creating an instance of the NSPointerFunctions class: That's how all of the above collection classes implement their equality comparison, hashing, and weak-reference behavior. See the header or documentation for details.

One other thing you can do is create hybrid applications and frameworks. As Scott Stevenson shows in his Objective-C 2.0 tutorial, you can build an application or framework such that garbage collection is either supported or required. For frameworks this is particularly useful; after all, you may want to use a framework in both a GC and in a non-GC application. By building it as GC-supported rather than GC-required, you can create a single framework binary that can be used in either.

How does this work? It's simple: When running under GC, the Objective-C runtime will "eat" all -retain, -release and -autorelease messages such that objects never receive them, and objects will never be sent a -dealloc. However, you can still write your code such that it has all of the proper non-GC memory management too. Then when you build it GC-supported, if it's loaded into a process that is using the collector it will use GC, but if it's loaded into a process using reference counting it will behave correctly there too.

There are a couple of things that you can't do with GC. One is that you can't use a non-GC framework in a GC application; an entire process has to be either GC or non-GC. Thus if you use third-party frameworks that haven't been made hybrid, GC-supporting frameworks, you'll need to stick with reference counting until the frameworks are updated.

The other thing you can't do with GC is write code that makes use of GC but that runs on pre-Leopard operating systems. Code built for GC uses certain runtime calls that are not present in earlier versions of the Objective-C runtime, and unfortunately this means that code built for GC requires the Leopard version of the runtime to execute. Leopard contains so many other compelling developer and end-user features, and other Objective-C 2.0 features also require Leopard, so in practice this probably won't be such a huge deterrent.

Update: At jcr's request, I've expanded the discussion of weak references to make it clear that the number of weak references to an object doesn't matter; as long as there are no strong references to it (whether in instance variables, reachable via globals, or on the stack or in registers), it will be eligible for collection.

NSCoder Night Silicon Valley (and hopefully elsewhere)
userpic
[info]chanson
I've been toying with this idea for a while now, and now that Leopard is coming out, it's finally time to make it happen! I call it NSCoder Night! (Major thanks to Scott Stevenson for coming up with the name!) Here's the inaugural announcement:
NSCoder Night Silicon Valley will be having its inaugural get-together between 7:00 and 9:00 PM on October 30, 2007 — next Tuesday night — at Orchard Valley Coffee in Campbell, CA. From then on, we'll be getting together there every Tuesday!

What's the point? To get together with other folks who are also writing Cocoa code for Mac OS X, of course! There's no formal structure, which is why I described it as a "get-together" rather than a meeting. There's no need to RSVP, either; just show up if you like, or don't if you can't make it. Just bring your brain, laptop, and enthusiasm, pick up one of the tasty drinks available at Orchard Valley Coffee, and have a great time!

Why did I pick Orchard Valley Coffee for the inaugural Silicon Valley get-together? I first went there one afternoon during a little vacation I took after WWDC, and I found it a nice, large, relaxing place. It has wireless connectivity, reasonable hours, plenty of room, and great drinks, and it's also quite accessible since it's right in the heart of downtown Campbell.

So come on down, say hi, and see just how much fun it is to hang out and hack for a couple hours!
Want to hold your own NSCoder Night? Just let me know — here, on the NSCoder Night web site, or via email — and I'll post the announcement. Hopefully we'll have get-togethers across the world, even in places where there's not yet a CocoaHeads chapter.

What's the difference between NSCoder Night and CocoaHeads? CocoaHeads is a once-a-month user group meeting, typically with presentations and demos. NSCoder Night is a weekly get-together, a time to talk to your fellow hackers, to get help with issues you're bumping into, to show off cool tricks, and to actually have some time specifically set aside for your side projects.

CocoaHeads Silicon Valley at Apple on Thursday, October 11, 2007
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, October 11, 2007 — that's this Thursday! — at 7:30 in the Town Hall auditorium at Apple. That's just inside Infinite Loop 4, right around the loop at Apple's campus in Cupertino. See the web site for directions.

This month's presentation is a special treat! Greg Robbins and David Phillip Oster of Google will be speaking about the Google Data APIs and the Objective-C Google Data framework that they've created for easy access to it on Mac OS X. It's a great API and they're going to be great speakers, so come on down to Apple and take a look at what they've created. Scott Stevenson has more Google Data API pointers at his Theocacao weblog.

Thanks a ton to Scott Stevenson and Steve Zyszkiewicz for doing the organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion.

When we haven't had a presentation or two lined up, we've also had some great "unmeetings" (in the spirit of "unconferences") where we came up with an agenda for the core of the meeting on the fly by writing down topics and questions on our room's whiteboard and talking about each one of them for a few minutes. It worked really well.

LLVM Developers' Meeting 2007-05
userpic
[info]chanson
The LLVM Compiler Infrastructure is a great technology that came out of the computer science research community and can be used to develop extensible compiler platforms. Among other things, it provides a platform-independent assembly and object code (the "low level virtual machine" that its name is taken from), and a great object-oriented compilation, linking, optimization and code-generation infrastructure that you can use to efficiently target real hardware. The main idea is that LLVM provides a comprehensive back-end that you can easily build a front-end to target.

There's a huge amount of material available on the LLVM web site, including the LLVM Assembly Language Reference Manual and LLVM Programmer's Manual, a wide variety of papers on LLVM, and a great walkthrough of the creation of Stacker, a Forth front-end that targets LLVM. It shows how the budding language creator might leverage the tools available as part of the LLVM infrastructure. I fully expect that in time, "little languages" will have no excuse not to be JIT compiled simply because targeting LLVM is actually easier than writing your own interpreter or bytecode engine! Just walk your AST and generate naïve LLVM code for what you encounter, and let the infrastructure handle the rest. (For those who aren't developer tools weenies, an Abstract Syntax Tree is the internal representation of a program's structure before it's turned into instructions to execute.)

A couple months back, the May 2007 LLVM Developers' Meeting was held at Apple. The proceedings from this meeting — the actual session content, both in slides and in video form — are available online, and I've even created an LLVM Developers' Meeting podcast (including a subscribe-directly-in-iTunes version) for easy viewing. The video may be low bit rate, but it has a 16:9 aspect ratio so you can even pretend it's HD. (I put together the podcast primarily so I could watch the sessions on my Apple TV, since I couldn't attend the meeting.)

So if you're at all interested in compilers, language design or development, optimization, or development platforms in general, you'll be very well-served by checking out LLVM. It is a seriously cool enabling technology.

Unit testing Cocoa user interfaces: Cocoa Bindings
userpic
[info]chanson
About a year ago, I wrote about unit testing target-action connections for Cocoa user interfaces. That covers the traditional mechanism by which user interfaces have typically been constructed in Cocoa since the NeXTstep days. However, with the release of Mac OS X 10.3 Panther we've had a newer interface technology available — Cocoa bindings — which has presented some interesting application design and testing challenges.

Among other hurdles, to properly use Cocoa bindings in your own applications, you need to ensure that the code you write properly supports key-value coding and key-value observing. However, since the release of Mac OS X 10.4 Tiger, the necessary APIs have been available to easily do test-driven development of your application's use of Cocoa bindings, following a trust, but verify approach. (It's also been quite easy from the start to test your support for key-value coding and key-value observing, to ensure that your code meets the necessary prerequisites for supporting bindings. I can write more on this topic in another post if anyone is interested.)

The key to writing unit tests for Cocoa bindings is the -infoForBinding: method in AppKit's NSKeyValueBindingCreation informal protocol. Using this simple method, you can interrogate any object that has a binding for all of the information about that binding! It simply returns a dictionary with three keys:
  1. NSObservedObjectKey, which is the object that the binding is bound to;
  2. NSObservedKeyPathKey, which is the key path that is bound — in Interface Builder terms, this is the controller key path combined with the model key path, with a dot in between them; and
  3. NSOptionsKey, which is a dictionary of additional binding options unique to the binding. These are all of those additional checkboxes and pop-ups in the Interface Builder bindings inspector for setting things like a value transformer.
By specifying what this dictionary should contain for a particular binding, you can describe the binding itself and thus start doing test-driven development of your Cocoa bindings-based user interface. Note that all of the system-supported binding names — as well as the binding option names — are specified in <AppKit/NSKeyValueBinding.h> and are documented, too!

Let's take a simple example, like the one in last year's target-action example, of a window controller whose window has a static text field in it. The field should have its value bound to the name of a person through an object controller for that person. Assume that I've already created the test case and set up some internal methods on my window controller to refer to the contents of the window via outlets, and to load the window (without displaying it) in -setUp just like in the target-action example.

First, to see that my text field has a value binding, I might write something like this:
- (void)testPersonNameFieldHasValueBinding {
    NSTextField *personNameField = [_windowController personNameField];

    NSDictionary *valueBindingInfo = [personNameField infoForBinding:NSValueBinding];
    STAssertNotNil(valueBindingInfo,
        @"The person name field's value should be bound.");
}
Of course, this tells us nothing about how the binding should be configured, so it needs some fleshing out...

Let's check the object and key path for the binding.
- (void)testPersonNameFieldHasValueBinding {
    NSTextField *personNameField = [_windowController personNameField];

    NSDictionary *valueBindingInfo = [personNameField infoForBinding:NSValueBinding];
    STAssertNotNil(valueBindingInfo,
        @"The person name field's value should be bound.");

    NSObjectController *personController = [_windowController personController];
    STAssertEquals([valueBindingInfo objectForKey:NSObservedObjectKey], personController,
        @"The person name field should be bound to the person controller.");

    STAssertEqualObjects([valueBindingInfo objectForKey:NSObservedKeyPathKey], @"name",
        @"The person name field's value should be bound to the 'name' key.");
}
Not very exciting, and a little verbose, but it'll easily lead us through what needs to be set up in Interface Builder for this binding to work. If you want to cut down the verbosity, you can of course extract a method to do the basic checking...
- (BOOL)object:(id)object shouldHaveBinding:(NSString *)binding
            to:(id)boundObject throughKeyPath:(NSString *)keyPath
{
    NSDictionary *info = [object infoForBinding:binding];

    return ([info objectForKey:NSObservedObjectKey] == boundObject)
            && [[info objectForKey:NSObservedKeyPathKey] isEqualToString:keyPath];
}

- (void)testPersonNameFieldHasValueBinding {
    NSTextField *personNameField = [_windowController personNameField];
    NSObjectController *personController = [_windowController personController];
    
    STAssertTrue([self object:personNameField shouldHaveBinding:NSValue
                           to:personController throughKeyPath:@"name"],
    @"Bind person name field's value to the person controller's 'name' key path.");
}
If you're writing code that needs, say, a value transformer, it's a simple matter to extend this model to also check that the correct value transformer class name is specified for the NSValueTransformerNameBindingOption key in the binding options dictionary returned for NSOptionsKey.

You can even extract these kinds of checks into your own subclass of SenTestCase that you use as the basis for all of your application test cases. This will let you write very concise specifications for how your user interface should be wired to the rest of the code, that you can use to just walk through Interface Builder and connect things together — as well as use to ensure that you don't break it accidentally by making changes to other items in Interface Builder.

This is the real power of test-driven development when combined with Cocoa: Because you can trust that the framework will do the right thing as long as it's set up right, you simply need to write tests that specify how your application's interface should be set up. You don't need to figure out how to create events manually, push them through the run loop or through the window's -sendEvent: method, how to deal with showing or not showing the window during tests, or anything like that. Just ensure that your user interface is wired up correctly and Cocoa will take care of the rest.