How can I make program automatically startup on login?
For anyone looking for an updated Swift compatible version of this code, I've made a gist here: https://gist.github.com/plapier/f8e1dde1b1624dfbb3e4
Just call toggleLaunchAtStartup()
from within your app. (Most likely inside a checkbox IBAction
).
And the actual code for reference:
import Foundation
func applicationIsInStartUpItems() -> Bool {
return (itemReferencesInLoginItems().existingReference != nil)
}
func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
if let appURL : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
if let loginItemsRef = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef? {
let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as! LSSharedFileListItemRef
for (index, loginItem) in enumerate(loginItems) {
let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(index) as! LSSharedFileListItemRef
if let itemURL = LSSharedFileListItemCopyResolvedURL(currentItemRef, 0, nil) {
if (itemURL.takeRetainedValue() as NSURL).isEqual(appURL) {
return (currentItemRef, lastItemRef)
}
}
}
return (nil, lastItemRef)
}
}
return (nil, nil)
}
func toggleLaunchAtStartup() {
let itemReferences = itemReferencesInLoginItems()
let shouldBeToggled = (itemReferences.existingReference == nil)
if let loginItemsRef = LSSharedFileListCreate( nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef? {
if shouldBeToggled {
if let appUrl : CFURLRef = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
println("Add login item")
LSSharedFileListInsertItemURL(loginItemsRef, itemReferences.lastReference, nil, nil, appUrl, nil, nil)
}
} else {
if let itemRef = itemReferences.existingReference {
println("Remove login item")
LSSharedFileListItemRemove(loginItemsRef,itemRef);
}
}
}
}
Used with Xcode 6.3.2, Swift 1.2
(This solution is for non-sandboxed apps only. The LSSharedFile
functions used in this solution are available only to non-sandboxed apps.)
You use the Session Login Items shared file list. This is the list that is displayed in System Preferences when you check your login items under your profile settings.
The typical scenario in an app is to provide a checkbox in the app's preferences, that allows the user to choose wether they want to start the app or not on login. If you intend to distribute through the app store DO NOT set the app to launch on login by default. You will get rejected :)
So, in this scenario, we will make a property, say in the App Delegate, called "launchOnLogin" and we will bind the value of the check box to this property.
The getter method will check if our app's bundle id is in the shared list and return true or false.
The setter method ads or removes the item from the shared list.
Here goes:
AppDelegate.h
@property (atomic, assign) BOOL launchOnLogin;
AppDelegate.m
- (BOOL)launchOnLogin
{
LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
CFArrayRef snapshotRef = LSSharedFileListCopySnapshot(loginItemsListRef, NULL);
NSArray* loginItems = [NSMakeCollectable(snapshotRef) autorelease];
NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
for (id item in loginItems) {
LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item;
CFURLRef itemURLRef;
if (LSSharedFileListItemResolve(itemRef, 0, &itemURLRef, NULL) == noErr) {
NSURL *itemURL = (NSURL *)[NSMakeCollectable(itemURLRef) autorelease];
if ([itemURL isEqual:bundleURL]) {
return YES;
}
}
}
return NO;
}
- (void)setLaunchOnLogin:(BOOL)launchOnLogin
{
NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (launchOnLogin) {
NSDictionary *properties;
properties = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"com.apple.loginitem.HideOnLaunch"];
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsListRef, kLSSharedFileListItemLast, NULL, NULL, (CFURLRef)bundleURL, (CFDictionaryRef)properties,NULL);
if (itemRef) {
CFRelease(itemRef);
}
} else {
LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
CFArrayRef snapshotRef = LSSharedFileListCopySnapshot(loginItemsListRef, NULL);
NSArray* loginItems = [NSMakeCollectable(snapshotRef) autorelease];
for (id item in loginItems) {
LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item;
CFURLRef itemURLRef;
if (LSSharedFileListItemResolve(itemRef, 0, &itemURLRef, NULL) == noErr) {
NSURL *itemURL = (NSURL *)[NSMakeCollectable(itemURLRef) autorelease];
if ([itemURL isEqual:bundleURL]) {
LSSharedFileListItemRemove(loginItemsListRef, itemRef);
}
}
}
}
}
That's pretty much it. Now if you do the binding and everything correctly, you'll see how your app appears and dissappears from the System Preferences list in realtime.
The part that actually makes the app launch on login
// Get the path of the app
NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
// Get the list you want to add the path to
LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
// Set the app to be hidden on launch
NSDictionary *properties = @{@"com.apple.loginitem.HideOnLaunch": @YES};
// Add the item to the list
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsListRef, kLSSharedFileListItemLast, NULL, NULL, (CFURLRef)bundleURL, (CFDictionaryRef)properties,NULL);
Please note that the code above is retain/release but it is pretty trivial to convert it to ARC if you need it.
Hope this helps.