installAutographNativeTapCapture

fun installAutographNativeTapCapture(tracker: Tracker, scopeStack: ScopeStack, eventName: String = DEFAULT_AUTOCAPTURE_EVENT_NAME): AutographNativeTapCapture

Starts reporting taps on native UIKit content as eventName, resolved by resolveNativeTapTargetByHitTest — one resolver, which reads no accessibility state at any point.

Opt-in, exactly as Compose autocapture is: observing every tap is a different privacy posture than instrumenting individual elements, so nothing happens until an app calls this.

UIKit is covered, in a cold process and a warm one alike. SwiftUI is not covered at all (#189, #191). A UIControl, or a view carrying an enabled single-tap recognizer, resolves through UIView.hitTest — the path UIKit actually delivers the touch along — provided it carries an accessibilityIdentifier, which is the only thing ever reported as a target. An untagged control resolves to nothing. SwiftUI has no per-element backing view for hitTest to find (measured: a whole SwiftUI screen is a handful of UIViews, and .accessibilityIdentifier appears on none of them), and no public API offers another route, so taps on a SwiftUI surface must be instrumented explicitly with a track() call. Compose content has its own pipeline and is unaffected by any of this.

Why SwiftUI is not captured warm either, though it once was. UIKit and SwiftUI build the accessibility element tree only when an accessibility client has run in the process — VoiceOver, Voice Control, the Accessibility Inspector, an XCUITest runner (#135, measured on a freshly created simulator and again on a rebooted physical device). Until #191 a second resolver walked that tree behind this one, which did name SwiftUI elements — but only in a process where such a client was running. That is not a partial capture, it is a biased one: the taps it recorded were exactly those made by assistive-technology users and by test automation, and its silence elsewhere is indistinguishable downstream from nobody having tapped. Data that is quietly conditioned on a user's assistive technology is worse to ship than no data, so the fallback was removed rather than kept for the population it happened to serve. (This also removed the misattribution that same walk produced: a UIView-backed SwiftUI container carrying an identifier could claim a tap on its own contents.)

A tap that resolves to nothing is at least not silently lost: the first time it happens, an NSLog line explains why — see warnOnceIfANativeTapResolvedToNothing — so a developer running the app during integration has something in the console instead of nothing.

Pass the same scopeStack the app gives AutographProvider if it also renders Compose. Sharing one stack is what lets a native tap carry the screen and scope a Compose screen pushed, and vice versa; two stacks leave both sides attributing against half-empty context.

Taps on Compose content are not reported here. autograph-compose registers its host views and this pipeline drops any tap whose hit path crosses one, so a hybrid app reports each tap exactly once, from the pipeline that owns the content. See AutographComposeHosts.

A native tap carries no screen of its own yet. The Compose path falls back to its ScreenHistory when no ambient frame supplies one; there is no native equivalent until #65 adds screen-transition capture, so a native tap's screen comes from the shared scopeStack or not at all.

Threading. Main thread only, to install and to uninstall — it touches UIKit throughout.

Keep the returned handle: it is the only way to AutographNativeTapCapture.uninstall, and the capture holds tracker and scopeStack strongly until then.