Main Picture
NEWS

Recently I had occasion to deal with the Finder, beyond what NSFileManager or NSWorkspace could handle. Now, the easy way to do this is through AppleScript, and Cocoa does provide a way to run AppleScripts (NSAppleScript). The trouble is, (a) NSAppleScript is slow and only runs on the main thread, and (b) you can't pass input easily.

AppleScript is built on Apple events, a mid-to-high-level form of interprocess communication that's been around since System 7. Apple events can be constructed and sent off using either pure C code (what's now part of the CoreServices framework) or the Objective-C wrapper, NSAppleEventDescriptor, in the Foundation framework. But constructing a proper Apple event is a little onerous, and again, customizing the message being sent has to be done interspersed with the building of the event. Even with the convenient AEBuildAppleEvent() function, which takes a stringified version of the event, it's just not transparent and simple.

Enter Scripting Bridge. It was announced and trumpeted (at moderate volume) way back before Leopard was released—it is, of course, a 10.5-only feature. And somehow it slipped beneath my radar, though of course most of my apps were/are backwards compatible to 10.4 or even 10.3. The ADC site had a rather trivial example that actually shows the most important part of the bridge: it looks like Objective-C and uses regular Foundation classes whenever possible.

For example, using the Bridge, you could get the name of the current iTunes track with the following line of code:

NSString *currentTrackName = [[iTunes currentTrack] name];

The Scripting Bridge uses native Cocoa data types, such as NSString and NSArray, requires far less code than using an NSAppleEventDescriptor, and runs more than twice as fast as a precompiled NSAppleScript.

The fallout benefit was that all the languages bridged with the Cocoa runtime, like Ruby, Python, and F-script, also get this functionality. It's in that context that I've heard Scripting Bridge mentioned since the Leopard launch. But let's take a look at what it would do to a bit of Dockyard code:

result = AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplicationBundleID, systemEventsIDChars, systemEventsIDLength,
  kAutoGenerateReturnID, kAnyTransactionID, &event, NULL,
  (([OrientationLeft isEqual:[defaultsDict objectForKey:OrientationKey]]) ?
    "data:left,'----':'obj '{form:prop,want:prop,seld:dplo,from:'obj '{form:prop,want:prop,seld:dpas,from:()}}" :
    [OrientationRight isEqual:[defaultsDict objectForKey:OrientationKey]] ?
      "data:righ,'----':'obj '{form:prop,want:prop,seld:dplo,from:'obj '{form:prop,want:prop,seld:dpas,from:()}}" :		
      "data:bott,'----':'obj '{form:prop,want:prop,seld:dplo,from:'obj '{form:prop,want:prop,seld:dpas,from:()}}"));
if (result == aeBuildSyntaxNoErr)
{
  result = AESendMessage(&event, NULL, kAENoReply, 0);
  AEDisposeDesc(&event);
}

Bleah. It works. It's a lot more efficient than calling out to an AppleScript. But it's certainly not clear what's going on. Compare that to this (though untested):

id orientation = [defaultsDict objectForKey:OrientationKey];
SystemEventsApplication *sysEvents = [SBApplication applicationWithBundleIdentifier:@"com.apple.systemevents"];
if ([OrientationLeft isEqual:orientation]) {
  sysEvents.dockPreferences.location = SystemEventsDplsLeft;

} else if ([OrientationRight isEqual:orientation]) {
  sysEvents.dockPreferences.location = SystemEventsDplsRight;

} else {
  sysEvents.dockPreferences.location = SystemEventsDplsBottom;
}

Which one's easier to read?

So, there is a downside. Scripting Bridge is very simple when it works, but downright impossible to debug when it doesn't. With several layers of indirection and insulation from the Apple event architecture, you can't really do things "manually" with Scripting Bridge. (There is a -propertyWithClass:code: method that lets you override some default behavior, particularly when SB isn't able to determine the right class for you.) The one error-recovery method isn't actually very useful for error-recovery, and it's the one place where the framework unexpectedly drops you down to the C level. I would expect them to at least wrap the event in an NSAppleEventDescriptor, but no...

In the end, I was able to do almost everything I needed to while staying above the bridge. Debugging is hard, but there generally is a way to do everything. (Hint: use URLs to refer to filesystem items, not paths.) But there is another high-level alternative: appscript. Appscript is a very similar framework optimized for Python, Ruby, and then Objective-C. The project states that it isn't yet feature-complete for Objective-C, but it's both stable, being around since Panther, and still under active development. ...But I haven't used it myself.

Now, implementing scriptability is still unfortunately annoying if your app doesn't fit a nice model. Fortunately we've been insulated from raw AppleEvents on that end for a while now. I'll try to document my efforts in that field next time it comes around.


As any programmer knows, feature creep is dangerous, deadlines tend to slip, and work expands not just to fill the time allotted, but far past.

As a Dockyard fan would know (though I doubt there are any), Dockyard 2.0 is now almost two years late. Yes, there was supposed to be a release shortly after Leopard, after I could work out the kinks. What happened?

Well, first I didn't get my hands on Leopard for quite a while. Then I noticed one of its big new features—Spaces—had turned out to be a big new problem for Dockyard. (Basically, the Dock controls Spaces, and Dockyard forces the Dock to reload by quitting it. The result would be windows getting "trapped" in another Space and suddenly popping back...or not.) How to fix this... I wondered. And the current Dockyard alphas do have an imperfect workaround in place now.

But that's just an excuse. The real reasons for Dockyard's inordinate delays are threefold...

Time. As a college student, webmaster for multiple sites, developer supporting a few existing projects, singer and arranger for various groups, etc., I underestimated the amount of time that I could put towards new development of long-term personal projects. The "ten-hour Tetris" a few months back was a sort of self-dare, about what could be accomplished in a short time. But Dockyard isn't a short-term project, it's a large project, which I have high standards for.

A surprising factor was Conflict of Interest. I haven't mentioned it on this site before, but last summer I was fortunate enough to have an internship at Apple, Inc. Yes, the Apple. This summer I'm back there again (though in a different group), and have the same problem: working on independent Cocoa apps (maybe even web apps) can definitely be a conflict of interest. Particularly apps like Dockyard and the Safari plugin I hinted at a few weeks ago, which are full of what's been called "undocumented goodness" but what could easily be construed as "inside information". To keep things clear, I'm not touching Cocoa development during the summer.

(By the way, if you want to tell me about something you want fixed, don't. File a bug (and here too) — we do read them, though a few months before a new OS is released is probably not the best time for a response.)

The biggest problem, though, is what I said before: Dockyard is now a Large Product. When did that happen?

Let's look at Dockyard itself. Dockyard v1.0 was a simple menu listing of docks. You couldn't even change the name of a dock after it had been created, and you couldn't launch items from the menu. The feature that was so valuable to me, after the core functionality, was the implementation as a menu extra, a first-class status item like the clock, battery, or even the Scripts Menu. One of several undocumented strategies in a app (actually a plugin) written during my first year of Objective-C.

Dockyard 1.5 added renaming (hooray) and that obvious-but-great feature of opening items in another dock by choosing them from a submenu. Other dock switchers already did this, but it worked well in Dockyard. No problems yet.

Dockyard 1.6...menu extra, plus widget, plus manager application. Uh-oh! The old Dockyard format was stretching, and a lot of code went into keeping these three in sync if you had them all open. Plus, killing the Dock also means killing the Dashboard. I turned that into a feature by allowing you to switch Dashboard configurations along with your dock. But the current system was untenable.

And now we came to the feature requests and expectations: integration with VirtueDesktops or another pre-Spaces desktop manager, integration with Spaces itself, global hotkeys, switching via screen corners or sides (I had liked this feature in another dock switcher), and of course, not disrupting Spaces to the point of unusability. All of these gave me delusions of grandeur, making me envision a grand plug-in model for switch triggers and switch actions, and an implementation based on distributed objects, instead of keeping separate Dockyard "clients" in sync.

Alas, such visions were much too far-reaching. The one bug that's a show-stopper is the Spaces-disruption, and everything else merely drained my time and made the project spin out of control. I don't want to abandon the project, but I look around at Dock Spaces and Docks and see two products with nice UIs that implement the core functionality as well as I do, or better. (Actually, Dock Spaces used to have better UI than it does now. Oh well.)

Both of those still use the "kill-the-dock-to-reload" strategy, though, so both are subject to Leopard's occasional Spaces-related window-loss. And neither has a true menu extra, though both have the Apple-supported "status item" available. (The difference to the user is mainly that menu extras can be reordered, instead of just adding on to the left end of the status bar. The difference to the developer is that menu extras run inside a single app called SystemUIServer, and can have a few extra features beyond having a plain menu.) It's just like that situation four, five years ago: there are apps that do it, but not the way I'd do it. Only now I have three large roadblocks keeping me from doing it my way.

So, a plan. First, assume Dockyard is not coming back. GenericToolbar has already been retired, and PHP/CC was never updated for Xcode 3. (By the way, Xcode is not easy to reverse-engineer, basic support for other languages got a little better anyway, and I'm not working on PHP-based projects these days. Don't expect an update.) That leaves Webmailer as the lone released project for Belkadan Software right now, and while it deserves a quick look-over, I think it's actually doing pretty well. The Safari keyword plugin will join it hopefully in early autumn.

As for Dockyard, I still want that functionality. Here's a short list of must-have features:

  • Must not break Spaces (unless the user tells it to use old-style switching)
  • Menu extra, with submenus for the contents, i.e. "at least what v1.5 had"
  • Scripting support, so it can be controlled from outside
  • Distributed notifications, so other things can react to it
  • Switch docks, picking up user modifications along the way

And here's some nice-to-have features:

  • Manager interface. Hopefully a better one than the 1.6/1.0 release.
  • Global hot-keys
  • Screen corners and edges

Maybe some day in the future I'll get to all of these. But meanwhile, I'm going to have to retire Dockyard as well. It's had to suffer the embarrassing "10.3 - 10.4" on the front page of Belkadan Software long enough. Come the next site redesign (hopefully before the end of summer, but maybe not until the Safari plugin's release), Dockyard (and GenericToolbar) will be leaving their place of prominence and relegated to a "past products" section.

Apologies to anyone who was desperately waiting for Dockyard 2.0. I'm disappointed, too, but it's far past time to be realistic about the future of this project.


For those of you who were really hoping to get the solution to the Downloads stack drawer problem, it goes something like this. Attach a folder action to your Downloads stack that moves the drawer item out of the drawer and then back in when an item is added. Why don't I have an implementation to give you? Simply, because it doesn't always work. Safari's trick of putting a package down for temporary downloads, then moving the downloaded file out of the package when it's done, seems to confuse the folder action system. Or maybe it's just throttling it, or something. Or it's infinite-looping (firing the action on the drawer icon item coming back). Whatever it is, I sometimes see the drawer pop to the front and sometimes don't. YMMV.

If you really care about this, file a bug with Apple to allow custom stack icons, or badging, or something to avoid the "stack of folders" syndrome seen in the last post.


This week's post contains two short unrelated topics, one interesting and one practical.

For a while now, I've been casually following the birth of the Parrot Virtual Machine. Parrot's goal is to be a general-use virtual machine for dynamic languages, most notably Perl 6 and followed by Python. (Sort of like Microsoft's Common Language Infrastructure, but more dynamic and open source.) As someone interested in high-level language design, there's a lot of cool things here...if only I knew enough Perl to write my own front-end. (You can do it in other languages too, including good old lex-yacc / flex-bison, but you lose out on some of the work they've done for you.)

Anyway, today on the Parrot blog there was a post about (mostly) lockless concurrent garbage collection. The idea is not only pretty amazing to me, it's also more and more applicable as parallelism becomes more and more relevant. The original technique was also published in 1998. (For context, the garbage collectors in Java and Cocoa today are already running on separate threads, and two of the "features" of the upcoming Mac OS X Snow Leopard are Grand Central Dispatch and OpenCL. Parallelism is the way to go.) I haven't yet read the technical papers linked from that post, but there's a very clear summary there that is astounding in its simplicity. (As in, I could probably go implement the simplest one myself without reading the paper.)

Things like this are cool for two reasons: one, that such a neat hack can be done, and two, that Parrot would take care of it for me if I target that as a backend.

That wraps it up for Parrot, for now. Someday I'll get a chance to play with it for real.

A plain stack of...folders? On to the next tip! If you use Leopard's "Stacks" feature, you might get frustrated with the untidy look of your stacks in your Dock...especially if you have stacks of all folders! Fortunately, a solution's been around for a couple of years now: wonderful drawer icons by Yasushi Chida. These icons are actually attached to old resource fork text clippings (what you get if you drag text to the Finder). These are cleverly (?) named with a ".app" extension to keep QuickLook away.

Installing these wonderful drawers is easy: just drag the icon you like to your stack. They're already named with a leading space, so they'll pop to the front of your stack if it's sorted by Name, and they're timestamped in the future to handle Date Created or Date Modified. (Date Added is a little trickier; I'll summarize my solution at the end of next week's post. I'm not just being lazy; it's on another computer.) The problem comes when you accidentally (?) click on the drawer icon.

The application “ System ” cannot be launched.

To fix this, I cannibalized an AppleScript application, a legitimate Mac OS X bundle application, and dropped in a simple script that pops open the enclosing folder (essentially mirroring the "Open in Finder" option at the end of the list). For the icon, I used Iconverter (VersionTracker link, developer's site seems to have disappeared) to extract the icon to a Mac OS X .icns file, which I then place in the dummy application's resources directory. (Preview can pull icons from the clipboard, but it can't save .icns. The developer app Icon Composer can't pull icons from the clipboard — I'm going to file a bug on this.) Here's the source of the executable script at the core of the "application"; you can also download the whole setup and modify it yourself.

#!/bin/bash

container=`dirname "$0"`
open "$container/../../.."

A stack clearly labeled as system-related Now things work beautifully; there are no resources or outdated formats involved, and the drawer icon does something reasonable if you click on it. I don't use stacks for much, and when I do they're almost always in Grid view. But this makes them a lot easier to distinguish—and a lot more aesthetically pleasing as well. Now, if only you could get rid of the icon in the Grid view...