Launch at Login Implementors Support Group

September 6, 2012

I’ve had a number of improvements in the pipeline for SwiftText 1.1.0 for quite some time, and finally submitted it to the Mac App Store tonight. One of the most difficult features to get right was Launch at Login. This was a feature that was in 1.0 and was fairly easy to implement at the time. However, in this brave new world of sandboxed apps, launch at login is a bit of a beast. If you’re thinking about adding it to your application, or perhaps you’re struggling to make it work, perhaps this post will help.

Sandboxed apps need to use a helper tool to do the actual launching for them. In short, the helper app is bundled under Contents/Library/LoginItems and its bundle identifier is passed to SMLoginItemSetEnabled(). delite studio has a very helpful post on this, but you’ll want to make sure to read Mike Cohen’s own post containing some key corrections. Your helper app will need to do some path acrobatics to concoct the path to your main application, which it launches with -[NSWorkspace launchApplication:], and then calls [NSApp terminate:nil].

If you follow the above, you should have a working launch at login feature. There was one more hurdle for me to overcome, however. My application passed the Validate step within Xcode, but when I uploaded the binary I was told my application had an “Invalid binary”:

Invalid Provisioning Profile Location - The provisioning profile for your Mac OS X app must be located in the Contents directory of the main app bundle. A provisioning profile is optional, but you cannot submit more than one.

What the iTunes Connect robot was trying to tell me was that my helper tool was, evidently, incorrectly signed. Using this Stack Overflow answer I was able to re-sign it in a Run Script build phase, which passed the robot’s muster. (One note about that answer: I found that copy-pasting the lines from the answer yielded a non-working script. By retyping the paths I was able to get it working. Bad characters in the mix, I presume.)

Why so much work to get such a simple feature working? The launch-at-login APIs were never developer-friendly: the old Launch Services method was a couple dozen lines, all told. Here we have less code, but many more obstacles to maneuver. Why the helper application, though? As far as I can tell the SMLoginItemSetEnabled() call (part of the Service Management Framwork) is not really intended for matters so trivial as launch at login. I suspect that it was simply the last man standing after the Launch Services method was knocked out by sandboxing. From a developer’s perspective this is a pretty unpleasant pattern. I see three possible explanations:

  • Apple didn’t have time to concoct a new sandbox-friendly API for this in time for 10.7/10.8.
  • Apple doesn’t see it as unpleasant, or not unpleasant enough.
  • My expectations are too high.

My money’s on the first. With the Service Management method, the user must use the application itself to toggle launch at login. Applications with this enabled don’t show up in the Users & Groups Login Items pane, nor do they show a checkbox in the ctrl-click menu on the dock. Not the best user experience. Perhaps there will be a better solution in 10.9. Now to write a bug in hopes that it will help that happen. Here’s what I’m thinking:

@interface NSApplication (LaunchAtLogin)
@property BOOL launchAtLogin;
Tags: ,