2

To learn about macOS services (daemons and agents), I have a sample GUI app which counts every 10 sec in the background and displays some UI.

In AppDelegate.swift

class AppDelegate: NSObject, NSApplicationDelegate {

    static let TAG: String = "[BackgroundProcess]: "
    static let sNotificationCenter: UNUserNotificationCenter = UNUserNotificationCenter.current()
    
    // Uses ViewController to define and display UI
    var mainWindow: NSWindow?

    func applicationWillFinishLaunching(_ notification: Notification) {
        
        NSLog(AppDelegate.TAG + "applicationWillFinishLaunching(_:)")
        
        // Request user permission to display notification
        AppDelegate.sNotificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
            if(granted) {
                NSLog(AppDelegate.TAG + "Permission granted!")
            } else {
                NSLog(AppDelegate.TAG + "Permission denied!")
            }
        }
        
        let queue: DispatchQueue = DispatchQueue.global(qos: .background)
        queue.async {
            self.countEvery10Sec()
        }
    }
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {

        NSLog(AppDelegate.TAG + "applicationDidFinishLaunching(_:)")
        
        // Sets the window variable and displays UI
        CreateMainWindow()
    }

    // Other delegate methods

    func countEvery10Sec () {
        
        var numOfSeconds: Int = 0
        var date: Date
        
        while(true) {
            
            // Sleep for 10 sec
            Thread.sleep(forTimeInterval: 10)
            
            numOfSeconds += 10
            date = Date.now
            NSLog(AppDelegate.TAG + "Time = " + date.description)
            
            // When 1 min is up, display a notification
            if (numOfSeconds % 60 == 0) {
                
                NSLog(AppDelegate.TAG + "That's 60 sec. Displaying notification...")
                DisplayNotification(date: date)
                
                // Reset the count to start again
                numOfSeconds = 0;
            }
        }
    }
}

App is installed as an agent and started using launchctl. I got the following logs,

[BackgroundProcess]: applicationWillFinishLaunching(_:)
[BackgroundProcess]: applicationDidFinishLaunching(_:)
[BackgroundProcess]: CreateMainWindow()
[BackgroundProcess]: Permission granted!
[BackgroundProcess]: Time = 2023-02-09 06:28:12 +0000
[BackgroundProcess]: Time = 2023-02-09 06:28:22 +0000
[BackgroundProcess]: Time = 2023-02-09 06:28:32 +0000
[BackgroundProcess]: Time = 2023-02-09 06:28:42 +0000
[BackgroundProcess]: Time = 2023-02-09 06:28:52 +0000
[BackgroundProcess]: Time = 2023-02-09 06:29:02 +0000
[BackgroundProcess]: That's 60 sec. Displaying notification...
[BackgroundProcess]: Time = 2023-02-09 06:29:12 +0000

Permission granted as app was registered as an agent, which can interact with user. The GUI was visible and there was a notification every minute in the notification centre. This is as expected.

But when app is registered as a daemon and launched,

[BackgroundProcess]: applicationWillFinishLaunching(_:)
[BackgroundProcess]: Permission denied!
[BackgroundProcess]: applicationDidFinishLaunching(_:)
[BackgroundProcess]: CreateMainWindow()
[BackgroundProcess]: Time = 2023-02-09 06:13:39 +0000
[BackgroundProcess]: Time = 2023-02-09 06:13:49 +0000
[BackgroundProcess]: Time = 2023-02-09 06:13:59 +0000
[BackgroundProcess]: Time = 2023-02-09 06:14:09 +0000
[BackgroundProcess]: Time = 2023-02-09 06:14:19 +0000
[BackgroundProcess]: Time = 2023-02-09 06:14:29 +0000
[BackgroundProcess]: That's 60 sec. Displaying notification...

Permission denied, as a daemon should not interact with the user. And according to documentation,

A daemon cannot display any GUI; more specifically, it is not allowed to connect to the window server

That means, even if UI code gets executed, app wouldn't be able display UI. But in my example, the daemon showed GUI (same as that of an agent). But it wasn't able to display notification every minute.

Since daemons cannot connect to window server, they cannot display UI correct? But it was able to show the GUI. Does that mean app has to take care not to display any UI when registered as a daemon and the documentation is wrong?

NightFuryLxD
  • 847
  • 5
  • 15
  • The last update of the archived TN was in 2007 for Mac OS X 10.5 Leopard. "This document is no longer being updated." – Willeke Feb 09 '23 at 14:22
  • @Willeke I followed [this](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html#//apple_ref/doc/uid/10000172i-SW4-BBCBHBFB) to define plist file and launch daemon. The same point about daemon not allowed to connect to window server is mentioned here. It could be out-of-date as well, since its under archives. Can you please point me to the right documentation? – NightFuryLxD Feb 10 '23 at 05:07
  • @Willeke In [launchd](https://www.launchd.info) page, `Only agents have access to the macOS GUI`. – NightFuryLxD Feb 10 '23 at 05:11

0 Answers0