I have a need to boot my Mac application when the user logins in.
This is what I did:
I created a new Coca Application Target.
• Build Settings set "Skip Install" to "YES"
• Info.plist "Application is background only" to "YES"
• Enabled Sandbox on both main and helper app.
• Added Copy File Phase to main application: Wrapper, Contents/Library/LoginItems, added Helper.app
AppDelegate.swift of Helper application
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("hi")
let mainAppIdentifier = "co.myprogress.osx"
let runningApps = NSWorkspace.shared.runningApplications
let isRunning = !runningApps.filter { $0.bundleIdentifier == mainAppIdentifier }.isEmpty
if !isRunning {
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()
components.removeLast()
components.removeLast()
components.append("MacOS")
components.append("TODOs Menubar") //main app name
let newPath = NSString.path(withComponents: components)
NSWorkspace.shared.launchApplication(newPath)
}
else {
self.terminate()
}
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
AppDelegate.swift of Main Application
func applicationDidFinishLaunching(_ aNotification: Notification) {
let launcherAppId = "co.myprogress.TodosMenubarHelper"
let runningApps = NSWorkspace.shared().runningApplications
let isRunning = !runningApps.filter { $0.bundleIdentifier == launcherAppId }.isEmpty
let ret = SMLoginItemSetEnabled(launcherAppId as CFString, true)
print(ret)
if isRunning {
DistributedNotificationCenter.default().post(name: .killLauncher,
object: Bundle.main.bundleIdentifier!)
}
}
Testing
• Cmd + B to build application
• Right click .app in Product > Show in Finder
• Launched application
• Log out
• Log back in -- Expected: Application boots. What Happened: Application Did not boot
Not a full solution, but perhaps it helps on debugging:
I followed this tutorial which describes steps very similar to yours. I also archived and exported the application as a Developer ID application and installed it into /Applications.
Result: it didn't work for me either.
After opening Console.app the system.log showed many lines like the following, appearing every 10 seconds:
May 24 21:18:16 FooBar com.apple.xpc.launchd[1] (my.domain.TestHelper[43372]): Could not resolve CFBundleIdentifier specified by service: -10814: my.domain.TestHelper
I.e. registering the helper via SMLoginItemSetEnabled works, but the system cannot find it. After opening the main app manually and disabling the launch-on-login feature, no more messages showed up. Deregistering also seems to work.
At last I tried to open the main app manually in Terminal.app:
$ open -b my.domain.Test
It opened many old builds from various Xcode build locations and archives! Hence rather try the feature with a clean system. Otherwise the launch services my open unexpected versions of the main or helper apps. Testing the feature from Xcode seems to be impossible.
Apple also documents some conditions that must be met for login items to work.
With a clean system I could get it to work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With