SMLoginItemSetEnabled sometimes silently fails to

2019-02-06 13:12发布

问题:

I have an app that is sandboxed, and includes a helper that presents some UI (as a full-screen window, but could be a status item or similar too).

This works... most of the time. But sometimes it doesn't; it just silently fails to start the helper.

Since the helper has UI, I use SMLoginItemSetEnabled to load it, then NSXPCConnection to communicate with it. But sometimes SMLoginItemSetEnabled fails to launch it, while still returning YES.

This seems to be due to an old build of the app somewhere on the machine; that seems to confuse the login mechanism. Deleting the old app fixes it, but I can't reasonably expect users to do this (some people like to keep old versions around).

I can detect this situation by comparing the result of -[NSWorkspace URLForApplicationWithBundleIdentifier:] with the URL of the helper in the app bundle, but having to ask the user to remove the other app isn't a very elegant solution.

Is there any way to make SMLoginItemSetEnabled always use the login item from the current app bundle, rather than some random one elsewhere on the disk?

Or has anything changed in recent OS releases to support a more elegant mechanism for helpers with UI?

I've read many other questions here and elsewhere on this topic, and it appears that this clunky mechanism is still the best solution, but maybe I missed something.

回答1:

Is there any way to make SMLoginItemSetEnabled always use the login item from the current app bundle, rather than some random one elsewhere on the disk?

It seems that there is is a bug in SMLoginItemSetEnabled. When I test my application, the executable is in the DerivedData folder of Xcode.

When I build the release, I put the application and it's helper in the /Applications folder. But for some obvious reasons, the helper that is launched is the helper which is in the DeriveData folder. That's why I'm used to remove everything that is in this folder before launching the main application in /Applications.



回答2:

Highlighting a source / explanation posted in the comments:

If multiple applications (for example, several applications from the same company) contain a helper application with the same bundle identifier, only the one with the greatest bundle version number is launched. Any of the applications that contain a copy of the helper application can enable and disable it.

https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLoginItems.html#//apple_ref/doc/uid/10000172i-SW5-SW1

Thus a potential fix for this issue is to increment the bundle version number of the helper app. The new version with the greatest version number will then be the one that is launched.



回答3:

The system seems to log the location of the login item it's about to launch via LSApplicationCheckIn():

LSApplicationCheckIn(), app being registered is: "/Applications/YourApp.app/Contents/Library/LoginItems/YourLoginItem.app/Contents/MacOS/YourLoginItem"

If you force quit the login item from Activity Monitor, it will be relaunched. Check the console log above to determine the location of the new instance being launched.