Skip to content

tvOS XCUITest spike

The XCUITest runner approach is viable on the tvOS simulator, as demonstrated with Xcode 26.6, tvOS 26.5, and an Apple TV 4K (3rd generation) simulator. The probe app is a SwiftUI app with a 3×3 button grid and a text field.

Capability boundary

Check Result Evidence and limits
Launch Yes xcodebuild test-without-building with platform=tvOS Simulator runs the service-style test.
Socket Yes A runner NWListener on port 4189 accepts a host loopback connection and exchanges ping/pong. TEST_RUNNER_AM_PORT=4189 on the xcodebuild command arrives as AM_PORT. SIMCTL_CHILD_* was not tested.
Hierarchy with focus Yes snapshot() returns frames, labels, identifiers, element types, hasFocus, isEnabled, and isSelected, including for com.apple.TVSettings.
Remote Yes XCUIRemote.shared.press(_:) handles up, down, left, right, select, menu, home, and play/pause; press(.select, forDuration: 1.5) works. Focus is visible in the next snapshot after the press returns.
Direct focus targeting No XCUIElement does not respond to focus, setHasFocus:, or requestFocus. Directional moves reach the geometric neighbour from known grid cells, but focus memory changes subsequent destinations.
Text Yes Selecting the focused field opens the full-screen keyboard, with Keyboard focused. app.typeText("hello") sets the field value to hello.
Screenshots Yes XCUIScreen.main.screenshot() and simctl io screenshot both work.
CtrlProxy typecheck Yes, compilation only Sources/CtrlProxy typechecks with the sdk-wire module and ObjC catcher module map for arm64-apple-tvos17.0-simulator. os(iOS)-gated and UIKit-importing paths are compiled out and provide no tvOS behaviour. CtrlProxy itself was neither built with xcodebuild nor run.

Focus and runner constraints

Focus targeting requires a path of directional remote moves and a fresh focus read after every move. The focus engine remembers the last item per column: moving up from the text field after leaving B22 returns to B22, rather than the geometrically expected B21. A planner cannot infer focus solely from grid geometry or a previous planned path.

Settings supports system-app hierarchy capture. Activating com.apple.TVWatchList and com.apple.TVHomeScreen fails, so those bundle identifiers are not usable capture targets in this environment. Test invocations can remain running after a passing test under load; use a hard timeout and obtain results from test logs or result files.

The capture at test/fixtures/tvos-xcuitest/focus-hierarchy.json preserves the probe hierarchy with the text field focused at launch. Element types are XCUIElement.ElementType raw values.

Decision

GO for the runner-dependent implementation: hierarchy, focus observation, remote input, text, sockets, and screenshots are demonstrated. No simulator device-side questions remain for this spike. Physical Apple TV is out of scope; tvOS 27 and Duo-style runtimes are untried.