如何让程序在登录时自动启动?

时间:2014-05-13 07:32:44

标签: objective-c macos cocoa

如何设置菜单栏应用以在登录时自动启动?我希望这是默认的。我可以通过在info.plist中添加一个bool来实现吗?

4 个答案:

答案 0 :(得分:14)

(此解决方案仅适用于非沙盒应用。此解决方案中使用的LSSharedFile函数仅适用于非沙盒应用。)

您使用会话登录项共享文件列表。当您在个人资料设置下检查登录项时,这是“系统偏好设置”中显示的列表。

应用程序中的典型情况是在应用程序的首选项中提供一个复选框,允许用户选择是否要在启动应用程序时启动应用程序。如果您打算通过应用程序商店分发,请不要将应用程序设置为默认登录时启动。你会被拒绝:)

因此,在这种情况下,我们将在App Delegate中创建一个名为“launchOnLogin”的属性,我们将复选框的值绑定到此属性。

getter方法将检查我们的app的bundle id是否在共享列表中并返回true或false。

setter方法在共享列表中广告或删除该项目。

这里是:

<强> 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);
                }
            }
        }
    }
}

这就是它。现在,如果您正确地执行绑定和所有操作,您将看到您的应用程序如何实时显示并从系统首选项列表中实时消失。

实际在登录时启动应用程序的部分

// 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);

请注意,上面的代码是保留/释放,但如果您需要,将它转换为ARC非常简单。

希望这有帮助。

答案 1 :(得分:3)

对于正在寻找此代码的更新Swift兼容版本的任何人,我在这里做了一个要点:https://gist.github.com/plapier/f8e1dde1b1624dfbb3e4

只需在您的应用中调用toggleLaunchAtStartup()即可。 (很可能在复选框IBAction内)。

实际参考代码:

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);
            }
        }
    }
}

与Xcode 6.3.2,Swift 1.2一起使用

答案 2 :(得分:2)

您可以从应用程序运行下面给出的Apple脚本,将其添加到Login Item。

on run args_list
    tell application "System Events" to make login item at end with properties {path:item 1 of args_list, hidden:false}
end run

将上述脚本保存为AddToLogin.scpt并将其添加到项目中。

将以下给出的代码添加到应用程序启动

NSString *appPath =[[NSBundle mainBundle] bundlePath] ;
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"AddToLogin" ofType:@"scpt"];

NSArray *argArray;
if(scriptPath)
{
    argArray = [NSArray arrayWithObjects:scriptPath, appPath, nil];
    NSTask * task = [[NSTask alloc] init];
    [task setLaunchPath:@"/usr/bin/osascript"];
    [task setArguments:argArray];
    [task launch];
    [task waitUntilExit];
}

下面给出的链接解释了执行相同操作的osascript方法

http://hints.macworld.com/article.php?story=20111226075701552

答案 3 :(得分:0)

因为这个答案已经有很多年了,所以有一个很好的 Swift 包可以处理这个问题

https://github.com/sindresorhus/LaunchAtLogin