Why would UI scripting vary on two Macs with the same version of macOS?

I’ve created a Keyboard Maestro macro that switches the macOS Sound Output (Set Sound Output - Macro Library - Keyboard Maestro Discourse). This macro uses AppleScript (UI Scripting) to control the System Settings (or System Preferences).

As I’m sure many of you know, UI scripting is a pain, not very robust, and often requires localization adjustments; but unfortunately I’ve not found a better way to automate the Sound Output when AirPlay devices are in the mix.

As Apple has upgraded macOS, I’ve normally had to modify the AppleScript to accommodate the new version. The AppleScript was essentially rewritten when Apple switched from System Preferences to System Settings. :face_vomiting:

With that said, I’ve managed to keep the macro up to date because I’ve normally had access to beta versions of macOS. With Sonoma another change was required and after testing on my Macs I thought the macro was ready to share. Oddly, however, a few users reported problems and after some investigation I’ve discovered that with the identical version of macOS (Sonoma 14.2.1, Build 23C71), the UI element hierarchy is different.

The script below is the shell of the Sonoma AppleScript that is relevant to the issue:

tell application "System Settings"
	activate
	reveal pane id "com.apple.Sound-Settings.extension"
end tell

tell application "System Events"
	tell its application process "System Settings"
		repeat until exists its window "Sound"
		end repeat

		repeat until exists its group 2 of scroll area 1 of group 1 of list 2 of splitter group 1 of list 1 of window "Sound"
			-- (some users) repeat until exists its group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
		end repeat

		tell its group 2 of scroll area 1 of group 1 of list 2 of splitter group 1 of list 1 of window "Sound"
			-- (some users) tell its group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"

			repeat until exists its scroll area 1
			end repeat

			tell its scroll area 1
				get entire contents
			end tell

		end tell
	end tell
end tell

In the above, the (some users) comment, notes the difference.

So for me, the hierarchy is:

list 2 of splitter group 1 of list 1 of window "Sound"

But for other Sonoma 14.2.1 users, the hierarchy is:

group 2 of splitter group 1 of group 1 of window "Sound"

Does anyone know why the hierarchy might differ, i.e., in my case the UI includes list elements, but in others they are groups?

Thank you!

I’m not sure what’s causing the difference. I’ve noticed the same issue, also with System Settings, on my own Macs. Haven’t been able to narrow it down yet.

1 Like

It’s still a mystery, so for now, here’s the approach I’ve taken…

-- Within the Keyboard Maestro macro, the System Settings Sound pane will be opened using native actions 
tell application "System Settings"
	activate
	reveal pane id "com.apple.Sound-Settings.extension"
end tell


-- Within the Keyboard Maestro macro, kmSoundInputName will be supplied via a local variable
set kmSoundInputName to "MacBook Pro Speakers"

-------------------------------------------------------

set rbOutput to 1

tell application "System Events"
	tell its application process "System Settings"
		
		repeat until exists its window "Sound"
		end repeat
		
		-- For some unknown reason, with Sonoma 14.2.1 (23C71), the gui hierarchy can differ on some some macs; the code below accommodates both. For more information, see:
		-- a. https://forum.latenightsw.com/t/why-would-ui-scripting-vary-on-two-macs-with-the-same-version-of-macos/4613
		-- b. https://forum.keyboardmaestro.com/t/re-macro-set-sound-output-v7-0/34859
		
		repeat until ¬
			(exists its group 2 of scroll area 1 of group 1 of list 2 of splitter group 1 of list 1 of window "Sound") ¬
				or ¬
			(exists its group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound")
		end repeat
		
		if (exists its list 1 of window "Sound") then
			
			tell its group 2 of scroll area 1 of group 1 of list 2 of splitter group 1 of list 1 of window "Sound"
				
				repeat until exists its tab group 1
				end repeat
				
				tell its tab group 1
					repeat until exists its radio button rbOutput
					end repeat
					delay 0.1
					click its radio button rbOutput
				end tell
				
				repeat until exists its scroll area 1
				end repeat
				
				tell its scroll area 1
					set theRows to (its every row of outline 1)
					repeat with aRow in theRows
						if (name of first item of static text of group 1 of UI element 1 of aRow) starts with kmSoundInputName then
							set selected of aRow to true
							return
						end if
					end repeat
				end tell
				
			end tell
			
		else
			
			tell its group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
				
				repeat until exists its tab group 1
				end repeat
				
				tell its tab group 1
					repeat until exists its radio button rbOutput
					end repeat
					delay 0.1
					click its radio button rbOutput
				end tell
				
				repeat until exists its scroll area 1
				end repeat
				
				tell its scroll area 1
					set theRows to (its every row of outline 1)
					repeat with aRow in theRows
						if (name of first item of static text of group 1 of UI element 1 of aRow) starts with kmSoundInputName then
							set selected of aRow to true
							return
						end if
					end repeat
				end tell
				
			end tell
			
		end if
		
	end tell
end tell
1 Like