Login items in the sandbox

Apple will soon require all Mac apps submitted to the app store to be sandboxed for heightened security, which means it needs to request permission for doing even basic things like accessing files or connecting to the internet. A lot of things like disk burning aren’t allowed at all in the sandbox, and some things like sending AppleEvents require temporary exemptions which will be phased out.

One common task that’s complicated 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 using LSRegisterURL to register your helper app. However, I found that it always fails with error -10819. According to an Apple engineer in Apple’s developer forum, you should not call LSRegisterURL in a sandboxed app.

Note that your application can’t add itself as a login item. You need to write a simple helper app that launches your main app and it must be included in the main application bundle in the relative path /Contents/Library/LoginItems/.

The method for adding & removing a login item turned out to be very simple:

- (void)addLoginItem {
    NSString *ref = @"com.madebynotion.myLoginHelper";
	if (!SMLoginItemSetEnabled((CFStringRef)ref, true)) {
		NSLog(@"SMLoginItemSetEnabled failed.");
	}
}

- (void)removeLoginItem {
    NSString *ref = @"com.madebynotion.myLoginHelper";
	if (!SMLoginItemSetEnabled((CFStringRef)ref, false)) {
		NSLog(@"SMLoginItemSetEnabled failed.");
	}
}

If you need to find out whether your login item is enabled, here’s a way to do it:

-(BOOL)appIsPresentInLoginItems
{
    NSString *bundleID = @"com.madebynotion.myLoginHelper";
    NSArray * jobDicts = nil;
    jobDicts = (NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd );
    // Note: Sandbox issue when using SMJobCopyDictionary()
    
    if ( (jobDicts != nil) && [jobDicts count] > 0 ) {
        
        BOOL bOnDemand = NO;
        
        for ( NSDictionary * job in jobDicts ) {
            
            if ( [bundleID isEqualToString:[job objectForKey:@"Label"]] ) {
                bOnDemand = [[job objectForKey:@"OnDemand"] boolValue];
                break;
            } 
        }
        
        CFRelease((CFDictionaryRef)jobDicts); jobDicts = nil;
        return bOnDemand;
        
    } 
    return NO;
}

Although this method works and seems to be the preferred way to do it in the sandbox, it’s less optimal than the old non-sandbox method, since your login item won’t appear in the users & groups preference panel’s login items list and can only be turned on & off from your own application.

5 Responses to Login items in the sandbox

  1. Can you share the code from your helper app, too?

  2. Tthanks for this post!
    Could you kindly release the code for your helper app?

  3. Thank you for this great article!
    I've got some problems with the codesign and the entitlements for the Helper App. When I try to validate the Main app which include the Helper, I get errors related to entitlements and provisioning profile. If I remove the Helper from the Bundle it works like a charm!
    Do you have some suggestions?

  4. I use this method in my application, it seems Tim Schroeder have the same solution (http://blog.timschroeder.net/2012/07/03/the-launch-at-login-sandbox-project/). it works. But when I was update my OS X from 10.8.1 to 10.8.2 app doesn't start automatically. I need to go to the settings of application and make it login item again.

  5. https://github.com/ianyh/IYLoginItem

    Works like a charm, and you can install it with cocoapods!

Leave a Reply