C.W.K.
Stream
Lesson 01 of 05 · published

The Accessibility API Is a Granted Power

~11 min · accessibility, ax-tree, trust, permission

Level 0Cold Flint
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The same door that lets a screen reader speak your screen lets Flint read your selection. That door only opens when you unlock it."

What Accessibility Actually Is

macOS has a whole system, the Accessibility API (everyone calls it AX), built so assistive tools — screen readers, switch controls, voice control — can read and operate the interface of other apps. That's a remarkable power: one app inspecting and driving another app's UI. Flint is not a screen reader, but it needs exactly that power for a narrow purpose — to read the text you selected in whatever app is frontmost. So it uses the same API, through the same permission, that assistive technology does.

The Tree of Elements

Every app exposes its interface to AX as a tree of AXUIElement nodes — windows, buttons, text fields, each with attributes you can query: its role, its value, whether it's focused, what text is selected inside it. You start from a system-wide element, walk to the focused element, and ask that element questions. Reading a selection is just querying one attribute of one node in that tree. The elegance is that it's uniform: the same handful of attribute queries work across native apps that implement them properly.

AXUIElementCreateSystemWide()
   -> kAXFocusedUIElementAttribute   (whatever has focus, in any app)
        -> kAXSelectedTextAttribute  (the highlighted text)
        -> kAXSelectedTextRangeAttribute
        -> kAXRoleAttribute / kAXSubroleAttribute  (what kind of field)

One tree, a few attribute queries. That's the whole read.

Permission Is the Whole Point

Because AX can read and control any app, macOS gates it hard: Flint can do none of this until the user explicitly grants it Accessibility permission in System Settings. AXIsProcessTrusted() tells you whether you have it; you check that before you ever try to read. Until the user opens that door, every AX read fails — and that's correct. The permission is not an obstacle to route around; it is the trust the whole feature stands on.

A granted power should be scoped to exactly what it's for. Accessibility lets Flint read and drive any app — far more than it needs. The discipline is to use the minimum of that power: read the focused selection, insert one result, and nothing else. An app that holds a broad permission has a duty to act narrowly inside it, because the user granted the capability trusting it wouldn't be spent everywhere it technically could.
This is the same door assistive tech uses, which is why it exists and why it's trustworthy in principle: the AX API is a first-class, documented part of macOS, not a hack. What makes it safe is not that the power is small — it's large — but that it's gated behind explicit user consent and, in a good app, used narrowly.

Code

Check permission first; never read without it·swift
import ApplicationServices

// Do we have Accessibility permission? Check BEFORE any read.
func hasAccessibility() -> Bool {
    AXIsProcessTrusted()
}

// Ask for it (prompts the user, opens the System Settings pane).
func requestAccessibility() {
    let opts = [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true]
    _ = AXIsProcessTrustedWithOptions(opts as CFDictionary)
}

// The root of every read: the system-wide element.
let systemWide = AXUIElementCreateSystemWide()
// From here you walk to the focused element (next lesson).

External links

Exercise

Accessibility permission lets an app read and drive every other app on the system. List three things Flint could technically do with that power that it must never do, and explain the principle that keeps a broadly-permissioned app narrow. Then describe what should happen the very first time a user strikes a macro before granting permission.
Hint
Technically Flint could read any window's contents, click buttons in other apps, or harvest text it wasn't asked to touch — all of which betray the narrow grant. The keeping-narrow principle is minimum-use: hold the broad capability, act only within the feature the user understood. On the first strike without permission, Flint should not spend anything — it should detect the missing grant, explain it, and open the settings pane, never fail silently.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.