LoginItem or LaunchAgent for XPC

Trying to decide between a LaunchAgent or LoginItem, but also looking for a way of getting a launch agent registered with `launchd` without having to go through creating files and running a command line tool.


This is for a mac app, that is installed by dragging the app from the downloads folder. ie there isn't an installer. Currently the app isn't sandboxed but we are thinking of moving to sandboxing the app so we don't want to create a problem for ourselves in the near future.


Currently I have created a LaunchAgent for the application that works and the agent communicates with the application with XPC using the `NSXPC...` related API. The LaunchAgent also works as a LoginItem communicating via XPC with the application.


For this to work and for the LaunchAgent to work immediately it is not only necessary to copy the launch agent plist file to `~/Library/LaunchAgents` but to also run `launchctl load ...`*. This of course will be problematic for sandboxing. Is there a better or recommended approach that does not require creating plist files in `~/Library/LaunchAgents` and running the command line tool.


A restriction for LoginItems is that for `SMLoginItemSetEnabled` to work the application needs to be in the `/Applications` folder. This potentially creates unnecessary work for CS and for engineers debugging and testing. This would be fine for an app in the App Store. There also seems more control over the behaviour of a LaunchAgent via its plist than just calling the `SMLoginItemSetEnabled` function with YES or NO. For example enabling means the LoginItem stays alive all the time and we don't need that behaviour.


The readme.txt file for the example loginitem says that the LoginItem and application must be sandboxed. https://developer.apple.com/library/archive/samplecode/AppSandboxLoginItemXPCDemo/Listings/ReadMe_txt.html#//apple_ref/doc/uid/DTS40012292-ReadMe_txt-DontLinkElementID_3


I have got the login item working without the app and login item being sandboxed. Is that unintended behaviour and really a LoginItem should not be considered for non-sandboxed apps or is that information just wrong?


* I have tried reading the man page for `launchctl` so that I don't have to use the deprecated `load` subcommand but it is not clear from the man page what is the equivalent of just calling `launchctl load ~/Library/LaunchAgents/***.com.companyname.agentname`

Accepted Reply

To use launchd, you'll have to use the command line tool and have a config plist file. I'm not aware of any way around that.


Regarding sandboxing, that is only needed for the Mac App Store. If you want to be in the Mac App Store and your app is appropriate for that, then go for it. Otherwise, it adds a whole additional layer of hassle. I have seen some people fall for Apple's "encouragement" to sandbox everything, even when they aren't in the Mac App Store. I just shake my head and walk away in those cases.


If you want to be in the Mac App Store, then LoginItem is your only option.


Speaking of the sandbox, if you haven't already sandboxed, then any statements you have made about having communication working may no longer valid. That is one of the primary things that sandboxing may break.


Technically speaking, if you are just sandboxing for yourself rather than the Mac App Store, you can give yourself all kinds of temporary exceptions to copy files anywhere. That won't work for the Mac App Store and other Apple rules will forbid it or make it much more difficult. That being said, your plist files don't have to live in ~/Library/LaunchAgents. You can put them in /Library/LaunchAgents for global persistence. You can even load launchd tasks from a plist in any other directory, you just don't get persistence that way.


Do you have documentation that says SMLoginItemSetEnabled can only be used from /Applications? I'm not sure that is true. Running from outside /Applications adds some risk for some of these kinds of edge cases, but the system is designed to runs apps from anywhere. However, I think keeping everything in /Applications would actually make it easier for debugging and testing. The system can run apps from anywhere. If you launch apps using only a bundle id, as you would with a login item, then there is no guarantee of which executable will be run. If you know that your executable is always in /Applications, then you always know. If you have apps elsewhere, then you never know. (This obscure bit of Mac trivia may be documented somewhere, perhaps 9 replies deep in these very forums. Most recent by install date maybe? If anyone wants to prove me wrong on this, go for it.)


I think that example code just means that, if you sandbox, you must sandbox both the app and the helper. The example is "AppSandboxLoginItemXPCDemo" so, by definition, sandboxing is required, or else it would just be "AppLoginItemXPCDemo". You can have SM login items without the sandbox. I just tested it. I think it is just that this functionality was specifically designed for the sandbox, since this is the only way to automatically launch.


The "load/unload" subcommands are not deprecated, just "legacy". I know of no other way to load/unload tasks.

Replies

To use launchd, you'll have to use the command line tool and have a config plist file. I'm not aware of any way around that.


Regarding sandboxing, that is only needed for the Mac App Store. If you want to be in the Mac App Store and your app is appropriate for that, then go for it. Otherwise, it adds a whole additional layer of hassle. I have seen some people fall for Apple's "encouragement" to sandbox everything, even when they aren't in the Mac App Store. I just shake my head and walk away in those cases.


If you want to be in the Mac App Store, then LoginItem is your only option.


Speaking of the sandbox, if you haven't already sandboxed, then any statements you have made about having communication working may no longer valid. That is one of the primary things that sandboxing may break.


Technically speaking, if you are just sandboxing for yourself rather than the Mac App Store, you can give yourself all kinds of temporary exceptions to copy files anywhere. That won't work for the Mac App Store and other Apple rules will forbid it or make it much more difficult. That being said, your plist files don't have to live in ~/Library/LaunchAgents. You can put them in /Library/LaunchAgents for global persistence. You can even load launchd tasks from a plist in any other directory, you just don't get persistence that way.


Do you have documentation that says SMLoginItemSetEnabled can only be used from /Applications? I'm not sure that is true. Running from outside /Applications adds some risk for some of these kinds of edge cases, but the system is designed to runs apps from anywhere. However, I think keeping everything in /Applications would actually make it easier for debugging and testing. The system can run apps from anywhere. If you launch apps using only a bundle id, as you would with a login item, then there is no guarantee of which executable will be run. If you know that your executable is always in /Applications, then you always know. If you have apps elsewhere, then you never know. (This obscure bit of Mac trivia may be documented somewhere, perhaps 9 replies deep in these very forums. Most recent by install date maybe? If anyone wants to prove me wrong on this, go for it.)


I think that example code just means that, if you sandbox, you must sandbox both the app and the helper. The example is "AppSandboxLoginItemXPCDemo" so, by definition, sandboxing is required, or else it would just be "AppLoginItemXPCDemo". You can have SM login items without the sandbox. I just tested it. I think it is just that this functionality was specifically designed for the sandbox, since this is the only way to automatically launch.


The "load/unload" subcommands are not deprecated, just "legacy". I know of no other way to load/unload tasks.

I have tried reading the man page for

launchctl
so that I don't have to use the deprecated
load
subcommand but it is not clear from the man page what is the equivalent of just calling
launchctl load ~/Library/LaunchAgents/***.com.companyname.agentname
bootstrap
and
bootout
are the replacements for
load
and
unload
. However, they are much more complex, so for day-to-day stuff I still use the
load
and
unload
.

A restriction for LoginItems is that for

SMLoginItemSetEnabled
to work the application needs to be in the
/Applications
folder.

That’s not my experience.

When you test this, you need to separate the user experience from the developer experience. XPC login items rely on the Launch Services database, and on a developer machine that’s regularly messed up because developers are always building and rebuilding their app. User systems are generally more… sedate… and thus Launch Services works reasonably well there.

The readme.txt file for the example loginitem says that the LoginItem and application must be sandboxed.

That’s not the case. XPC login items were introduced for the benefit of Mac App Store apps — and hence a lot of that doc assumes that you’re creating a Mac App Store app, which must be sandboxed — but they do not require sandboxing.

There also seems more control over the behaviour of a LaunchAgent via its plist than just calling the

SMLoginItemSetEnabled
function with YES or NO. For example enabling means the LoginItem stays alive all the time and we don't need that behaviour.

So what lifecycle do you need? The whole purpose of a login item is to run all the time [1]. If you don’t need that, you may be able to get away with an XPC Service. Those are launched on demand and can terminate on idle. They are, however, bound to the lifecycle of your app, meaning that they’ll always be terminated when you app quits.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] For a given GUI login session.

Thanks for the info about load/unload.


> So what lifecycle do you need?


We have a task we want to hand off to a different process than the app that can continue if the app quits but that there is no reason for the process to continue after the task is completed. Communication using XPC feels right for the communication.


A restriction for LoginItems is that for

SMLoginItemSetEnabled
to work the application needs to be in the
/Applications
folder.

> That’s not my experience.


I couldn't get my loginitem to start, and read the blog post below that the app needs to be in the applications folder, copied the app to the Applications folder and the login item started as soon as I called SMLoginItemSetEnabled and was open for XPC communication.


http://martiancraft.com/blog/2015/01/login-items/


I was able to debug after that by attaching to both the login item and the application from within Xcode.


Further internal discussion, and we decided not to do launch agent or login item. I am replying for completeness sake.