According to Technical Note TN2083 the Window Server advertises itself in the global bootstrap namespace, which is why you can launch GUI applications from SSH sessions, even if sshd/sshd-keygen-wrapper
is launched as a launch daemon (in a non-GUI per-session bootstrap namespace).
As I understand it this is also why SessionGetInfo()
reports NO
for sessionHasGraphicAccess
, as the SSH session is not an Aqua session type, while CGSessionCopyCurrentDictionary()
does return a valid dict, because in practice you have access to the window server.
However, the tech note advices against running GUI programs from SSH sessions, as other GUI services may not be exposed to the global or non-GUI per-session bootstrap namespace. It uses com.apple.dock.server
as an example of such a service, showing how Activity Monitor has different behavior when launched via SSH than via the UI.
Based on the advice of the tech note, articles like https://aahlenst.dev/blog/accessing-the-macos-gui-in-automation-contexts/ recommends running CI UI tests via a Launch Agent instead of SSH.
Now, I've tried to reproduce the the Activity Monitor case on macOS 12 and macOS 15, and I can not reproduce the missing Dock features. The Testing with Xcode documentation also says that:
By default, when you use ssh to login to an macOS system that has no active user session running, a command-line session is created. To ensure that an Aqua session is created for an ssh login, you must have a user logged in on the remote macOS host system. The existence of a user running on the remote system forces Aqua session for the ssh login. Once there is a user running on the host system, running xcodebuild from an ssh login works for all types of tests.
Which begs the question: Does modern macOS versions expose GUI services to the global or non-GUI per-session bootstrap namespace, or otherwise enable UI testing from SSH sessions, so that UI tests can safely be run from SSH sessions (as long as the user is logged in to the remote system's UI). Has things changed in this regard?
The use-case here is UI testing, but not necessarily though xcodebuild, so any magic that xcodebuild does internally to handle being launched from a SSH session would not help in the general case.
OK, that makes this more of a product than a tool, so my advice is gonna be more risk averse.
The original Technical Note TN2083 lists some cases of why running GUI code from an SSH session is a bad idea (such as not being able to interact with the Dock)
Right. When I wrote that I included the Dock example because it was a quick way to demonstrate the issue. You shouldn’t put any stock in the fact that this example no longer works. If you’re building a product I recommend that you follow the rules outlined in that technote rather than rely on these implementation details. Specifically, have your code start in the right context rather than try to switch contexts.
I recently had almost the same discussion with a different developer. Have a read through Session, Desktops and login screen. I’m especially proud of “the Five Stages of Mac Screen Sharing Developer Grief™” (-:
I know of open, but are there others?
launchd
‘2.0’ added a bunch of commands to launchctl
and those commands can target specific domains. If you really want to make this work, use bootstrap
to load your agent into the gui/<UID>
domain and then start the executable from that agent. That way your executable will start in the right context.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"