-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: NSObservedObjectKey, which is the object that the binding is bound to;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 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.<AppKit/NSKeyValueBinding.h> and are documented, too!-setUp just like in the target-action example.- (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...- (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.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.-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.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.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.
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.
The developer tools in Mac OS X Leopard are crafted for one mission: to enable you to create amazing applications. Offering a streamlined workflow, Xcode 3.0 helps you develop your applications faster than ever. The all-new Interface Builder lets you build awesome user interfaces that take full advantage of Leopard's capabilities. The new language features in Objective-C 2.0 speed up your development cycle. The innovative new Xray brings the ability to visualize your application at runtime in a way that's never before been possible. And, the brand-new Dashcode makes it a snap to create elegant and powerful Dashboard widgets.It's the second article in the Leopard Technology Series for Developers, and boy is there a lot of meat there!
for ( … in …) syntax and properties that eliminate the tedium of implementing accessor methods and streamline accessor syntax.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.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.-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.Indeed, McGovern's rant reads more like a parody of a rant than the real thing:So by Enterprise, Architect, and Enterprise Architect standards, this gent must be the top of the pop. Thus, allow me to make this perfectly clear: I would be as happy as a clam never to write a single line of software that guys like James McGovern found worthy of The Enterprise.
If Ruby, Rails, and the rest of the dynamic gang we're lumped together to represent, is not now, nor ever, McGovern Enterprise Ready™, I say hallelujah! Heck, I'll repeat that in slow motion just to underscore my excitement: HAL-LE-LU-JAH!
With that out of the way, we're faced with a more serious problem. How do we fork the word enterprise? The capitalized version has obviously been hijacked by McGovern and his like-minded to mean something that is synonymous with hurt and pain and torment.
13. Lets say there is a sixteen week project and the productivity stuff was true and Ruby could save me an entire three weeks which would be significant. Since Ruby is a new vendor and not represented by existing vendors I already do business with, do you think that I will spend more than three weeks in just negotiating the contract?Yes, because there is some vendor out there named "Ruby that you need to sign a contract with before you can begin a project.
<gsmarkup>
<objects>
<window title="This is a test window" closable="no">
<button title="Print Hello!" action="printHello:" target="#NSOwner" />
</window>
</objects>
</gsmarkup>This gsmarkup file specifies an NSWindow with no close box and the title "This is a test window", which contains an NSButton titled "Print Hello!" that sends -printHello: to the object that owns gsmarkup (set when the gsmarkup is loaded). Note that you don't have to specify the size of the button, where in the window it is, etc. You can if you want (or need) to, you just don't have to. And if you don't specify a target for a control, its target is assumed to be nil (the First Responder).id attributes in a markup to name objects and then refer to them using the standard reference (#foo) notation to wire them together within the same gsmarkup file.