installAutographNativeScreenCapture
Starts reporting native UIKit screen transitions: on every UIViewController.viewDidAppear: that names a screen, a Screen Viewed event is emitted via tracker and a screen frame is pushed onto scopeStack so autocaptured taps on that screen carry it; the frame is removed on viewDidDisappear:. This is what makes #62's native taps stop carrying no screen.
Opt-in, exactly as tap capture is: nothing happens until an app calls this. Pass the same scopeStack handed to AutographProvider and to installAutographNativeTapCapture if the app is hybrid — one stack is what lets a native screen become the previous_screen of the next Compose screen and enriches taps from either pipeline.
What this captures, precisely — and what it does not
This is UIKit UIViewController appearance capture, nothing more. A screen is reported when a view controller that belongs to the app (not a UIKit/SwiftUI system container) appears. That covers the ordinary cases — a window's root controller, a controller pushed onto a UINavigationController, a presented controller — because each fires viewDidAppear: on a distinct app controller.
It deliberately does not cover:
SwiftUI, including
NavigationStack. Every SwiftUI screen is oneUIHostingControllerfrom a system bundle (so the filter excludes it), andNavigationStackswaps its destinations inside that single host with no per-destinationviewDidAppear:. A SwiftUI screen names itself with the one-line.autographScreen("Name")modifier instead; a pure-SwiftUI app gets no screen from this swizzle. (That modifier is a separate, Swift-side API.)Container controllers —
UINavigationController,UITabBarController,UISplitViewController,UIPageViewControllerand their subclasses. A container is not itself a screen; its content controller is the screen. Their ownviewDidAppear:is skipped so one push does not emit both "the nav controller" and "the screen it now shows". An app that subclasses a container (a common pattern) is caught by the same rule, because the check is by class kind, not by bundle.Embedded child controllers (
addChild) used as panels or decorations, and the multiple simultaneously-visible panes of aUISplitViewController: there is no single "current screen" there, so this reports whichever app controller'sviewDidAppear:fired and does not try to reconcile several visible at once. Treat those as needing the explicit screen API, like SwiftUI.Taps on Compose content. A controller whose view subtree contains a registered AutographComposeHosts view is Compose-owned and skipped, so a hybrid app's Compose host does not also report a native screen for the Compose screen it renders.
The hook is permanent
The swizzle installs once, process-wide, and is never removed — not even by uninstall. Other SDKs (Firebase, Sentry, Datadog) swizzle viewDidAppear: too; restoring our saved implementation when one of them hooked on top of us would break their chain. uninstall instead clears the sink this capture reports into, so reporting stops while the harmless hook stays. A second installAutographNativeScreenCapture replaces the sink.
Screen naming
By default a screen is named by its controller's class, verbatim — module prefix and all, no ViewController-stripping — so this imposes no naming convention of its own. Supply screenName to clean that up, or to return null for a controller that should not be reported at all (an opt-out).
Threading
Main thread only, to install, to uninstall, and throughout the hook — it is called by UIKit on the main thread and it touches ScopeStack and UIKit throughout.
Already-visible screens
Only transitions after install are seen: a controller already on screen when this is called does not retroactively fire viewDidAppear:. Installing at launch, before the first screen appears, captures that first screen; a late install misses the current screen until the next transition.
Keep the returned handle: it is the only way to AutographNativeScreenCapture.uninstall.