macOS开机自启动应用swift源码实例

MacOS开发中很多场景会用到登录后应用自动运行功能,网上能查阅到的有限几篇文章大多都是object-c实现,并且版本比较久远,大多是关于macOS 11前后版本的实现,需要修改调整的地方较多 阅读本文,您将实现以swift来编码,同时兼容原有旧系统api以及macOS 13 Ventura新特性的自启动应用。

  • Swift实现登录自启动
  • 兼容macOS 13 Ventura,新Api SMAppService
  • 自启动应用上线审核规范

新建SwiftUI工程TestAutoStartApp

import SwiftUI

@main
struct TestAutoStartAppApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

@MainActor
private final class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        //在启动主项目的时候 发送通知 关闭开机启动帮助程序
        DistributedNotificationCenter.default.post(name: NSNotification.Name(rawValue: "TerminateAppHelperNotificationName"), object: Bundle.main.bundleIdentifier)
    }
}

主界面

import SwiftUI

struct ContentView: View {
    
    /** 开机启动 */
    @State private var startup = false
    
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                HStack {
                    Text(LocalizedStringKey("开机启动"))
                    Text(":")
                }
                .frame(width: 80.0, alignment: .trailing)
                .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
                
                Toggle("", isOn: $startup)
                    .controlSize(.large)
                    .onChange(of: startup) { newValue in
//                        if !canStartupChange {
//                            return
//                        }
//                        canStartupChange = true
                        //print(newValue)
                        if Preference.startup == newValue {
                            return
                        }
                        Preference.startup = newValue
                        AppAutoStartUtils.setAppAutoStart(isAutoStart: newValue)
                    }
                //.toggleStyle(CheckBoxToggleStyle(shape: .square))
            }
            //.frame(width: 200.0, height: 100.0)
        }
        .frame(width: 240, height: 90)
        .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
    }

}

Preference类:存储自启动开启/关闭状态

import Foundation

extension UserDefaults {
    // 通过下标使用枚举
    subscript<T: RawRepresentable>(key: String) -> T? {
        get {
            if let rawValue = value(forKey: key) as? T.RawValue {
                return T(rawValue: rawValue)
            }
            return nil
        }
        set { set(newValue?.rawValue, forKey: key) }
    }
    
    subscript<T>(key: String) -> T? {
        get { return value(forKey: key) as? T }
        set { set(newValue, forKey: key) }
    }
}

struct Preference {
    
    /** 开机启动 */
    static var startup: Bool {
        get { return UserDefaults.standard[#function] ?? false }
        set { UserDefaults.standard[#function] = newValue }
    }
}

AppAutoStartUtils类:设置自启动工具类

//
//  AppAutoStartUtils.swift
//  TestAutoStartApp
//
//  Created by bin zhu on 2023/7/2.
//

import Foundation
import ServiceManagement
import AppKit

class AppAutoStartUtils {
    
    static func setAppAutoStart(isAutoStart: Bool) {
        //这里注意改成自己的帮助工程名称
        var launcherAppUrl = Bundle.main.bundleURL
        launcherAppUrl = launcherAppUrl.appendingPathComponent("Contents/Library/LoginItems/LaunchHelper.app")
        let launcherAppPath = launcherAppUrl.path
        //print(launcherAppPath)
        
        if !FileManager.default.fileExists(atPath: launcherAppPath) {
            print("launcherApp no exist")
            print("(\(launcherAppPath))")
            return
        }
        
        if #available(macOS 13.0, *) {
            if isAutoStart {
                addLoginItem()
            }
            else {
                removeLoginItem()
            }
        } else {
            if (!SMLoginItemSetEnabled(AppConfig.LauncherAppBundleId as CFString, isAutoStart))
            {
                print("SMLoginItemSetEnabled failed!");
            }
        }
        
        var alreadRunning = false;
        let runnings = NSWorkspace.shared.runningApplications;
        for app in runnings {
            //这里是帮助项目的bundle identifier
            if app.bundleIdentifier == AppConfig.LauncherAppBundleId {
                alreadRunning = true;
                break;
            }
        }
        if alreadRunning {
            //发送终止帮助程序的通知
            DistributedNotificationCenter.default().post(name: NSNotification.Name(rawValue: "TerminateAppHelperNotificationName"), object: Bundle.main.bundleIdentifier)
        }
    }
    
    // 创建登录项
    @available(macOS 13.0, *)
    private static func loginItem() -> SMAppService {
        // The bundle identifier of the helper application bundle.
        return SMAppService.loginItem(identifier: AppConfig.LauncherAppBundleId)
    }

    // 添加登录项
    @available(macOS 13.0, *)
    static func addLoginItem() {
        do {
            try loginItem().register()
            print("auto start register ok")
        } catch {
            print(error)
        }
    }

    // 移除登录项
    @available(macOS 13.0, *)
    static func removeLoginItem() {
        do {
             try loginItem().unregister()
            print("auto start unregister ok")
        } catch {
             print(error)
        }
    }
}


运行效果

新建Target

名称LaunchHelper,使用xib就可以,这个Target是后台运行没有显示界面

import Cocoa

@main
class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet var window: NSWindow!


    func applicationDidFinishLaunching(_ aNotification: Notification) {
        //主项目的bundle identifier
        let mainAppBundleId = "com.cosin.TidyBar"
        var alreadRunning = false
        let runnings = NSWorkspace.shared.runningApplications
        for app in runnings {
            if app.bundleIdentifier == mainAppBundleId {
                alreadRunning = true
                break;
            }
        }
        
        if (!alreadRunning) {
            //这里是注册的关闭帮助程序的通知监听
            DistributedNotificationCenter.default().addObserver(self, selector: #selector(terminate), name: NSNotification.Name(rawValue: "TerminateAppHelperNotificationName"), object: mainAppBundleId)

            
            guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: mainAppBundleId) else {
                print("bundleId not found(\(mainAppBundleId)")
                return
            }
            
            let configuration = NSWorkspace.OpenConfiguration()
            NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil)
        }
        else {
            NSApp.terminate(nil)
        }
    }

    @objc fileprivate func terminate(){
        //方法调用
        NSApp.terminate(nil)
    }

    func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
        return true
    }
}

修改设置

设置 LaunchHelp中的Application is Background Only : true
如果你使用的xcode14.3.1你可能也会遇到这个诡异问题内容显示不全,这里的info无法修改,value都看不到,可能是版本bug,也无法拖拉调整

不得已找到下图位置修改

设置LaunchHelper中Skill install:yes

在主项目中添加CopyFile到Contents/Library/LoginItems

完整完成上述操作,编译运行,主界面中勾选开机启动,之后重启系统看看是否生效

关于AppStore提交审核

苹果审核要求自启动应用,需要默认不开启,提供开启关闭选项让用户自己去决定是否使用。否则你的应用会被拒,违反的审核条例如下:

Guideline 2.4.5(iii) - Performance

Your app sets itself to auto-launch at startup without express consent from the user.

Specifically, Settings > Startup is pre-check marked.

Next Steps

You can resolve this by leaving this option unchecked by default, providing the user the option to turn it on.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 如果你想在 macOS 上使用 Java 实现应用程序开机自启动,你需要做以下步骤: 1. 首先,你需要确保你的计算机上已经安装了 Java 环境,并且你已经编写好了你要自动启动的 Java 程序。 2. 然后,你需要打开 macOS 的「用户与群组」设置,选择「登录项」标签。 3. 在「登录项」标签中,你可以看到「自动启动程序」列表,点击「+」号按钮,选择你要自动启动的 Java 程序的执行文件。 4. 最后,你需要将你的 Java 程序加入「自动启动程序」列表中,这样它就会在你的计算机开机时自动启动。 注意:如果你的 Java 程序是由可执行的 JAR 文件生成的,你可能需要在执行文件前面加上 "java -jar" 命令,才能让系统正确地启动你的程序。 ### 回答2: 在macOS中,我们可以使用Java编写一个应用程序,并实现开机自启动的功能。下面是使用Java实现应用程序开机自启动的步骤: 步骤1:创建一个Java应用程序。可以使用Java开发工具,如Eclipse或IntelliJ IDEA,创建一个新的Java项目。在项目中编写我们的应用程序代码。 步骤2:创建一个启动脚本。在macOS中,我们可以使用shell脚本来启动我们的Java应用程序。创建一个文本文件,将以下内容复制粘贴到文件中: ``` #!/bin/sh java -jar /path/to/your/java/application.jar ``` 将`/path/to/your/java/application.jar`替换为你的Java应用程序的路径。 步骤3:将启动脚本添加到用户的登录项中。打开“系统偏好设置” -> “用户与群组” -> “登录项”,将启动脚本拖放到列表中。 步骤4:设置脚本的可执行权限。在终端中,使用以下命令修改脚本的权限: ``` chmod +x /path/to/your/startup/script.sh ``` 将`/path/to/your/startup/script.sh`替换为你的启动脚本的路径。 现在,当你的macOS启动时,你的Java应用程序将自动启动。 需要注意的是,以上步骤仅适用于当前登录的用户。如果你希望所有用户都能够启动应用程序,可以将启动脚本添加到“/Library/LaunchAgents”目录下。 ### 回答3: 要实现 macOS 上的应用程序开机自启动,可以使用Java编写一个小型的启动器程序来实现。 首先,需要创建一个启动器程序,它可以在系统启动时自动运行,并负责启动您的Java应用程序。这个启动器程序可以是一个独立的Java应用程序,可以在系统启动时由操作系统自动运行,或者可以将其作为系统服务运行。 为了实现启动器程序,在Java中可以使用`java.awt.Desktop`类来打开指定的应用程序。可以在启动器程序中使用以下代码来启动您的Java应用程序: ``` import java.awt.Desktop; import java.io.File; import java.io.IOException; public class Launcher { public static void main(String[] args) throws IOException { String appPath = "/Applications/YourApp.app"; // 替换为您的应用程序的路径 File appFile = new File(appPath); if (Desktop.isDesktopSupported()) { Desktop.getDesktop().open(appFile); } } } ``` 请注意,您需要将路径`/Applications/YourApp.app`替换为您的应用程序的准确路径。确保路径是指向您的应用程序的实际位置。 然后,将编译后的启动器程序与您的应用程序一起打包,并将启动器程序添加到您的macOS用户登录项中。这样,当用户登录时,启动器程序将自动运行,并启动您的Java应用程序。 要将启动器程序添加到登录项中,您可以打开“系统偏好设置”,然后选择“用户与群组”。在用户偏好设置中的“登录项”选项中,可以将启动器程序拖动到该列表中,从而实现开机自启动。 这样,在macOS启动时,启动器程序将自动运行,并启动您的Java应用程序,实现应用程序的开机自启动
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ArslanRobot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值