The right way to launch a macOS app at login?

The right way to launch a macOS app at login?


On this tutorial I am going to present you the way to launch a totally sandboxed macOS utility on system startup written in Swift.

WARN: Replace: you must merely add the LaunchAtLogin library to your venture. It’ll handle every thing and it has another cool utility options.

Venture setup

Let’s begin this tutorial by creating a brand new Xcode venture with a macOS app template. Title it for instance MainApplication, use storyboards and naturally choose Swift because the default language, we don’t want checks for this venture in any respect.

Now that we’ve the principle utility goal, there’s this good little perform out there known as SMLoginItemSetEnabled. With that perform you’ll be able to register an utility bundle identifier to auto begin when the person logs in, however you can’t register your individual app identifier. Sounds loopy, huh? 😜

You may register a bundle identifier embedded into your foremost utility to get auto-launched by the system. To do that you’ll have to create a brand new launcher utility which might be launched later by your foremost utility.

You additionally must code signal your utility together with your Developer ID, in any other case it received’t begin after you log in to macOS. Sandboxing is an important a part of the method, so just remember to comply with each instruction fastidiously.

Targets & configurations

Create a brand new goal inside your present venture. Title this new goal for instance LauncherApplication. Allow sandbox and code signing for each targets (foremost and launcher apps) beneath the Signing & Capabilities tab. For the LauncherApplication goal within the construct settings set skip set up to sure.

Skip install

For the launcher app add a brand new entry to the Information.plist file: Software is background solely with the worth: sure. It will set your utility as a background app, we don’t really want person interface for a launcher software, proper?

Background only

Add a brand new copy file construct section to your foremost utility goal to repeat your launcher utility into the bundle. The vacation spot ought to be wrapper and the subpath ought to be Contents/Library/LoginItems.

Copy files

Hyperlink the ServiceManagement.framework to your foremost utility and double test that the launcher app is embedded into your foremost utility.

Frameworks

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 don’t want these silly issues to put round.

Creating the launcher programmatically

Someplace in your foremost utility you need to register your launcher utility’s identifier. When your foremost utility begins you need to kill the launcher utility if it’s 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.Title {
    static let killLauncher = Notification.Title("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().submit(
                identify: .killLauncher, 
                object: Bundle.foremost.bundleIdentifier!
            )
        }
    }
}

Within the launcher utility you need to begin your foremost utility if it’s not working already. That’s 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.Title {
    static let killLauncher = Notification.Title("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), 
                identify: .killLauncher, 
                object: mainAppIdentifier
            )

            let path = Bundle.foremost.bundlePath as NSString
            var parts = path.pathComponents
            parts.removeLast()
            parts.removeLast()
            parts.removeLast()
            parts.append("MacOS")
            parts.append("MainApplication") //foremost app identify

            let newPath = NSString.path(withComponents: parts)

            NSWorkspace.shared.launchApplication(newPath)
        }
        else {
            self.terminate()
        }
    }
}

That’s it, we’re able to launch. Export your foremost utility and right here is an important factor: code signal it together with your Developer ID. Begin it, shut it, sign off and again into the system. Hopefully your foremost utility might be working once more.

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.