Search code examples
macoscocoaaccessibility

macOS Accessibility API - kAXErrorAttributeUnsupported when querying Books.app


Books.app Top Charts

I would like to recursively query the children element of the Books.app to fetch all the clickable elements.

However, I noticed I can't seem to fetch the value of the AXChildren attribute from the Books window element.

Code snippet you can try out in Playgrounds:

import Cocoa

guard let books = NSWorkspace.shared.runningApplications.first(where: { $0.bundleIdentifier == "com.apple.iBooksX" }) else {
    fatalError()
}

let booksPid = books.processIdentifier
let booksElement = AXUIElementCreateApplication(booksPid)

var bookRole: AnyObject?
let bookRoleError = AXUIElementCopyAttributeValue(booksElement, "AXRole" as CFString, &bookRole)
print("query book role error: \(bookRoleError.rawValue)")
print("book role: \(bookRole)")

var bookFocusedWindow: AnyObject?
let bookFocusedWindowError = AXUIElementCopyAttributeValue(booksElement, "AXFocusedWindow" as CFString, &bookFocusedWindow)

print("book focused window error: \(bookFocusedWindowError.rawValue)")
print("book focused window: \(bookFocusedWindow)")

guard let focusedWindowElement = bookFocusedWindow as! AXUIElement? else {
    fatalError()
}

var windowRole: AnyObject?
let windowRoleError = AXUIElementCopyAttributeValue(focusedWindowElement, "AXRole" as CFString, &windowRole)
print("windowRole error: \(windowRoleError.rawValue)")
print("windowRole: \(windowRole)")

var children: AnyObject?
let childrenError = AXUIElementCopyAttributeValue(focusedWindowElement, "AXChildren" as CFString, &children)
print("children error: \(childrenError.rawValue)")
print("children: \(children)")

Output:

query book role error: 0
book role: Optional(AXApplication)
book focused window error: 0
book focused window: Optional(<AXUIElement 0x7fc6acd28280> {pid=17149})
windowRole error: 0
windowRole: Optional(AXWindow)
children error: -25205
children: nil

The -25205 error code matches the attribute unsupported code.

I checked the list of attributes the window element supports, and AXChildren is one of them.

var windowAttributes: CFArray?
let windowAttributesError = AXUIElementCopyAttributeNames(focusedWindowElement, &windowAttributes)
print("query window attributes error: \(windowAttributesError.rawValue)")
print("window attributes: \(windowAttributes)")
...
AXProxy,
AXDefaultButton,
AXMinimized,
AXChildren,
AXRole,
AXParent,
AXTitleUIElement,
AXCancelButton,
AXModal,
...

This behavior is really strange because Accessibility Inspector shows the children elements of the window element:

Acccessibility Inspector showing children elements


Solution

  • Turns out the solution is to use AXUIElementCopyAttributeValues. AXUIElementCopyAttributeValue has worked for me for every application other than this though.

    var children: CFArray?
    let childrenError = AXUIElementCopyAttributeValues(focusedWindowElement, "AXChildren" as CFString, 0, 999, &children)
    print("children error: \(childrenError.rawValue)")
    print("children: \(children)")