September 25 2010
Cocoa Tip: Enabling "Launch on Startup"
by Brian Dunagan
When I start up my Mac, I have a number of apps launch, including Jing, Evernote, LaunchBar, and BusySync. Each registers itself as a “Login Item”, listed in System Preferences under Accounts. Developers have different names for this option, like “Launch on Startup” or “Launch at Login”.
In 10.4 and older, Mac OS X enabled this feature through AppleScript, and Apple has sample code for that in LoginItemsAE . In 10.5 and 10.6, the API is in LSSharedFileList.h , buried in the LaunchServices framework:
/System/Library/Frameworks/CoreServices.framework/
Frameworks/LaunchServices.framework/Headers/
I played around with this API a bit today and came up with a couple wrapper methods to abstract away the details: isLaunchAtStartup and toggleLaunchAtStartup: . Feel free to use this code.
// MIT license
- ( BOOL ) isLaunchAtStartup {
// See if the app is currently in LoginItems.
LSSharedFileListItemRef itemRef = [ self itemRefInLoginItems ];
// Store away that boolean.
BOOL isInList = itemRef != nil ;
// Release the reference if it exists.
if ( itemRef != nil ) CFRelease ( itemRef );
return isInList ;
}
- ( IBAction ) toggleLaunchAtStartup :( id ) sender {
// Toggle the state.
BOOL shouldBeToggled = ! [ self isLaunchAtStartup ];
// Get the LoginItems list.
LSSharedFileListRef loginItemsRef = LSSharedFileListCreate ( NULL , kLSSharedFileListSessionLoginItems , NULL );
if ( loginItemsRef == nil ) return ;
if ( shouldBeToggled ) {
// Add the app to the LoginItems list.
CFURLRef appUrl = ( CFURLRef )[ NSURL fileURLWithPath :[[ NSBundle mainBundle ] bundlePath ]];
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL ( loginItemsRef , kLSSharedFileListItemLast , NULL , NULL , appUrl , NULL , NULL );
if ( itemRef ) CFRelease ( itemRef );
}
else {
// Remove the app from the LoginItems list.
LSSharedFileListItemRef itemRef = [ self itemRefInLoginItems ];
LSSharedFileListItemRemove ( loginItemsRef , itemRef );
if ( itemRef != nil ) CFRelease ( itemRef );
}
CFRelease ( loginItemsRef );
}
- ( LSSharedFileListItemRef ) itemRefInLoginItems {
LSSharedFileListItemRef itemRef = nil ;
NSURL * itemUrl = nil ;
// Get the app's URL.
NSURL * appUrl = [ NSURL fileURLWithPath :[[ NSBundle mainBundle ] bundlePath ]];
// Get the LoginItems list.
LSSharedFileListRef loginItemsRef = LSSharedFileListCreate ( NULL , kLSSharedFileListSessionLoginItems , NULL );
if ( loginItemsRef == nil ) return nil ;
// Iterate over the LoginItems.
NSArray * loginItems = ( NSArray * ) LSSharedFileListCopySnapshot ( loginItemsRef , nil );
for ( int currentIndex = 0 ; currentIndex < [ loginItems count ]; currentIndex ++ ) {
// Get the current LoginItem and resolve its URL.
LSSharedFileListItemRef currentItemRef = ( LSSharedFileListItemRef )[ loginItems objectAtIndex : currentIndex ];
if ( LSSharedFileListItemResolve ( currentItemRef , 0 , ( CFURLRef * ) & itemUrl , NULL ) == noErr ) {
// Compare the URLs for the current LoginItem and the app.
if ([ itemUrl isEqual : appUrl ]) {
// Save the LoginItem reference.
itemRef = currentItemRef ;
}
}
}
// Retain the LoginItem reference.
if ( itemRef != nil ) CFRetain ( itemRef );
// Release the LoginItems lists.
[ loginItems release ];
CFRelease ( loginItemsRef );
return itemRef ;
}