You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

已实现Mac登录启动助手应用,但登录时无法自动启动

Hey,我刚好折腾过Mac应用登录自动启动的功能,下面是一套完整的实现方案,要是你按步骤做完后发现应用没自动启动,也可以看看最后的排查要点:

实现Mac应用登录自动启动的完整方案

一、项目配置步骤

首先得给主应用配一个专门的登录助手Target,步骤如下:

  • 创建新的Cocoa Application Target(作为登录助手应用,命名比如Helper
  • 在助手Target的Build Settings里,找到Skip Install选项,设置为YES
  • 编辑助手应用的Info.plist,添加Application is background only字段并设置为YES(让助手在后台运行,不显示Dock图标)
  • 确保主应用和助手应用都启用了沙盒(App Sandbox)(沙盒环境下也能实现登录启动,别漏了)
  • 为主应用添加Copy File Phase:目标选择Wrapper,路径填写Contents/Library/LoginItems,然后把助手应用的Helper.app添加进去(这样主应用打包时会把助手放在正确的位置)

二、助手应用(Helper)代码实现

替换助手应用的AppDelegate.swift代码如下:

import Cocoa
import ServiceManagement

extension Notification.Name {
    static let killLauncher = Notification.Name("killLauncher")
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    @objc func terminate() {
        NSApp.terminate(nil)
    }

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        print("助手应用启动")
        // 替换成你的主应用Bundle Identifier
        let mainAppIdentifier = "co.myprogress.osx"
        let runningApps = NSWorkspace.shared.runningApplications
        let isMainAppRunning = !runningApps.filter { $0.bundleIdentifier == mainAppIdentifier }.isEmpty
        
        if !isMainAppRunning {
            // 监听主应用发来的终止通知
            DistributedNotificationCenter.default().addObserver(self, selector: #selector(self.terminate), name: .killLauncher, object: mainAppIdentifier)
            
            // 拼接主应用的可执行文件路径
            let path = Bundle.main.bundlePath as NSString
            var components = path.pathComponents
            components.removeLast() // 移除Helper.app
            components.removeLast() // 移除LoginItems
            components.removeLast() // 移除Library
            components.append("MacOS")
            // 替换成你的主应用可执行文件名称(在主应用Info.plist的CFBundleExecutable字段查看)
            components.append("TODOs Menubar")
            let mainAppPath = NSString.path(withComponents: components)
            
            // 启动主应用
            NSWorkspace.shared.launchApplication(mainAppPath)
        } else {
            // 主应用已经在运行,直接终止助手
            self.terminate()
        }
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // 可选:添加应用终止时的清理逻辑
    }
}

三、主应用代码实现

在主应用的AppDelegate.swift中添加如下代码:

import Cocoa
import ServiceManagement

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // 替换成你的助手应用Bundle Identifier
    let launcherAppId = "co.myprogress.TodosMenubarHelper"
    let runningApps = NSWorkspace.shared.runningApplications
    let isHelperRunning = !runningApps.filter { $0.bundleIdentifier == launcherAppId }.isEmpty
    
    // 启用登录项,让系统在登录时启动助手
    let enableResult = SMLoginItemSetEnabled(launcherAppId as CFString, true)
    print("登录项启用结果:\(enableResult)") // 打印true表示设置成功
    
    if isHelperRunning {
        // 通知助手应用终止,避免重复运行
        DistributedNotificationCenter.default().post(name: .killLauncher, object: Bundle.main.bundleIdentifier!)
    }
}

四、测试流程

按下面的步骤验证功能:

  • 按下Cmd + B构建整个项目
  • 在Xcode的Product目录中右键点击主应用的.app文件,选择"Show in Finder"
  • 双击启动主应用
  • 注销当前账户后重新登录,检查主应用是否自动启动

五、常见问题排查(针对应用未自动启动的情况)

如果按上面的步骤操作后,应用还是没自动启动,可以检查这些点:

  • Bundle Identifier匹配:确保助手代码中的mainAppIdentifier和主应用的Bundle ID完全一致,主应用代码中的launcherAppId和助手的Bundle ID也完全一致(大小写、拼写都不能错)
  • 沙盒设置:确认主应用和助手应用的App Sandbox都已勾选,并且如果需要网络等权限,也要在沙盒设置中开启
  • 登录项列表:进入系统设置 > 通用 > 登录项,看看你的助手应用是否在"允许在登录时打开"的列表里,如果不在,大概率是Copy File Phase的路径配置错了
  • 可执行文件名称:助手代码中components.append("TODOs Menubar")这里的名称,要和主应用Info.plist里CFBundleExecutable字段的值完全一致
  • 日志排查:打开Mac自带的Console.app,搜索助手应用的Bundle Identifier,看看有没有启动失败的报错信息,能帮你定位具体问题

内容的提问来源于stack exchange,提问作者safaiyeh

火山引擎 最新活动