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.

Interface Builder helps you leverage MVC
userpic
[info]chanson
Kevin Hoffman, The .NET Addict's Blog: A NYC .NET Developer in Steve Jobs' Court - Day 1:
I took a step back and tried to figure out what the hell was going on. After a few minutes, I realized that Xcode was actually doing a really, really, really good thing. The vast majority of problems that arise from a poor separation of concerns between the GUI and the underlying code, model, and controller (if you even have such constructs in your app!) stem from the fact that you can double-click a button and immediately start writing code without thinking about the consequences of such a thing.

The NIB (stands for NeXTStep Interface Builder) file is a loosely coupled, standalone, user interface definition. It wouldn't make sense to double-click a button and immediately be taken to a code-behind. Instead, you have to create a controller class, and then ctrl-drag from the button to the controller and then pick the outlet you're going to use (something like click: or calculate:). To me, as a huge fan of the MVC pattern, this makes perfect sense. And it seems so elegant in its simplicity, and so incredibly cool in the fact that it is truly enforcing good design simply by the way the IDE works.
Kevin sees very clearly what sometimes takes people quite a while to realize: The design of Interface Builder and the design of the Cocoa frameworks go hand-in-hand to help you best leverage a model-view-controller architecture in your application and thus create something maintainable over the long haul. Interface Builder isn't just a "form painter" for a rapid application development environment that's discarded by the "more serious" developers — it's a critical piece of technology that every Cocoa developer can leverage to improve their application's design.

Unfortunately I can't say a whole lot about Leopard and its improvements to Interface Builder here. I can, however, point you to the Xcode 3.0 page on Apple's Mac OS X Leopard Sneak Peek site and the Leopard Technology Series for Developers at the Apple Developer Connection, which includes a great high-level Developer Tools Overview.

Create classes to represent your model objects
userpic
[info]chanson
Collections are quite widely used in a lot of the code I see posted to the Cocoa-dev mailing list and elsewhere. Unfortunately, many times they're not used to represent collections of objects but rather to represent objects themselves. In one recent pathological case, I even saw some code that used an NSMutableArray as a model object instead of the typical NSMutableDictionary, complete with hard-coded indexes to represent the different properties of the object!

Why's this a problem? This goes back to the core tenet of object-oriented programming: In object-oriented programming, you write software by sending messages to objects, and these objects are state plus behavior. When you use a dictionary or an array as a model object behavior goes out the window — the only behavior you'll get out of that model object is that which makes it a dictionary or array, not anything specific to your application.

Thus even if you're only just going to implement accessors to start with, you're generally better off creating classes to stand behind your model objects because you'll have an easier time moving appropriate behavior into them. And if your application targets Leopard this is extra-easy thanks to the new support in Objective-C 2.0 for properties.

Collect 'em all!
userpic
[info]chanson
As of a used book delivery today, I now own three-quarters of all the products that Taligent shipped! The only thing I don't have is the CommonPoint 1.0 for AIX, which did actually ship. (I've seen a CD set for it.) The CommonPoint documentation is online though!

Separation of Concerns
userpic
[info]chanson
Jeremy Miller, The Shade Tree Developer, On Writing Maintainable Code:
Separation of Concerns is the Alpha and Omega of design principles. No other design principle that I'm going to discuss -- be it loose coupling, high cohesion, encapsulation, minimal duplication, or orthogonality -- is possible without Separation of Concerns. Simply put, strive to do one thing at a time in your code. Layering. Divide and conquer. A lot of the other principles are about enabling a system to change with minimal effort and risk. Before you can even think about that, you need to be able to build the system, and then understand that code.
Jeremy hits the nail on the head here: The single most important factor in ease of software maintenance is how well-factored the software is. If its code commonly does more than one thing at once or otherwise mixes concerns, apply the Extract Method, Extract Class, Extract Interface, and Replace Conditional with Polymorphism refactorings liberally until the software is broken up in such a way that each logical unit of code only does one thing.

Also, be sure to check out Working Effectively with Legacy Code by Michael Feathers. (Here's a good review by James Robertson.) It contains a lot of techniques for taking legacy code — which Feathers defines as any code without unit tests — and breaking it up in well-defined ways into code for which it's possible to write unit tests so further, safer refactoring can be done more quickly. It's based on this paper, Working Effectively with Legacy Code, available from the ObjectMentor web site.

Leverage Cocoa patterns
userpic
[info]chanson
David Aames posted on the Cocoa-Dev mailing list asking for some general development tips for managing complexity in the project he's working on. I responded pretty comprehensively based on my experience dealing with complex Cocoa projects, but I wanted to call out part of it here because I think it's a subtle point that a lot of people could leverage. (Note that I've cleaned it up just a little from what I posted. I'm a compulsive editor, what can I say?)

The Cocoa frameworks are built around a number of common design patternsmodel-view-controller, target-action and chain of responsibility, delegation, notification and observation, etc. — and structure your code along the same lines. That will keep your cognitive load low as you work with your code, in that the only thing you'll really have to worry about is what's unique about your code, since its interaction with the rest of both your code and the frameworks you're using all follow common patterns.

As an example, say you want a view that displays a hierarchical diagram of some data in your application. One way to do this is to tie your view very tightly to the data it's displaying — perhaps even to the point of having the view act as the "container" for that data. However, if you do this, you'll have to remember everything about this code and how it works every time you want to change it. That's a pretty high cognitive load.

Instead, with probably about the same amount of effort, you could implement your view in such a way that it uses an arbitrary cell to actually draw its data, and it's designed to be bound to an NSTreeController (and/or uses the NSOutlineView data source informal protocol) to obtain the data to display. This may result in a slightly more complex internal design, but it's likely to be both more reusable and easier to maintain because much of what it does goes through standard interfaces. In other words, the only code in the view is the code that makes it unique and the code that hooks it into the rest of Cocoa.

Unit testing Cocoa user interfaces: Target-Action
userpic
[info]chanson
It's really great to see that a lot of people are adopting unit testing for their projects and dramatically improving their quality. Test-driven development and agile development methodologies built around it are really taking off. However, a lot of people still feel that their user interface is difficult to test through code, and either requires a capture-playback tool or requires a different design approach based heavily on interfaces/protocols to get right.

In last year's post Trust, but verify. I tried to dispel some of the mystery of testing your application's user interface when using the Cocoa frameworks. However, I've still had a lot of (entirely well-justified!) requests for examples of how to put it into practice. So here's a simple example of what I'd do to write a unit test for a button in a window that's supposed to perform some action.

First, when implementing my window, I'd follow the standard Cocoa pattern of having a custom NSWindowController subclass to manage my window. This window controller will have an outlet connected to each of the views in the window, and will also wind up with a private accessor method — used only within the class and any subclasses, and in testing — for getting the value of each of its outlets. This design flows naturally from the test which I would write to specify that the window should contain a button. First, here's the skeleton into which I'd put tests:
// TestMyWindow.h

#import <SenTestingKit/SenTestingKit.h>

@class MyWindowController;

@interface TestMyWindow : SenTestCase {
    MyWindowController *_windowController;
    NSWindow *_window;
}
@end

// TestMyWindow.m

#import "TestMyWindow.h"
#import "MyWindowController_Private.h"

@implementation TestMyWindow

- (void)setUp {
    // MyWindowController knows its nib name and
    // invokes -initWithWindowNibName: in -init
    _windowController = [[MyWindowController alloc] init];

    // Load the window, but don't show it.
    _window = [_windowController window];
}

- (void)tearDown {
    [_windowController release];
    _window = nil; // owned by _windowController
}

@end
That's the infrastructure into which I'd put my other test methods for this window. For example, I'll want to specify the nib name for the window controller and ensure that it actually knows its window:
- (void)testNibName {
    STAssertEqualObjects([_windowController windowNibName], @"MyWindow",
      @"The nib for this window should be MyWindow.nib");
}

- (void)testWindowLoading {
    STAssertNotNil(_window,
      @"The window should be connected to the window controller.");
}
Now let's check that I have a "Do Something" button in the window, and that it sends an action directly to the window controller.
- (void)testDoSomethingButton {
    // _doSomethingButton is a private method that returns the button
    // conected to the doSomethingButton outlet
    NSButton *doSomethingButton = [_windowController _doSomethingButton];
    
    STAssertNotNil(doSomethingButton,
      @"The window should have a 'Do something' button.");
    
    STAssertEqualObjects([doSomethingButton title], @"Do Something",
      @"The button should be titled accordingly.");

    STAssertEquals([doSomethingButton action], @selector(doSomething:),
      @"The button should send -doSomething: to its target.");

    STAssertEquals([doSomethingButton target], _windowController,
      @"The button should send its action to the window controller.");
}
You'll notice something I'm not doing in the above: I'm not simulating interaction with the interface. This is the core of the trust, but verify approach to unit testing of your user interface.

I can trust that as long as I verify everything is hooked up properly that Cocoa will cause the button to send its action message to its target — whether it's a specific object or, if the target is nil, the responder chain — whenever the button is clicked while it's enabled and not hidden. I don't need to simulate a user event, and I don't even need to display the interface while running the unit tests. All I need to do is inspect, through code, that everything is wired up correctly.

Note that I can do way more than the above in testing my interface design, too. For example, I can ensure that the control layout is correct according to what my interface designer has specified, by checking bounding rectangles for example. But testing only the functionality of my interface has significant advantages, too. For example, it doesn't matter if I wind up using a custom kind of button to achieve exactly the kind of look and feel or behavior I need. It doesn't matter if I wind up changing the layout in response to feedback. No matter what I do, I'll know that functionality won't accidentally break while I'm messing around in Interface Builder — even if I completely rip out my interface and replace it with a new one!

This approach can also be used for testing Cocoa bindings using the -infoForBinding: method that was introduced in Mac OS X 10.4 Tiger. I hope to write up a post soon on how to approach Cocoa bindings using these same techniques, but it should be fairly straightforward given the above and the above documentation.

Update: I've struck through the check of the button's title above, because you may or may not want to do that. For example, if you're primarily running your unit tests against your development localization, you may want to put it in. But if you want to run your unit tests against a localized build of your application, you'll probably want to avoid checking a localized title against an English string. A "have your cake and eat it too" strategy might be to keep a variable somewhere in your application that can be used to selectively disable checks of only localized strings.

Update July 7, 2007: I've finally written a post, Unit testing Cocoa user interfaces: Cocoa bindings, on how to write tests for Cocoa bindings. Now there's no excuse for not doing test-driven development of your Cocoa user interfaces!

Cocoa memory management & exceptions
userpic
[info]chanson
Cocoa memory management is pretty straightforward; you only have to learn a few simple rules.

Chances are, though, that you're also going to deal with exceptions in your code. In Cocoa exceptions can complicate memory management if you're not careful. In this post I'll give you a guided tour of some of the pitfalls and show you how to avoid them.

Modern exception handling is a great mechanism for dealing with errors: It decouples the detection and handling of exceptional conditions, and automates the propagation from the point of detection to the point of handling. This results in code that's much cleaner when written, much easier to write correctly, and much easier to maintain correctly over time. If you've ever had to deal with a lot of procedural code that returns error codes that need to be checked and propagated manually, you'll know just how much more complex they make your code.

If they're so great, then, why do I say exceptions can complicate memory management? First of all, they make it easy to leak memory. Say you have a simple method that, for efficiency, avoids using autorelease for a temporary object:
- (void)leakOnException {
    NSMutableArray *array = nil;

    array = [[NSMutableArray alloc] initWithCapacity:0];
    
    [self doSomething:array];

    [array release];
}
There's a pretty obvious memory leak, if -doSomething: throws an exception. The solution is also obvious: Put the -release in a @finally block:
- (void)noLeakOnException {
    NSMutableArray *array = nil;
    
    array = [[NSMutableArray alloc] initWithCapacity:0];
    
    @try {
        [self doSomething:array];
    }
    
    @finally {
        [array release];
    }
}
Of course, your code may not be quite so simple, but the pattern will be obvious — and it applies to all resource management, not just memory. So be careful not to leak file descriptors, or forget to unlock any locks you've acquired!

Another issue you might run into is over-releasing an exception object. Exceptions are effectively out-of-band return values; in Cocoa, this means the exception object itself is autoreleased. When would this result in over-releasing the exception? When you're using an internal autorelease pool:
- (void)overReleaseException {
    NSAutoreleasePool *pool = nil;

    pool = [[NSAutoreleasePool alloc] init];
    
    [self doSomething];
    
    [pool release];
}
At first glance, this looks fine. After all, if -doSomething throws an exception, the autorelease pool will still be cleaned up when the next autorelease pool "out" in scope is released. However, this could actually happen before the exception is delivered — by the release of an outer autorelease pool in a @finally block — meaning the exception would be a zombie on delivery!

How do you fix it? One way is to make sure you don't release any pools in @finally blocks; so long as the exception is caught before any pools are released, you're safe. Another way is catch and re-throw any thrown exception in order to "promote" it to the next pool out any time you or any of your callers releases a pool in a @finally block:
- (void)promoteException {
    NSAutoreleasePool *pool = nil;
    id savedException = nil;

    pool = [[NSAutoreleasePool alloc] init];
    
    @try {
        [self doSomething];
    }
    
    @catch (id anything) {
        savedException = [anything retain];
        @throw;
    }
    
    @finally {
        [pool release];
        [savedException autorelease];
    }
}
There are several things to note about the above. The first is the use of id in both the @catch block and in the declaration of the variable we're using to save any thrown exception. You need to do this to avoid limiting your exception handling to just exceptions of class NSException. Unlike in Java, Objective-C exceptions are not required to be subclasses of any specific class or to conform to any specific protocol.

Also note that there is no argument in the @throw statement; this re-throws the caught exception exactly as caught; this is important, because Objective-C does support matching @catch blocks based on the type of the exception.

Finally — ahem — the order of operations in the @finally block is important. The whole point of the above is to retain any thrown exception across the release of the interior autorelease pool, which is the pool the exception was placed in on its way out of -doSomething, and ensure that it's autoreleased in the next pool "out" in scope. To do this, we need to make sure that the interior pool is released before autoreleasing the exception. Once the @finally block executes, exception propagation will continue and the exception will be located in the proper autorelease pool.

One other comment about this pattern: You'll need to follow it any time you are returning an object created within the scope of an interior autorelease pool. For example, if -doSomething could return an NSError you would need to ensure that it's promoted to the outer pool before being returned, like so:
- (BOOL)promoteError:(NSError **)error {
    BOOL success;
    NSAutoreleasePool *pool = nil;

    pool = [[NSAutoreleasePool alloc] init];
    
    success = [self doSomething:error];
    
    if ((success == NO) && (error != NULL)) {
        [*error retain];
    }
    
    [pool release];
    
    if ((success == NO) && (error != NULL)) {
        [*error autorelease];
    }
    
    return success;
}
The above is the same pattern as before, modulo any exception handling. The checks around the manipulation of *error are there because it's valid to pass NULL for output NSError parameters to indicate that no error should be generated, and even if NULL isn't passed, you're only allowed to touch the parameter if an error is actually generated.

One more bit about exceptions in Cocoa before I go: In general, the Cocoa frameworks themselves will not generate exceptions for anything but "programming errors." In other words, -[NSArray objectAtIndex:] may generate an exception if you ask for an object outside the range of the array, but this is the kind of exception that should never occur when an end user is using an application. The Cocoa frameworks will generally not use exceptions to do things like indicate a file doesn't exist, or a server couldn't be connected to. Other frameworks, however, may not follow the same pattern — and application code may not either. So it pays to be robust.

Update: Thanks to Michael Tsai for pointing out that I wasn't explicit enough in explaining how you might over-release an exception. I've clarified that section a bit.

Model/View/ViewModel
userpic
[info]chanson
This guy must be kidding. Introduction to Model/View/ViewModel pattern for building WPF apps (John Gossman):
Model/View/ViewModel is thus a refinement of MVC that evolves it from its Smalltalk origins where the entire application was built using one environment and language, into the very familiar modern environment of Web and now Avalon development.
Yeah, because Model-View-Controller is just so inadequate when you're using visual human interface construction tools to create desktop applications or web applications.

I mean, it's not like anyone else has had an "interface builder" as an integral part of their platform. Or tools for creating web applications out of reusable and composable "web objects."

Trust, but verify.
userpic
[info]chanson
President Reagan, for all his faults, gave us a very useful aphorism in describing his approach to diplomacy with the Soviet Union: "Trust, but verify." This is also a very useful approach to take when writing unit tests when you're working with a framework, particularly when you're developing a human interface.

For example, Cocoa uses the target-action pattern for controls. When you create an NSButton you can specify which object to send a message to when it's clicked — the target — and what message to send — the action.

You can trust that when you click the button, Cocoa will cause the action message to be sent to the target object so long as they have been properly specified. Therefore you probably don't need to write a unit test that simulates clicking on the button. However, you should verify that your button has had its target and action properly specified, and you can write a test for this.

You can also apply this principal in your own code. Let's say you're implementing a new type of control that also has to follow the target-action pattern. In the tests for your control itself, you probably will want to simulate the appropriate user-interface events and see that a testing instance of your control behaves appropriately. However, you don't need to do this in the code that uses the control — you can trust that the control behaves correctly due to the tests you wrote for the control itself, and just verify that it's been properly configured.

Good Reads Online
userpic
[info]chanson
A while back, I saw on Lambda the Ultimate that Smalltalk-80: Bits of History, Words of Advice was available online.

There are quite a few more worthwhile language books on the Web. For example, Tim Budd's A Little Smalltalk is also available at Professor Ducasse's site.

You can also find Common Lisp: The Language, Second Edition in various places online, and Paul Graham has made On Lisp available.

I started down this route because I'm reading Graham's Hackers & Painters right now and in some ways it reminds me of Patterns of Software by Richard P. Gabriel which, it turned out, Gabriel posted online once it went out of print.

Class Clusters
userpic
[info]chanson
[info]moonlessnights has a good post on class clusters in the [info]ood community. Class clusters are one of the unsung great design patterns in Cocoa; they don't just encapsulate instance functionality but class functionality as well, and a lot more naturally and flexibly than the Factory pattern used in a lot of Java and C++ development.

Cocoa Menu Validation
userpic
[info]chanson
Cocoa supports automatic menu validation based on a few simple rules. However, it can be inconvenient to keep adding things to -validateMenuItem: all the time, particularly when there are inheritance hierarchies involved.

There's a pattern that can help you get it right much more easily using introspection. You can implement your -validateMenuItem: method to ask the receiver whether it can respond to a particular selector at the moment, like so:
- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
{
    BOOL enabled = YES;
    
    SEL itemAction = [menuItem action];
    
    if (itemAction != nil) {
        if ([self respondsToSelector:itemAction]) {
            SEL canAction = [self canSelectorForAction:itemAction];
            
            if ((canAction != nil) && [self respondsToSelector:canAction]) {
                BOOL (*performCanAction)(id, SEL) = (BOOL (*)(id, SEL)) objc_msgSend;
                enabled = performCanAction(self, canAction);
            } else {
                enabled = YES;
            }
        }
    }
    
    return enabled;
}

- (SEL)canSelectorForAction:(SEL)action
{
    NSString *actionString = NSStringFromSelector(action);
        // actionString is guaranteed to be 2+ characters
    NSString *actionStringFirst = [actionString substringToIndex:1];
    NSString *actionStringBody = [actionString substringWithRange:
        NSMakeRange(1, [actionString length] - 2)];
    
    NSString *canString
    = [[@"can" stringByAppendingString:[actionStringFirst uppercaseString]]
        stringByAppendingString:actionStringBody];
    
    return NSSelectorFromString(canString);
}
The above checks whether or not the receiver responds to the item's action selector. If it does, it will check if the receiver also implements a -(BOOL)canActionName selector, and use that to tell whether the menu item should be enabled.

The -canSelectorForAction: method just takes an action selector and returns its equivalent "canAction" selector, by upper-casing the first letter and appending it to "can". This lets you write a pair of methods for each action, one to perform the action and one to tell whether the action can be performed at the moment, leading to easy-to-test and uncluttered validation code.

Update (June 19, 2008): Changed how the -(BOOL)canActionName selector is performed to be safe and correct on Intel, and other architectures where a (BOOL) return might not be exactly equivalent to an (id) return. Thanks to an anonymous commenter for the correction!

NSArrayController design patterns
userpic
[info]chanson
So in building an application that uses NSArrayController, I stumbled across the same issue that a number of Cocoa developers have.

The issue is that, in many cases, you want to programmatically manipulate the contents of the array an NSArrayController is bound to. For things like master-detail interfaces, NSArrayController will do everything for you including manage the array, but for other things — like, say, buddy lists — you'll want to manipulate the array directly and have the changes reflected in your interface.

The problem is that NSMutableArray doesn't actually broadcast Key-Value Observing notifications. I call this a bug, but it's not clear whether or not it'll be fixed. So you can't write code like [buddies addObject:buddy]; and expect a table managed by an NSArrayController to be updated.

Thanks to mmalcolm crawford's notes on the controller layer, I figured out what needs to be done. Essentially, you can either use Key-Value Coding within your class to manipulate your array, or you can use the Key-Value Coding compliant accessor methods to manipulate your array's contents. In other words, you can either do
[[self mutableArrayValueForKey:@"buddies"] addObject:buddy]
or you can do
[self insertObject:buddy inBuddiesAtIndex:[self countOfBuddies]]
assuming you implement -insertObject:inBuddiesAtIndex: and -countOfBuddies.

The thing that tripped me up was that KVC compliant accessor methods are not the same as Scripting KVC compliant accessor methods. So don't go looking at the documentation for NSScriptKeyValueCoding as a guide for what to implement to generate KVO notifications. Hopefully another thing that will be considered a bug...

AppleScript and Cocoa applications
userpic
[info]chanson
Adding AppleScript support to Mac applications has always been difficult. AppleScript is built on a complex object model and a data-driven mapping between an application-defined terminology and that object model; this determines what events get sent to an application and how they're constructed, what the replies look like, how they should be represented to the user, and so on. And then you had to actually implement all the low-level details of responding to the events and managing the object model. Frameworks like Metrowerks PowerPlant and MacApp helped, but you still had to do a lot of work to make anything significant happen.

Enter Cocoa. Because Cocoa was designed around the Smalltalk-like Objective-C object model, there are all sorts of nifty run-time introspection features available to developers. Therefore, to make a Cocoa application scriptable, all you need to do is make sure your model objects are accessible via Key-Value Coding — which most tend to be due to the common idioms and design patterns Cocoa developers use — and define your terminology. The terminology has been extended slightly to describe how to map from AppleScript-level entities & commands to classes & messages in an application; however, you don't generally have to write the code to actually handle all the individual events associated with AppleScript, interpret the various object specifier forms, and so on. Cocoa does that stuff for you.

What's more, Don Briggs wrote a great application called SuiteModeler that lets you edit the script suite & terminology files very easily and directly. The files are already easy-to-edit property lists (either in the old ASCII or new XML format), but SuiteModeler will keep them consistent for you, and make the process of developing or tweaking your scripting support go so much faster. It's definitely worth the $25 shareware fee.

So something that used to be so heinous that very few applications did it and that applications had to be almost entirely re-architected to support is now nearly free and has a very minimal impact on your codebase. I love this new world.

Model-View-Controller
userpic
[info]chanson
Back in the 1970s, when Alan Kay's team at Xerox PARC (now "Palo Alto Research Center Incorporated") was inventing object-oriented programming, the Smalltalk language and the basis of the modern graphical human interface, they invented a design pattern that every software developer should know, understand, and use today: Model-View-Controller.

In an MVC application, there are three primary types of objects: Model objects, view objects, and controller objects. Each type of object has its own role to play, and by keeping objects' roles separate an application can be designed to be much more maintainable and extensible than if everything is intermingled.

A model object typically represents a single piece of data or knowledge. It may have relationships to other model objects, but it is not directly linked to anything in the application's human interface. Keeping human interface knowledge out of model objects helps them to be very reusable between different applications. For instance, model objects can be shared between a desktop and a web application, even though those applications may have radically different interfaces.

A view object represents an interface element in your application's human interface. This includes both display-only elements like windows, pictures and static text strings, and editable elements such as edit fields, checkboxes, buttons, and pop-up menus. View objects are "dumb"; they have no knowledge of the information they present, they only know how to show it. By keeping views dumb, views can often be made extremely reusable between different applications. A slider is a slider and a checkbox is a checkbox and a window is a window, no matter what they represent in your application.

Controller objects are the glue that binds an application together. Controllers have deep knowledge about both your model objects and your view objects, and control the interaction between the two. When you pick an item from a popup menu, the popup sends a message to a controller, which determines which item was picked and how that should modify your data model. The controller modifies the data model, and then modifies other elements as necessary based on the data model changes. Controller objects are often not reusable between different applications because they represent the bulk of the application-specific behavior.

It's disturbing how many people write applications without thinking about how to break them down into model, view, and controller objects. I've seen applications -- and written some, before I understood MVC -- that do things like rely on interface elements for data storage, subclass interface elements to represent an element that manipulates or displays a particular type of object, and so on. You may be able to write and debug an application this way fairly easily, but extending it down the line will be tough.

Why did I write this? I just added a feature to a non-MVC application. It had a popup menu that needed to display a human-readable version of some raw data, instead of the just displaying the raw data. Changing the popup menu to display the human-readable version was easy. Changing the places in the code that relied on being able to extract the raw data from the popup menu was a pain. If instead there was just a little bit more abstraction, it would have been a lot easier. And if this feature needs to change again, it's going to be a whole bunch harder...