AXIsProcessTrusted returns true, but AXUIElementCopyAttributeValue fails with .cannotComplete

This was working a few days ago, but it has since stopped and I can't figure out why. I've tried resetting TCC, double-checking my entitlements, restarting, deleting and rebuilding, and nothing works.

My app is a sandboxed macOS SwiftUI LSUIElement app that, when invoked, checks to see if the frontmost process is Terminal, then tries to get the frontmost window’s title.

	func
	getFrontmostWindowTitle()
		throws
		-> String?
	{
		let trusted = AXIsProcessTrusted()
		print("getFrontmostWindowTitle AX trusted: \(trusted)")
		
		guard let app = NSWorkspace.shared.frontmostApplication else { return nil }
		let appElement = AXUIElementCreateApplication(app.processIdentifier)

		var focusedWindow: AnyObject?
		let status = AXUIElementCopyAttributeValue(appElement, kAXFocusedWindowAttribute as CFString, &focusedWindow)
		guard
			status == .success,
			let window = focusedWindow
		else
		{
			if status == .cannotComplete
			{
				throw Errors.needAccessibilityPermission
			}
			return nil
		}

		var title: AnyObject?
		let titleStatus = AXUIElementCopyAttributeValue(window as! AXUIElement, kAXTitleAttribute as CFString, &title)
		guard titleStatus == .success else { return nil }

		return title as? String
	}

I recently renamed the app, but the Bundle ID has not yet changed. I have com.apple.security.accessibility set to YES in the Entitlements file (although i had to add it manually), and a NSAccessibilityUsageDescription string set in Info.plist.

The first time I ran this, macOS nicely prompted for permission. Now it won't do that, even when I use AXIsProcessTrustedWithOptions() to try to force it.

If I use tccutil to reset accessibility and apple events, it still doesn't prompt. If I drag my app from the build products folder to System Settings, it gets added to the system TCC DB (not the user DB). It shows an auth value of 2 for my app:

% sudo sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" "SELECT client,auth_value FROM access WHERE service='kTCCServiceAccessibility' OR service='kTCCServiceAppleEvents';"
com.latencyzero.<redacted>|2
<redactd>

I'm at a loss as to what went wrong. I proved out the concept earlier and it worked, and have since spent a lot of time enhancing and polishing the app, and now things aren't working and I'm starting to worry.

Answered by DTS Engineer in 850466022
My app is a sandboxed macOS SwiftUI LSUIElement app

We do not support the Accessibility APIs in sandboxed apps. See Review functionality that is incompatible with App Sandbox. I’m not sure how this ever worked, but the behaviour you’re currently seeing is correct.

You can probably achieve this goal using the <CoreGraphics/CGWindow.h> API. Be aware, however, that certain properties, most notably kCGWindowName, are now only populated if your app has the Screen Recording privilege, and that presents a world of different TCC issues.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

My app is a sandboxed macOS SwiftUI LSUIElement app

We do not support the Accessibility APIs in sandboxed apps. See Review functionality that is incompatible with App Sandbox. I’m not sure how this ever worked, but the behaviour you’re currently seeing is correct.

You can probably achieve this goal using the <CoreGraphics/CGWindow.h> API. Be aware, however, that certain properties, most notably kCGWindowName, are now only populated if your app has the Screen Recording privilege, and that presents a world of different TCC issues.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It’s better to reply as a reply, rather than in the comments; see Quinn’s Top Ten DevForums Tips for this and other titbits.

I had done this with Apple events before. I think that I can do in the Sandbox, right?

Unlikely. Well, you could probably do it with enough temporary exception entitlements, but use of those runs counter to your goal of deploying to the App Store. I talk about this more in various posts linked to from App Sandbox Resources.

App Sandbox has a general policy of preventing app A from messing around with the state of app B [1], and that’s exactly what you’re trying to do here. The CGWindow option I outlined above might let you achieve your goal, but that’s only because it’s behind significant TCC limits. The only other path forward I can see is to build a general-purpose script attachment feature and put the user in charge of this workflow. Again, I have a post about that in App Sandbox Resources.

IMPORTANT Script attachment is not meant to be a generic sandbox bypass mechanism, and if you approach it as such then I wouldn’t be surprised if you encounter App Review problems. Rather, think of it as a general way for the user to integrate interesting workflows into your app.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Unless both apps are from the same team.

In my case, all I need is the window title for the frontmost Terminal.app window (and ideally, any other terminal emulator a user might use, like iTerm2). I don't need to control anything or record the screen.

Okay, it seems ScreenCaptureKit will let me enumerate windows and get their titles, but I have to rely on the hope that the first one listed is the topmost window (seems to be, but not sure that's guaranteed). I also need screen recording permission, unfortunately, as it gives my app much more than it needs.

My alternative is AppleEvents with the temporary exemption (which I think still applies, as there’s no alternative).

AXIsProcessTrusted returns true, but AXUIElementCopyAttributeValue fails with .cannotComplete
 
 
Q