Start dockless apps at login with App Sandbox enabled
The Problem
If we want to run a dockless application at login and we are using Mac OS X 10.5 or later, we can add login items using a shared file list with LaunchServices/LSSharedFileList.h API.
This technique works well until App Sandbox has been introduced. Now, with the file permissions enforcements, accessing the shared file list of login items is forbidden.
This is remarked by Apple here:
“With App Sandbox, you cannot create a login item using functions in the
LSSharedFileList.hheader file. For example, you cannot use the functionLSSharedFileListInsertItemURL.”
The Solution Recommended by Apple
In the “App Sandbox Design Guide”, Apple says:
“Instead, use the
SMLoginItemSetEnabledfunction along with theLSRegisterURLfunction, as described in “Adding Login Items Using the Service Management Framework” in Daemons and Services Programming Guide.”
Here the details:
“Applications can contain a helper application as a full application bundle, stored inside the main application bundle in the
Contents/Library/LoginItemsdirectory. Set either theLSUIElementorLSBackgroundOnlykey in theInfo.plistfile of the helper application’s bundle.Use the
SMLoginItemSetEnabledfunction (available in Mac OS X v10.6.6 and later) to enable a helper application. It takes two arguments, a CFStringRef containing the bundle identifier of the helper application, and a Boolean specifying the desired state. Pass true to start the helper application immediately and indicate that it should be started every time the user logs in. Pass false to terminate the helper application and indicate that it should no longer be launched when the user logs in. This function returns true if the requested change has taken effect; otherwise, it returns false. This function can be used to manage any number of helper applications.Note: Before calling the
SMLoginItemSetEnabledfunction, first register with Launch Services by calling theLSRegisterURLfunction with the URL for the helper application bundle.”
The Helper Application
Why not directly set the application with SMLoginItemSetEnabled? Why do we need to use a helper application?
Because the SMLoginItemSetEnabled function works only with executables stored in the Contents/Library/LoginItems directory of the bundle.
Taking into account this, we can create a minimal (i.e. helper) application with these features:
- Standalone, sandboxed and sharing the same bundle.
- Stored in
Contents/Library/LoginItems. - It must load the main application and then terminate.
The first point can be accomplished creating a new project, enabling App Sandboxing and adding it to the main application project. It’s out of topic explain how to do this, there is plenty of tutorials about this argument.
To store the helper app binary in Contents/Library/LoginItems enter the main project info, click on target and go into the Build Phases tab. Here go to the Copy Files detail and set Wrapper as Destination, Contents/Library/LoginItems as Subpath, leave unchecked Copy only when installing and add the Helper Application binary in the list:
Then insert a code like this to load your main application applicationDidFinishLaunching:
[[NSWorkspace sharedWorkspace] launchApplication:
@"/Path/To/Main/App/Bundle"];
To enable the helper app the main app must:
- Register the helper application.
- Enable or disable helper application startup loading.
This can be accomplished by the following code:
#import <ServiceManagement/ServiceManagement.h>
+ (void)setStartAtLogin:(NSURL *)bundleURL enabled:(BOOL)enabled {
// Creating helper app complete URL
NSURL *url = [bundleURL URLByAppendingPathComponent:
@"Contents/Library/LoginItems/MyHelperApp.app"];
// Registering helper app
if (LSRegisterURL((CFURLRef)url, true) != noErr) {
NSLog(@"LSRegisterURL failed!");
}
// Setting login
if (!SMLoginItemSetEnabled((CFStringRef)@"com.mycompany.MyHelperApp",
enabled)) {
NSLog(@"SMLoginItemSetEnabled failed!");
}
}
/Applications (not in your Xcode build folder), otherwise you’ll get a sandbox violation. And SMLoginItemSetEnabled appears to work even when LSRegisterURL fails.
Some Tips
Setting an application to auto-launch at startup without express consent from the user is forbidden by the Mac App Store Review Guideline #2.26, so if you want to submit your program to the Mac App Store you can:
- Leave it off by default and make it customizable.
- Show an alert to get the consent from the user.
[...] by the sandbox is adding a login item. A good tutorial on creating login items is available at delite studio, although some changes to the code are needed. That code follows Apple’s earlier guideline of [...]