[ad_1]
On this tutorial I am going to present you the best way to launch a totally sandboxed macOS software on system startup written in Swift 5.
Swift
Mission setup
Let’s begin this tutorial by creating a brand new Xcode undertaking with a macOS app template. Identify it for instance MainApplication, use storyboards and naturally choose Swift because the default language, we do not want checks for this undertaking in any respect.
Now that we’ve got the principle software goal, there’s this good little operate accessible known as SMLoginItemSetEnabled. With that operate you’ll be able to register an software bundle identifier to auto begin when the person logs in, however you cannot register your individual app identifier. Sounds loopy, huh? 😜
You may register a bundle identifier embedded into your principal software to get auto-launched by the system. To do that you’ll have to create a brand new launcher software which shall be launched later by your principal software.
You additionally need to code signal your software together with your Developer ID, in any other case it will not begin after you log in to macOS. Sandboxing is an important a part of the method, so just remember to observe each instruction rigorously.
Targets & configurations
Create a brand new goal inside your present undertaking. Identify this new goal for instance LauncherApplication. Allow sandbox and code signing for each targets (principal and launcher apps) underneath the Signing & Capabilities tab. For the LauncherApplication goal within the construct settings set skip set up to sure.
For the launcher app add a brand new entry to the Data.plist file: Utility is background solely with the worth: sure. It will set your software as a background app, we do not really want person interface for a launcher software, proper?
Add a brand new copy file construct section to your principal software goal to repeat your launcher software into the bundle. The vacation spot needs to be wrapper and the subpath needs to be Contents/Library/LoginItems.
Hyperlink the ServiceManagement.framework to your principal software and double test that the launcher app is embedded into your principal software.
From the LauncherApplication’s storyboard file delete your window and your view controller, additionally you’ll be able to take away the ViewController.swift file from this goal. This can be a background app in any case, so we do not want these silly issues to put round.
Creating the launcher programmatically
Someplace in your principal software you need to register your launcher software’s identifier. When your principal software begins you need to kill the launcher software if it is nonetheless working. You are able to do this by sending a notification to that particular app with the NSDistributedNotificationCenter class.
import Cocoa
import ServiceManagement
extension Notification.Identify {
static let killLauncher = Notification.Identify("killLauncher")
}
@NSApplicationMain
class AppDelegate: NSObject {}
extension AppDelegate: NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let launcherAppId = "com.tiborbodecs.LauncherApplication"
let runningApps = NSWorkspace.shared.runningApplications
let isRunning = !runningApps.filter { $0.bundleIdentifier == launcherAppId }.isEmpty
SMLoginItemSetEnabled(launcherAppId as CFString, true)
if isRunning {
DistributedNotificationCenter.default().put up(title: .killLauncher, object: Bundle.principal.bundleIdentifier!)
}
}
}
Within the launcher software you need to begin your principal software if it isn’t working already. That is it. You must also subscribe for the notifications from the principle app to terminate if the launcher isn’t wanted anymore.
import Cocoa
extension Notification.Identify {
static let killLauncher = Notification.Identify("killLauncher")
}
@NSApplicationMain
class AppDelegate: NSObject {
@objc func terminate() {
NSApp.terminate(nil)
}
}
extension AppDelegate: NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let mainAppIdentifier = "com.tiborbodecs.MainApplication"
let runningApps = NSWorkspace.shared.runningApplications
let isRunning = !runningApps.filter { $0.bundleIdentifier == mainAppIdentifier }.isEmpty
if !isRunning {
DistributedNotificationCenter.default().addObserver(self, selector: #selector(self.terminate), title: .killLauncher, object: mainAppIdentifier)
let path = Bundle.principal.bundlePath as NSString
var parts = path.pathComponents
parts.removeLast()
parts.removeLast()
parts.removeLast()
parts.append("MacOS")
parts.append("MainApplication")
let newPath = NSString.path(withComponents: parts)
NSWorkspace.shared.launchApplication(newPath)
}
else {
self.terminate()
}
}
}
That is it, we’re able to launch. Export your principal software and right here is crucial factor: code signal it together with your Developer ID. Begin it, shut it, log off and again into the system. Hopefully your principal software shall be working once more.
[ad_2]
