We've got a non-sandboxed app with a built-in daemon that does some root-privileged things for us on occasion. We're using the newest SMAppService APIs, using NSXPCConnections for communications, and generally things work as expected. The daemon is set up to terminate when the parent app terminates.
Our app also has (and uses the daemon for) a self-update feature. Once the new app is downloaded, the daemon takes over, replaces the app in-place, terminates the old app and launches the new one.
However, after this update, the daemon no longer works.
Any other build & launch of the app will silently fail when trying to talk to the daemon. The XPC connection can be constructed as usual, no errors, the process goes along like it should app-side, but the daemon never actually launches and never responds.
I can imagine there could be a few rules being broken here with the self-update and the built-in daemon, but what would they be and how can we work within the rules?
Yea, seems to work from the daemon:
“Seems” being the operative term here.
AppKit is the canonical daemon unsafe framework, as defined in TN2083 [1]. If you use it from a daemon the results you get are unspecified. If you depend implementation details like this, you undermine the long-term stability of your product.
In this specific case, for example, it’s not clear which GUI login session the app will launch in, or what happens if there are no GUI login sessions.
The best way to relaunch your app after the update is to have the app relaunch itself. I’d do something like this:
-
Have the app download the update.
-
Have it message the daemon to prepare the update.
-
At this point the daemon gets everything ready. It’s also a good point to check the update’s provenance.
-
Throughout this process the app is running normally. Once the daemon is done with the previous step, have the app spawn a small executable and then terminate. The key thing about this small executable is that it has no dependency on the app’s bundle, so the next step can’t cause it grief.
-
That executable then tells the daemon to commit the update. This replace’s the app’s bundle on disk.
-
Once that’s done, the executable uses
NSWorkspace
to relaunch the app. -
And terminates.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] TN2083 is old, and some of the details have changed over the years, but the core message is still valid.