This article has been updated, and moved here.
Archive for April 2007
There are two options:
1) Wait for facebook to fix the API. Who knows how long this will take.
2) Somehow simulate a user in a webrowser manually updating their status message. This is a lot of work.
Unfortunately, at this point in time, nether option looks like it will result in a quick fix.
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.
It made me realize I don’t test enough on PowerPC (PPC) systems, since I don’t own one anymore. (One of the joys of being a student developer is only having one computer).
So to simulate a PPC system on my intel box, I’ve started occasionally building for PowerPC only, and making sure everything still runs under Rosetta, OSX’s PPC emulator. You can get the same effect without rebuilding your application, if it is Universal, by selecting it in the Finder, choosing Get Info (⌘-I) -> “General” triangle -> checking “Open Using Rosetta”.
This isn’t a full substitute for testing on a real PPC Mac, of course. But it’s quick to do, catches some endian bugs, and gives you a bit more test coverage.
A feature I thought I would love, but began to hate is automatic muting in “quite” places, like lecture halls. Whenever I take my laptop to a place I have designated as quiet, IMLocation will mute it. This works great at preventing embarrassing beeps during class. But I like to listen to headphones while I work, and they were getting muted whenever I would work in a library or study hall.
What I needed was selective muting — if I was in a quiet place, sound would be turned off, unless I was using headphones.
At first I suspected that this feature would only work on newer Macs. My MacBookPro remembers the volume of my headphones when I plug them in. (By the way, if you don’t know about this handy feature of your new Mac, check it out — it’s an ear saver). But older Macs don’t do this, so I figured there was just no way for them to figure out if headphones were plugged in or not.
Well, it turns out this is not the case. Apparently on older Macs, OS X chooses not to change the volume, even though it can. I have confirmed that this is the case on an iBook G4, iMac G4, 12″ Powerbook, and 15″ TiBook. They can detect if they have headphones plugged in or not, even though OS X ignores this bit of information.
Selective muting is currently available as of IMLocation version 0.24.