使 Swift Cocoa 应用在 OS X 10.11 启动时启动
Make Swift Cocoa app launch on startup on OS X 10.11
我需要编写一个函数,将我的应用程序添加到 OS X 10.11 上的启动项。这就是我目前发现的:
func applicationIsInStartUpItems() -> Bool {
return (itemReferencesInLoginItems().existingReference != nil)
}
func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
if let appUrl : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
let loginItemsRef = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef?
if loginItemsRef != nil {
let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
if(loginItems.count > 0) {
let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as! LSSharedFileListItemRef
for var i = 0; i < loginItems.count; ++i {
let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(i) as! LSSharedFileListItemRef
if let itemURL = LSSharedFileListItemCopyResolvedURL(currentItemRef, 0, nil) {
if (itemURL.takeRetainedValue() as NSURL).isEqual(appUrl) {
return (currentItemRef, lastItemRef)
}
}
}
return (nil, lastItemRef)
} else {
let addatstart: LSSharedFileListItemRef = kLSSharedFileListItemBeforeFirst.takeRetainedValue()
return(nil,addatstart)
}
}
}
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) {
LSSharedFileListInsertItemURL(loginItemsRef, itemReferences.lastReference, nil, nil, appUrl, nil, nil)
}
} else {
if let itemRef = itemReferences.existingReference {
LSSharedFileListItemRemove(loginItemsRef,itemRef);
}
}
}
}
但是 LSSharedFileListCreate
、LSSharedFileListInsertItemURL
、LSSharedFileListItemRemove
、kLSSharedFileListItemBeforeFirst
、LSSharedFileListItemCopyResolvedURL
、LSSharedFileListCopySnapshot
、kLSSharedFileListSessionLoginItems
在OS X 10.11。如何在 Mac OS 的最新版本上运行?如何更改或重写此代码?
您现在必须使用服务管理框架。您创建一个辅助应用程序,将其添加到您的应用程序包中,它的工作是编写 运行 代码来启动您的主应用程序。一些资源供您使用:
- Tim Schröder excellent blog post 关于如何做到这一点
- Alex Zielenski open source project 可以帮助解决这个问题
- 我已经 video tutorial 手动创建它
在 Swift 3.0 中它看起来像这样:
在您的主应用程序 AppDelegate 中:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Check if the launcher app is started
var startedAtLogin = false
for app in NSWorkspace.shared().runningApplications {
if app.bundleIdentifier == NCConstants.launcherApplicationIdentifier {
startedAtLogin = true
}
}
// If the app's started, post to the notification center to kill the launcher app
if startedAtLogin {
DistributedNotificationCenter.default().postNotificationName(NCConstants.KILLME, object: Bundle.main.bundleIdentifier, userInfo: nil, options: DistributedNotificationCenter.Options.deliverImmediately)
}
}
在 Launcher 应用程序 AppDelegate 中:
func applicationDidFinishLaunching(_ aNotification: Notification) {
let mainAppIdentifier = "<main-app-bundle-id>"
let running = NSWorkspace.shared().runningApplications
var alreadyRunning = false
// loop through running apps - check if the Main application is running
for app in running {
if app.bundleIdentifier == mainAppIdentifier {
alreadyRunning = true
break
}
}
if !alreadyRunning {
// Register for the notification killme
DistributedNotificationCenter.default().addObserver(self, selector: #selector(self.terminate), name: NCConstants.KILLME, object: mainAppIdentifier)
// Get the path of the current app and navigate through them to find the Main Application
let path = Bundle.main.bundlePath as NSString
var components = path.pathComponents
components.removeLast(3)
components.append("MacOS")
components.append("<your-app-name>")
let newPath = NSString.path(withComponents: components)
// Launch the Main application
NSWorkspace.shared().launchApplication(newPath)
}
else {
// Main application is already running
self.terminate()
}
}
func terminate() {
print("Terminate application")
NSApp.terminate(nil)
}
最终,我在主应用程序中添加了一个带有切换按钮的用户界面。用户可以选择是否在登录时启动应用程序。选择存储在 UserDefaults 中。
在视图控制器中:
@IBAction func toggleLaunchAtLogin(_ sender: Any) {
if toggleOpenAppLogin.selectedSegment == 0 {
if !SMLoginItemSetEnabled(NCConstants.launcherApplicationIdentifier as CFString, true) {
print("The login item was not successfull")
toggleOpenAppLogin.setSelected(true, forSegment: 1)
}
else {
UserDefaults.standard.set("true", forKey: "appLoginStart")
}
}
else {
if !SMLoginItemSetEnabled(NCConstants.launcherApplicationIdentifier as CFString, false) {
print("The login item was not successfull")
toggleOpenAppLogin.setSelected(true, forSegment: 0)
}
else {
UserDefaults.standard.set("false", forKey: "appLoginStart")
}
}
}
希望对大家有所帮助。
我需要编写一个函数,将我的应用程序添加到 OS X 10.11 上的启动项。这就是我目前发现的:
func applicationIsInStartUpItems() -> Bool {
return (itemReferencesInLoginItems().existingReference != nil)
}
func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
if let appUrl : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
let loginItemsRef = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef?
if loginItemsRef != nil {
let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
if(loginItems.count > 0) {
let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as! LSSharedFileListItemRef
for var i = 0; i < loginItems.count; ++i {
let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(i) as! LSSharedFileListItemRef
if let itemURL = LSSharedFileListItemCopyResolvedURL(currentItemRef, 0, nil) {
if (itemURL.takeRetainedValue() as NSURL).isEqual(appUrl) {
return (currentItemRef, lastItemRef)
}
}
}
return (nil, lastItemRef)
} else {
let addatstart: LSSharedFileListItemRef = kLSSharedFileListItemBeforeFirst.takeRetainedValue()
return(nil,addatstart)
}
}
}
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) {
LSSharedFileListInsertItemURL(loginItemsRef, itemReferences.lastReference, nil, nil, appUrl, nil, nil)
}
} else {
if let itemRef = itemReferences.existingReference {
LSSharedFileListItemRemove(loginItemsRef,itemRef);
}
}
}
}
但是 LSSharedFileListCreate
、LSSharedFileListInsertItemURL
、LSSharedFileListItemRemove
、kLSSharedFileListItemBeforeFirst
、LSSharedFileListItemCopyResolvedURL
、LSSharedFileListCopySnapshot
、kLSSharedFileListSessionLoginItems
在OS X 10.11。如何在 Mac OS 的最新版本上运行?如何更改或重写此代码?
您现在必须使用服务管理框架。您创建一个辅助应用程序,将其添加到您的应用程序包中,它的工作是编写 运行 代码来启动您的主应用程序。一些资源供您使用:
- Tim Schröder excellent blog post 关于如何做到这一点
- Alex Zielenski open source project 可以帮助解决这个问题
- 我已经 video tutorial 手动创建它
在 Swift 3.0 中它看起来像这样:
在您的主应用程序 AppDelegate 中:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Check if the launcher app is started
var startedAtLogin = false
for app in NSWorkspace.shared().runningApplications {
if app.bundleIdentifier == NCConstants.launcherApplicationIdentifier {
startedAtLogin = true
}
}
// If the app's started, post to the notification center to kill the launcher app
if startedAtLogin {
DistributedNotificationCenter.default().postNotificationName(NCConstants.KILLME, object: Bundle.main.bundleIdentifier, userInfo: nil, options: DistributedNotificationCenter.Options.deliverImmediately)
}
}
在 Launcher 应用程序 AppDelegate 中:
func applicationDidFinishLaunching(_ aNotification: Notification) {
let mainAppIdentifier = "<main-app-bundle-id>"
let running = NSWorkspace.shared().runningApplications
var alreadyRunning = false
// loop through running apps - check if the Main application is running
for app in running {
if app.bundleIdentifier == mainAppIdentifier {
alreadyRunning = true
break
}
}
if !alreadyRunning {
// Register for the notification killme
DistributedNotificationCenter.default().addObserver(self, selector: #selector(self.terminate), name: NCConstants.KILLME, object: mainAppIdentifier)
// Get the path of the current app and navigate through them to find the Main Application
let path = Bundle.main.bundlePath as NSString
var components = path.pathComponents
components.removeLast(3)
components.append("MacOS")
components.append("<your-app-name>")
let newPath = NSString.path(withComponents: components)
// Launch the Main application
NSWorkspace.shared().launchApplication(newPath)
}
else {
// Main application is already running
self.terminate()
}
}
func terminate() {
print("Terminate application")
NSApp.terminate(nil)
}
最终,我在主应用程序中添加了一个带有切换按钮的用户界面。用户可以选择是否在登录时启动应用程序。选择存储在 UserDefaults 中。 在视图控制器中:
@IBAction func toggleLaunchAtLogin(_ sender: Any) {
if toggleOpenAppLogin.selectedSegment == 0 {
if !SMLoginItemSetEnabled(NCConstants.launcherApplicationIdentifier as CFString, true) {
print("The login item was not successfull")
toggleOpenAppLogin.setSelected(true, forSegment: 1)
}
else {
UserDefaults.standard.set("true", forKey: "appLoginStart")
}
}
else {
if !SMLoginItemSetEnabled(NCConstants.launcherApplicationIdentifier as CFString, false) {
print("The login item was not successfull")
toggleOpenAppLogin.setSelected(true, forSegment: 0)
}
else {
UserDefaults.standard.set("false", forKey: "appLoginStart")
}
}
}
希望对大家有所帮助。