Back to the office, or, the iPhone Cometh

It's an interesting time to be a Mac developer. With the release last week of the iPhone SDK, Apple suddenly opened up a whole new interesting platform to software developers. And as one might expect, those who have experience developing for Mac OS X have a huge head start when it comes to this new platform. Development for the iPhone uses the same tools, language, and even many of the same frameworks as the Mac.

I think I can say all of that, despite the NDA and Apple's rapidly-developing official position on what can be said and where. Earlier this week Apple reps announced that iPhone development could be discussed on Apple's Cocoa-Dev mailing list, but this was followed the next day by a hasty retraction. That is, one day the rule seemed to be "go a head, talk amongst yourselves", but by the next day this had changed to "don't you dare". I can only imagine the internal discussions. The official line may have changed again by the time you read this.

Anyway, in my case the nature of the SDK means that my daily routine has changed rather suddenly, in a nice demonstration of business on internet time. On Thursday the SDK was released. Friday I saw an ad on Craigslist for a company near me needing an iPhone developer. Saturday morning the company's CTO phoned me, and Monday I started doing iPhone development on a contract basis. And incidentally, soon afterward I started filing bugs with Apple against the SDK. Of course, it's still a beta release.

I don't often do contracts, but I really wanted to get a fast start on the iPhone, and what better way to do it? Aside from the same deal where I can continue working from home, I guess. Some aspects of the work mean that I really do need to be on-site for a lot of it, and I'm out of practice at making daily trips to an office.

Right now I'm splitting my time between this contract and my own apps. I'm not putting anything on hold or making fundamental changes to my business, but at the same time I didn't want to pass up this opportunity.

We'll Meet Again?

I'd like to thank everyone who responded to my previous post, either here or in email. I appreciate the support and offers of assistance.

The continuing emergency ended just a few hours after that post appeared. Not, however, in the manner I would have preferred. From the Newark Star-Ledger, last week:

Nancy S. Harrington, 69

Mrs. Nancy S. Harrington, 69, of Montclair died on Sunday, Feb. 10, 2008, at Columbia-Presbyterian Hospital in New York.

A funeral service will be held at 10 a.m. on Saturday, Feb. 16, at St. James Episcopal Church, 581 Valley Rd., Upper Montclair. A reception will follow. Friends may also visit with the family on Friday, Feb. 15, from 7 to 9 p.m. at the church. Arrangements are by the Hugh M. Moriarty Funeral Home, 76 Park St., Montclair.

Nancy was born in Detroit, Mich., on Feb. 25, 1938, the daughter of Robert and Zilpha Steele. Following graduation from Edsel Ford High School in Dearborn, Mich., she lived in Washington, D.C., Detroit and New York City. Before moving to Montclair in 1986, she and her family lived in Brooklyn and in Nyack, N.Y.

Nancy's vocational interest centered on the education of young children, a field that she worked in for most of her life. She will be remembered by a generation of children who attended St. James Pre-School in Montclair, where she taught for 20 years before retiring in 2006. She taught Sunday school at St. James Episcopal Church for 21 years, serving as the director for part of that time.

Nancy was an avid gardener, always eager to try out a new addition to her backyard garden or her window greenhouses.

Nancy is survived by her husband of 42 years, Preston Harrington; her son, Tom and wife, Carey, of Colorado Springs, Colo.; her daughter, Lisa and husband, Jeff Mead, of London, England; her son, Gerry and wife, Katey, of Forest Hills, N.Y., and her grandson, Andre. Her sister, Peggy, of Florissant, Colo., and brother, Gerry, of Anchorage, Alaska, also survive her.

In lieu of flowers, donations may be made to St. James Episcopal Church Pre-School, 581 Valley Rd., Upper Montclair, N.J. 07043.

I'm back home now and getting back to the regular cycles of my life. I'm still going through my email backlog, but should be getting back to everyone soon.


The continuing emergency

I just wanted to post a short note for people who normally see me online all the time and who may wonder what's happened to me recently. And for people sending me support emails wondering why response time is so slow lately.

The short story is that my mom developed an autoimmune condition that led to liver failure. She had been due for a transplant, but soon became much too sick to be considered a transplant candidate (you can't get a transplant unless they think you'll survive the transplant surgery). For the past several days I've been spending all my time visiting the intensive care unit at NY Presbyterian hospital.

The nice thing about independent developers is that you can get hold of the guy who wrote the software, but the downside is that one person's personal life can bring the entire operation to a halt. For those writing me in regard to customer support, Sparkle, etc, I'm not ignoring you but I just can't deal with that stuff right now. I'll get back to you all as soon as I can.

If you're religious at all, say a prayer for my mom, she'd appreciate it.

MondoMouse 1.4.2

I've just released MondoMouse 1.4.2. This version makes two changes:

  • MondoMouse will not make "choose application" windows appear anymore. This has been happening on Leopard systems, when MondoMouse works with certain applications (e.g. OmniGraffle Professional).
  • If a window can't be resized, and "highlight windows being moved or resized" is turned on in MondoMouse preferences, the window will be highlighted in red to make it easier to tell what's going on. Thanks to Brian Cooke for suggesting this.

This upgrade is free to all registered users of MondoMouse. If you already have MondoMouse, the best way to get it is to use MondoMouse's "check for updates" button, or else just wait for MondoMouse to check automatically.

Otherwise, you can download the new version here.

Applescript: Endianness, FREFs, ARGH!

The fun started yesterday when I was testing an application, and I noticed this little gem in Xcode's debug console:

CoreEndianFlipData: error -4940 returned for rsrc type FREF
     (id 128, length 7, native = no)

At about the time this message appeared, my application seemed to stop responding. I didn't know what was causing that, but this mysterious message had to be a clue. I was also, sometimes, getting a "Choose Application" window appearing on the screen, for no readily apparent reason.

I scratched my head for a minute and asked Google what it thought about it. Google wasn't sure. The FREF suggested some old-school pre-X Mac OS programming, but I wasn't doing anything of the sort. The part about endian flipping suggested an Intel vs. PowerPC issue, but I've been doing everything universal for a while now, so that shouldn't be it.

So I asked a team of experts. Thanks to IRC I was able to get some friends on the case, and Daniel Jalkut, Jon Wight, Manton Reece and I spent an interesting and bizarre afternoon trying to figure out what the hell was going on.

Early theories held that, while I wasn't personally using FREFs, perhaps I had included some code that did. That briefly made NDResourceFork a suspect. NDResourceFork is a Cocoa-style interface for dealing with HFS+ resources, and one of its associated classes mentioned FREFs. That ended up being a red herring; I have a graphical front-end app and a back-end worker app, and NDResourceFork is used in the front end while the error was coming from the back end. It's in my project but not actually in the application printing the error message.

So we looked at what the error message appeared to be trying to tell us. Clearly, something must going on with FREFs and/or switching from one endian style to another. Fortunately the experts know a thing or two about old-style Mac stuff, and before long I was setting breakpoints on functions like GetResource() and GetIndResource(), and using PrintResourceChain() in the debugger (functions which, I believe, are older than Mac OS X and which I had never heard of before-- heck, some of them aren't even documented anymore). I also found I could reproduce the problem in the debugger, and it's always nice when the application behaves the same way in a debugger as when it's running normally.

That led to a surprising discovery, that the mystery message was occurring when my application was making AppleScript calls to another application via NSAppleScript. In a way that just confused things for a while, though. There didn't seem to be any reason for this script to be using FREFs. And I verified that my application, the target application, and the AppeScript (compiled with osacompile) were all universal binaries, so there shouldn't have been any question of endianness. It was about at this time I noticed that, for some reason, the bug only manifested itself when the AppleScript was targeting OmniGraffle Pro. Cue ominous music here.

The debugging also led to some seriously weird results, in cases. Trying PrintResourceChain() at the gdb prompt showed resources loaded from completely unrelated applications-- not mine, not the target app, not the source of a scripting addition, not anything that should have been remotely involved.

Trying to pin down exactly when the mystery message appeared, Daniel suggested I set a breakpoint on write(), which is pretty near the lowest-level print statement that should get used by nearly everything printing anything. That was when I regretted all of those NSLog() statements I had added for what Jon Wight called "nuke 'em from orbit style debugging". Some careful disabling and reenabling of breakpoints got me through that without too much trouble, though. And, aha! CoreEndianFlipData appeared in the stack trace! And that was a direct result of my own NSAppleScript call, albeit about 45 levels down in the stack (not an exaggeration, BTW) from there. As for what was going on in between, well, that took some interpreting.

Daniel and Jon both noticed the presence of AEVTsysoppcb in the stack which is, they tell me, AppleScript deciding it needs to ask you where the target application is. The "choose application" window loads icons for all applications. And THAT, apparently, is where FREFs come into all of this. Remember the FREFs? This started with an error message about FREFs. I was now able to start reproducing the error message from Script Editor, which simplified things a bit and, frankly, made me feel a little better by knowing this might not be my fault.

Reducing the AppleScript to the bare essentials needed to demonstrate the behavior, I get this:

if application "OmniGraffle Pro" is running then
	beep
end if

Do this in Script Editor, and a "Choose Application" window appears, asking "Where is OmniGraffle Pro?", even if you have it installed. Click "Cancel", and Script Editor crashes. Watch the console, and you'll see the mystery message that started all of this. Of course OmniGraffle's full name is "OmniGraffle Professional", but fixing that doesn't change the behavior.

Out of curiosity I tried a bunch of other applications. The only other one I've found that causes this is RealPlayer. There might be others.

Apparently then, the chain of events is:

  1. My app calls its AppleScript, targeting OmniGraffle
  2. The AppleScript tries to find out if OmniGraffle is running
  3. For some reason it can't figure this out, because it can't find OmniGraffle. I can only guess this is because of something weird in OmniGraffle's Info.plist, though I'm not sure what.
  4. AppleScript helpfully asks the user where to find it.
  5. The "Choose Application" window, in trying to find application icons, runs smack into that unrelated application that PrintResourceChain showed. It prints out a weird message about FREFs and endianness
  6. If the user cancels, AppleScript freaks out and explodes, killing innocent bystanders like Script Editor or my application.

Now, all I want to do is run this damn AppleScript. If I can find a workaround, I don't actually care why OmniGraffle is making the existing script choke. Daniel pointed out that if an AppleScript says "application appname" somewhere, it causes a full name resolution for the application. If I could make the script check for a running application without that, no name resolution would happen, and tragedy would be avoided. The Pre-Leopard way to do this does just that:

tell application "System Events"
	return first application process of application
		"System Events" whose name is "OmniGraffle"
end tell

Unfortunately it's not workable for me. My script's not actually about OmniGraffle, it targets a bunch of different applications, and it gets the application name from NSWorkspace. NSWorkspace tells me that OmniGraffle is named "OmniGraffle Pro". But AppleScript sees it as "OmniGraffle Professional", and if I use anything but that then it can't find the process. AppleScript then reports that it's not running, even if it is. There's no name resolution, which is nice, but there's also no reliable result.

I'm still beating on this a bit, but it looks like another Leopard-ism may save the day. Beginning with 10.5 it's possible to target an AppleScript based on an application's bundle ID instead of its name. Bundle IDs are, fortunately, consistent where application names may not be. And since they're not application names, they don't get resolved in the same way. So I can do something like this:

if application id "com.omnigroup.OmniGrafflePro" is running then
	beep
end if

And... it works! I think. I need to do some testing to see if it's as reliable as I need it to be. But it looks like what I need.

I'll be reporting this to Apple, because whatever OmniGraffle is doing, AppleScript shouldn't crash and burn like that. I'll probably also report it to Omni, who may well be interested to know.

I'd also like to thank Daniel, Jon, and Manton for taking so much time to help track this down. I don't think I would have got this far without their help.

Update: Daniel Jalkut pointed out that it's possible to address applications by bundle ID in AppleScript on Mac OS X 10.4, although it involves what Apple's AppleScript release notes describe as "...a multi-line incantation using Finder." That incantation turns out to be something like this:

tell application "Finder"
	set appname to displayed name of application
		file id "com.omnigroup.OmniGrafflePro"
end tell
tell application "System Events"
	if exists process appname then
		beep
	end if
end tell


Atomic Bird, LLC