tell application “YourGreatApp” NSAppleScript is here to save the day
Cocoa includes the
wonderful NSAppleScript class, which lets you embed a high-level AppleScript code snippets in your Objective-C program. AppleScript is the bee’s knees for interfacing with other applications, particularly Apple programs and OS X itself. EDITED TO ADD: Unless you use threads in your program. Here is a more sobering perspective, written several months after this article.
For example, say you want to get the user’s current status message from iChat. You can mess around with the InstantMessaging.framework — only to discover that it can’t do this — or you can wrap the following AppleScript:
tell application “iChat” to get status message
in an NSAppleScript object, like so:
NSAppleScript *iChatGetStatusScript; //this would be in the header.
/*Apple recommends only compiling NSAppleScript objects once, for performance reasons. Be sure to call [iChatGetStatusScript release] in your dealloc method.*/
iChatGetStatusScript = [[NSAppleScript alloc] initWithSource:
@”tell application \”iChat\” to get status message”];
NSString *statusString =
[[iChatGetStatusScript executeAndReturnError:&errorDict] stringValue];
Skeptical readers have probably noticed that I cheated by picking functionality that wasn’t in a framework for the last example. Unfortunately this situation is more of a rule then an exception. AppleEvents have been on the Apple software development scene longer then Cocoa. Lots of programs only expose functionality through AppleScript/AppleEvents. Using NSAppleScript is the best way to interact with these programs from Cocoa.
AppleScript is a big part of how FrontRow integrates with iTunes, even though they are both modern programs.
grok the ouptut of:
strings /System/Library/CoreServices/Front\ Row.app/Contents/MacOS/Front\ Row
(hint: look for “tell”), and you’ll see a lot of gems like:
tell application “iTunes” to get (data of artwork 1 of current track) as picture
tell application “Finder”
set myCD to first disk whose format is audio format
Now I am the first to admit that a well-written Objective-C library trumps duck-taping one language on-top of another. But we don’t have the luxury of always being given well-written Objective-C interfaces.
As of version 0.24 IMLocation uses AppleScript to: interface with iChat and Adium; install/uninstall itself; mute/unmute sound; perform inter-process communication between the helper and GUI; and to get the MAC address of the first router connected to the ethernet port. NSAppleScript has also been heavily used during development for prototyping.
EDITED TO ADD: NSAppleScript is pathologically un-thread-safe. That is to say, you can only use it on from the main thread. No amount of @synchronized blocks will keep you from crashing if you use NSAppleScript in any other way. There is a workaround, but the cure is worse then the disease. You can use an NSTask to have the osascript program interpret the script for you. This won’t block your thread, but it will be take a long time, and is resource un-friendly.
NSTasks are the way to call the command line, or embed snippets of a different language in cocoa. I was originally using the do shell command AppleScript construct. If you are using it, please switch to NSTasks. They are much more powerful, still easy to use, and prevent all sorts of bugs caused by quote-marks in AppleScript.