CocoaHeads Silicon Valley at Apple on Thursday, May 15, 2008

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.

Copyright canonical form

One thing that’s nagged at me lately has been the series of applications I’ve seen lately with copyright statements that appear to be from the Bizarro universe. I don’t mean that they have weird license restrictions; rather, they have a copyright statement in their standard About panel that’s formatted strangely. It’s a minor pet peeve to be sure, but it’s a simple thing to get right and getting it wrong looks silly.

Note that the following is not legal advice on asserting or protecting your copyright — you’ll have to go to a lawyer for that — it’s just a suggestion on how to concisely format your statement that your work is covered under copyright.

In a Cocoa application, the standard About panel will show the copyright statement specified under the NSHumanReadableCopyright of its Info.plist file. This should generally be of the form

> Copyright © «YEARS» «HOLDERS». All rights reserved.

where «YEARS» represents the individual year, set of years, or range of years during which the application was authored and «HOLDERS» represent the authors of the application.

Thus if I were to start writing an application in 2007 and finish it in 2008, I would put

> Copyright © 2007–2008 Chris Hanson. All rights reserved.

in the NSHumanReadableCopyright key of its Info.plist file. (Yes, that’s an en-dash between the years, option-hyphen gets you one.) It wouldn’t have the year at the end, or random commas after things, or random abbreviations. Just one simple statement.

Someday I’ll figure out how to add

> Copyright © 2002–2008 Chris Hanson. All rights reserved.

to the bottom of my weblog, too. Hopefully in such a way that I can actually update it easily when the year rolls over…

CocoaHeads Silicon Valley at Apple on Thursday, Apple 17, 2008

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.

NSCoder Night Silicon Valley (and hopefully elsewhere)

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.

Unit testing Cocoa user interfaces: Cocoa Bindings

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.

Easily speed up your Xcode builds

A lot of developers don’t heavily modify the projects they instantiate from Xcode’s templates. This is a shame, because not only is it a great way to learn Xcode in depth, it’s also a great way to ensure your projects build as fast as possible!

To that end, then, here are two simple tips that you can apply to your projects right now that should get them building faster.

Normalize your build settings!

Projects and targets in Xcode have configurations — collections of build settings that influence how a target is built. The Debug configuration, for example, will typically specify the compiler optimization build setting with as low a value as possible. The Release configuration, on the other hand, will typically specify a relatively high value for this build setting. This begs the question: The Debug and Release configuration of what?

When you create a new project from one of Xcode’s templates, the principal target in that project will typically have a number of build settings customized in its configurations. The project itself, on the other hand, won’t have very may customized build settings. For the one-target-per-project case this doesn’t matter much. However, if you create a project with multiple targets, you can wind up with a number of targets that specify the same (or even subtly different!) information.

Instead of leaving your project this way, you can normalize your build settings such that build settings you want to specify for every target in the project — for example, that compiler optimization should be off for the Debug configuration — are specified at the project level rather than at the target level. This takes advantage of the fact that build settings are inherited in Xcode; if a build setting isn’t customized in a target, the value specified in the project is used.

What does this buy you? It ensures that you have a single, consistent set of settings that are passed to the compilers, linkers, and other tools that are used to build your software. That way you won’t wind up with subtle bugs like code built with C++ RTTI turned on calling a plug-in built with C++ RTTI turned off. But just as importantly, it’s enables the next trick, which can have a significant impact on the speed of large, multi-target builds.

Share your precompiled prefix!

Xcode, like many other IDEs, supports prefix files — header files that are included implicitly by every one of your source files when they’re built. Normally, as I described above, these are specified in target configurations. The text in the prefix file that is copied out of Xcode’s templates even mentions that it’s for a specific target.

Don’t be fooled!

Prefix files get precompiled for performance, so they can simply be loaded by the compiler rather than recomputed for every file you build. Precompiling a prefix file takes quite a bit of time though, especially if you use multiple languages in your project (such as C, Objective-C, and C++) because each language needs a separate precompiled prefix due to differences in how they’ll treat “the same” code.

However, just because precompiled prefix files can’t be shared between languages doesn’t mean they can’t be shared between targets. In fact, for performance, Xcode automates this sharing for you — if you meet it halfway. The critical thing that you need to do is to ensure that your prefix files are:

  1. Named the same; and,
  2. Built using the same compiler-related build settings.

That’s where the normalization I talked about above comes in. You can even promote your prefix file-related build settings to your project instead of your targets, so you can be certain that they’re shared.

In fact, if they meet the criteria that Xcode uses to determine whether precompiled prefix files should be shared, even multiple projects will wind up sharing them!

The pause between builds of a target’s dependent targets to generate a new precompiled prefix file is like a pipeline stall: An unwelcome hiccup that holds everything else up until it’s over. If Xcode can precompile a single set of prefix files at the start of your build and then re-use them for the entire rest of your build, it will stream past as quickly as it possibly can with only the occasional pause for linking. For large projects with a lot of dependent targets, this can make a big difference.

Separate your preprocessor macros!

“But Chris,” you say, “I have target-specific preprocessor macros that I can’t get rid of!” That’s OK. As long as you don’t need them in your prefix files, you can set them in the special Preprocessor Macros Not Used in Precompiled Headers build setting. These will be passed to the compiler when your sources are compiled, just like the regular Preprocess Macros will, but they won’t be passed when precompiling a prefix file. So you can have your cake and eat it, too.

Of course, there are macros that you do want to set in your precompiled prefix headers, because they change the behavior. Macros like NDEBUG to turn off C assert or NS_BLOCK_ASSERTIONS to turn off Foundation’s NSAssert are important to specify for your precompiled prefix files. Fortunately these types of macros typically differ only by configuration, and also remain consistent across targets and projects, allowing you to specify them at the project level rather than the target level.

Just these three small changes have the potential to make a dramatic difference in how quickly Xcode builds your project:

  1. Normalizing your build settings, so common settings across all your targets are specified at the project level;
  2. Increasing sharing of your precompiled prefix files by naming them consistently and using consistent compiler-related build settings; and
  3. Specifying separate preprocessor macros for your prefix files than for your targets,

Try it out and see for yourself!