AXWrapper is a lightweight Swift package that wraps the native macOS Accessibility APIs.
It is intended to be used as a small dependency in other macOS projects that need:
AXUIElementaccess- typed AX attribute reads and writes
- AX actions
- AXObserver wrappers
- accessibility permission helpers
- macOS 14+
- Swift 6.2+
dependencies: [
.package(path: "/absolute/path/to/ax-monitor")
]dependencies: [
.package(url: "https://your-git-url.git", branch: "main")
]Then add the product to your target:
.target(
name: "YourApp",
dependencies: [
.product(name: "AXWrapper", package: "AXWrapper")
]
)Import it with:
import AXWrapperimport AXWrapper
@MainActor
func ensurePermissions() -> Bool {
AXPermissionHelpers.hasAccessibilityPermissions()
}To prompt for accessibility access:
import AXWrapper
@MainActor
func requestPermissions() -> Bool {
AXPermissionHelpers.askForAccessibilityIfNeeded()
}import AXWrapper
@MainActor
func printFocusedElement() throws {
let appElement = try AXUIElement.focusedApplication()
let app = Element(appElement)
print("App role:", app.role() ?? "unknown")
print("Focused element:", app.focusedUIElement()?.briefDescription() ?? "none")
}import AXWrapper
@MainActor
func inspect(_ element: Element) {
print("Role:", element.role() ?? "nil")
print("Title:", element.title() ?? "nil")
print("Identifier:", element.identifier() ?? "nil")
print("Frame:", String(describing: element.frame()))
}import AXWrapper
@MainActor
func dumpChildren(_ element: Element) {
let children = element.children() ?? []
for child in children {
print(child.briefDescription())
}
}import AXWrapper
@MainActor
func press(_ element: Element) throws {
try element.performAction(.press)
}import AXWrapper
@MainActor
func setText(_ element: Element, value: String) {
_ = element.setValue(value, forAttribute: AXAttributeNames.kAXValueAttribute)
}import AXWrapper
@MainActor
func watch(_ element: Element) throws -> NotificationWatcher {
let watcher = NotificationWatcher(forElement: element, notification: .valueChanged) {
pid,
notification,
rawElement,
userInfo in
let wrapped = Element(rawElement)
print("PID:", pid)
print("Notification:", notification.rawValue)
print("Element:", wrapped.briefDescription())
print("User info:", userInfo ?? [:])
}
try watcher.start()
return watcher
}import AXWrapper
@MainActor
func subscribeToApp(pid: pid_t) {
let result = AXObserverCenter.shared.subscribe(
pid: pid,
notification: .focusedUIElementChanged
) { pid, notification, rawElement, userInfo in
print(pid, notification.rawValue, Element(rawElement).briefDescription(), userInfo ?? [:])
}
print(result)
}Element: Swift wrapper aroundAXUIElementAttribute<T>: typed AX attribute keyAXPermissionHelpers: accessibility trust helpersAXObserverCenter: subscription-based AXObserver wrapperAXObserverManager: lower-level observer managerNotificationWatcher: lifecycle wrapper for a single observation
swift build