Search code examples
macoscocoaappstore-sandboxmacos-mojave

Pros and cons of splitting Cocoa utility app into helper binary + main app binary?


I'm modernizing and sandboxing an old Cocoa utility and considering approaches. The app lives in the menu bar and works in the background, but shows a Dock icon and a configuration window when the icon is clicked.

There are two approaches:

A. A single binary with LSUIElement=YES, using TransformProcessType to show and hide the dock icon as necessary.

B. A helper tool performs the actual app functionality, manages the menu icon and always runs in LSUIElement mode in background. The main app presents the configuration UI and is only launched when necessary.

The app currently does A. I've noticed that many long-running utility apps have separate helper binaries and basically do B. Examples on my Mac include Paste Helper, TimingHelper, Discord Helper, CCC Helper (for Carbon Copy Cloner), 1Password Extension Helper.

I understand that having a separate helper app is conceptually more pure and potentially allows for better separation of sandbox privileges, but it's also harder to implement, so I doubt that's the sole reason why all these apps opted to have a separate helper binary.

So:

  1. What are the pros and cons of A and B, i.e. why do some choose B over A? Is it required to get some functionality these days?

  2. Is it even possible to have a helper tool outlive the main app in a sandboxed Mac App Store app?

  3. What API does one use to make such a helper? The old-style authorisation APIs seem deprecated, and XPC does not seem like it allows a helper app to launch at startup (and even outliving the main app may be hacky)?


Solution

  • I suspect the reason so many developers choose option B is because this arrangement is now baked into macOS via the "Login Items" facility.

    In a nutshell, your main application embeds a second (helper) app and that app is configured as a "Login Item". macOS detects this and automatically adds your helper app to the user's login items. You can control this, programmatically, using SMLoginItemSetEnabled(...).

    You end up with a regular app users are familiar with, and a helper app that automatically starts at login and can run in the background. I'm also pretty sure this includes a free XPC connection you can take advantage of.

    Read all about it in the Adding Login Items section of the infamous Daemons and Services Programming Guide.

    I've never done this myself (I currently install background apps as user agents, which I can do because I don't have a sandboxed app), but I did research it for another project and I know a lot of apps do this.

    One disadvantage of option A (based on user feedback from my own apps) is that the main app won't act like a regular app. Using the A approach, your users either can't quit the app (because it will need to automatically restart) or you need a way to hide it in the dock, and then there's no (obvious) way to launch it again. It just gets confusing. If you do let your users quit the app, then the background functionality goes away, and that creates other problems.