I know this has asked before, but I have not yet found an answer that has actually helped me.
What I'm trying to do is write a launchd daemon in Swift, that will catch the shutdown notification and run a script, or do something else.
This is the latest iteration I've tried, but it doesn't work. The logging messages are of course to verify if it works. The current behavior is, the daemon launches succesfully and sets the observer (or at least notifies me that it does, although I'm not sure if I can check whether it was actually successful in doing so)
But it never catches the powerdown notification as it should.
Here's the Swift code:
EDIT: I have updated this code with a newer iteration, which now does not give any selector errors (and catches activate/hide notifications properly), but it still fails to catch the shutdown notification.
import AppKit
import os
public class ShutdownHandlerDelegate : NSObject, NSApplicationDelegate {
static let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "LoggingTest")
var shutdownObserver: NSObjectProtocol?
@objc func handleShutdown() {
ShutdownHandlerDelegate.logger.notice("System is about to shut down. This is us logging that for debug...")
NSWorkspace.shared.notificationCenter.removeObserver(shutdownObserver!)
shutdownObserver = nil
// Perform your pre-shutdown tasks here
}
@objc func handleActivate(_ notification: Notification) {
if let app = (notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication)?.bundleIdentifier {
ShutdownHandlerDelegate.logger.notice("\(app, privacy: .public) will now be activated!")
// Perform your pre-shutdown tasks here
}
}
@objc func handleHide(_ notification: Notification) {
if let app = (notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication)?.bundleIdentifier {
ShutdownHandlerDelegate.logger.notice("\(app, privacy: .public) will now be hidden!")
// Perform your pre-shutdown tasks here
}
}
func setUpObservers() {
self.shutdownObserver = NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.willPowerOffNotification, object: nil, queue: OperationQueue.main, using: {
(_ note) in
self.handleShutdown()
})
// NSWorkspace.shared.notificationCenter.addObserver(
// self,
// selector: #selector(handleShutdown(_:)),
// name: NSWorkspace.willPowerOffNotification,
// object: nil
// )
NSWorkspace.shared.notificationCenter.addObserver(
self,
selector: #selector(handleActivate(_:)),
name: NSWorkspace.didActivateApplicationNotification,
object: nil
)
NSWorkspace.shared.notificationCenter.addObserver(
self,
selector: #selector(handleHide(_:)),
name: NSWorkspace.didHideApplicationNotification,
object: nil
)
}
override init() {
super.init()
}
deinit {
if let observer = shutdownObserver {
NSWorkspace.shared.notificationCenter.removeObserver(observer)
}
}
}
@main
public class ShutdownHandler : NSObject {
let _app : NSApplication?
static let _delegate : ShutdownHandlerDelegate = ShutdownHandlerDelegate()
static public func main() {
// _app.delegate = ShutdownHandlerDelegate
_delegate.setUpObservers()
ShutdownHandlerDelegate.logger.notice("We're registering the shutdown observer. bundle-id is: \(Bundle.main.bundleIdentifier!, privacy: .public)")
NSApplication.shared.run() // This keeps the app running & makes sure events are received
}
override init () {
_app = NSApplication.shared
_app!.delegate = ShutdownHandler._delegate
super.init()
}
}
And here's the launchd file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.lord-kamina.shutdownHandler</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/shutdown-daemon</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
shutdown-daemon[16353:125785] -[NSApplication handleActivate:]: unrecognized selector sent to instance 0x7fa219a04f00handleActivate:a selector you're trying to send from some other part of your codebase?handleActivatemethod now, but what's actually calling it? DO you have some other notification observer configured to sendhandleActivate:to your object?ShutdownHandler._app=NSApplication.shared. Where/when isShutdownHandlerinstantiated? Do you set the app delegate? Did you tryaddObserver(forName:object:queue:using:)?