Skip to content

Add tvOS UITest runner support#53

Open
setoelkahfi wants to merge 2 commits into
mobile-next:mainfrom
setoelkahfi:feature/tvos-support
Open

Add tvOS UITest runner support#53
setoelkahfi wants to merge 2 commits into
mobile-next:mainfrom
setoelkahfi:feature/tvos-support

Conversation

@setoelkahfi

@setoelkahfi setoelkahfi commented Jun 30, 2026

Copy link
Copy Markdown

What

The DeviceKit XCUITest runner now builds for tvOS, so the automation stack can drive the tvOS Simulator over the same JSON-RPC interface used for iOS.

Changes

  • New devicekit-tvos scheme and tvOS target in the Xcode project, plus scripts/patch-tvos-runner.sh and Makefile targets that build and patch the tvOS runner.
  • A tvOS device.io.button handler that maps button names to XCUIRemote.Button presses (up, down, left, right, select, menu, home, playPause). tvOS navigates by focus, so there is no touch input.
  • The touch, keyboard, and orientation handlers stay iOS-only behind #if os(iOS) in the JSON-RPC dispatcher.
  • DeviceInfo reads the screen size from UIScreen.main.bounds on tvOS, since tvOS has no SpringBoard to query.
  • The remaining iOS-only handlers and helpers are guarded with #if os(iOS) so the shared sources compile for both platforms.
  • tvOS test runner app icon now matches the iOS one (new brandassets catalog + Runner.app patch for CFBundleIcons/CFBundleDisplayName).

Testing

Built the tvOS runner and ran it against a booted Apple TV 4K simulator, then drove a full profile create and delete flow in a tvOS app. Remote button presses, screenshots, and UI dumps all worked. Verified the Runner.app icon on-device via simulator install.

Related

The DeviceKit XCUITest runner now builds for tvOS, so the automation
stack can drive the tvOS Simulator.

- Add a `devicekit-tvos` scheme and tvOS target to the Xcode project,
  plus a `scripts/patch-tvos-runner.sh` helper and Makefile targets that
  build and patch the tvOS runner.
- On tvOS, register a `device.io.button` handler that maps button names
  to `XCUIRemote.Button` presses (up, down, left, right, select, menu,
  home, playPause). tvOS navigates by focus, not touch.
- Keep the touch, keyboard, and orientation handlers iOS-only in the
  JSON-RPC dispatcher, behind `#if os(iOS)`.
- Read the screen size from `UIScreen.main.bounds` on tvOS, since there
  is no SpringBoard to query.
- Guard the remaining iOS-only handlers and helpers with `#if os(iOS)`
  so the shared sources still compile for both platforms.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 84627289-32cc-4094-a031-3ca1ca44bf2c

📥 Commits

Reviewing files that changed from the base of the PR and between dee5578 and 927bdbd.

⛔ Files ignored due to path filters (9)
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/bkg.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/logo.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/mask_clean.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/bkg@1x.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/bkg@2x.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/logo@1x.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/logo@2x.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/mask_clean@1x.png is excluded by !**/*.png
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/mask_clean@2x.png is excluded by !**/*.png
📒 Files selected for processing (19)
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json
  • devicekit-ios.xcodeproj/project.pbxproj
  • scripts/patch-tvos-runner.sh
👮 Files not reviewed due to content moderation or server errors (19)
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
  • scripts/patch-tvos-runner.sh
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json
  • devicekit-ios.xcodeproj/project.pbxproj
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json
  • DeviceKitTests/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding tvOS UI test runner support.
Description check ✅ Passed The description is directly related to the tvOS runner, handler, and build changes in the patch.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Makefile`:
- Line 137: The tvOS simulator lookup in the Makefile can pick the wrong booted
device because the current selector takes the first booted UDID across all
runtimes. Update the `tvos-sim-install` booted-device selection logic to filter
the `xcrun simctl list devices booted -j` results by tvOS runtime before
choosing the UDID, keeping the change localized to the `BOOTED` assignment used
by that target.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9224b49b-8a90-4ecc-9ece-a11fcbe7ccc0

📥 Commits

Reviewing files that changed from the base of the PR and between 4f7e98c and dee5578.

📒 Files selected for processing (20)
  • DeviceKitTests/JSONRPC/Handlers/DeviceInfo.swift
  • DeviceKitTests/JSONRPC/Handlers/IOButton.swift
  • DeviceKitTests/JSONRPC/Handlers/IOGesture.swift
  • DeviceKitTests/JSONRPC/Handlers/IOKeys.swift
  • DeviceKitTests/JSONRPC/Handlers/IOLongpress.swift
  • DeviceKitTests/JSONRPC/Handlers/IOOrientationGet.swift
  • DeviceKitTests/JSONRPC/Handlers/IOOrientationSet.swift
  • DeviceKitTests/JSONRPC/Handlers/IOSwipe.swift
  • DeviceKitTests/JSONRPC/Handlers/IOTap.swift
  • DeviceKitTests/JSONRPC/Handlers/IOText.swift
  • DeviceKitTests/JSONRPC/JSONRPCDispatcher.swift
  • DeviceKitTests/XCTest/EventRecord.swift
  • DeviceKitTests/XCTest/OrientationGeometry.swift
  • DeviceKitTests/XCTest/PointerEventPath.swift
  • DeviceKitTests/XCTest/RunnerDaemonProxy.swift
  • DeviceKitTests/XCTest/SystemPermissionManager.swift
  • Makefile
  • devicekit-ios.xcodeproj/project.pbxproj
  • devicekit-ios.xcodeproj/xcshareddata/xcschemes/devicekit-tvos.xcscheme
  • scripts/patch-tvos-runner.sh

Comment thread Makefile

# Build and install tvOS runner on the booted tvOS simulator
tvos-sim-install:
@BOOTED=$$(xcrun simctl list devices booted -j | jq -r '[.devices[][] | select(.state=="Booted")] | first | .udid'); \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the structure/keys of `simctl list devices -j` to confirm tvOS runtime key naming.
xcrun simctl list devices -j 2>/dev/null | jq -r '.devices | keys' || echo "simctl unavailable in this environment"

Repository: mobile-next/devicekit-ios

Length of output: 2002


🌐 Web query:

xcrun simctl list devices -j tvOS runtime key format CoreSimulator.SimRuntime.tvOS JSON

💡 Result:

The command xcrun simctl list devices -j outputs a JSON object where the primary key for grouping simulators is the runtime identifier (e.g., com.apple.CoreSimulator.SimRuntime.tvOS-XX-X) [1][2]. When you run xcrun simctl list devices -j, the resulting JSON structure is organized as follows: 1. Root Object: Contains a key named devices [1][2]. 2. Devices Dictionary: The devices object contains keys representing each installed simulator runtime (such as com.apple.CoreSimulator.SimRuntime.tvOS-18-2) [2][3]. 3. Simulator Array: Each runtime key maps to an array of objects, where each object represents an individual simulator device and includes fields such as name, udid, state, and isAvailable [2][4]. To parse the runtime key format—which follows the pattern com.apple.CoreSimulator.SimRuntime.[Platform]-[Version]—you can programmatically split the string or use regex [2][5]. For example, extracting the platform and version from com.apple.CoreSimulator.SimRuntime.tvOS-13-4 involves splitting by the dash character after the prefix [5]. Note that: - You can filter the JSON output efficiently using tools like jq to isolate specific platforms (e.g., jq '.devices | to_entries | map(select(.key | startswith("tvOS"))) | from_entries') [1]. - The isAvailable boolean field is critical for identifying whether a simulator is ready for use, as the list may include unavailable runtimes [2][4]. - In some scenarios, especially when multiple Xcode versions are involved, runtime identifiers might be duplicated or squashed in the JSON output, which can lead to unexpected behavior in automated scripts [6].

Citations:


tvos-sim-install can select the wrong booted simulator. xcrun simctl list devices booted -j still returns booted devices across all runtimes, so first can resolve to an iOS simulator when one is booted too. Filter for tvOS runtimes before choosing the UDID.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` at line 137, The tvOS simulator lookup in the Makefile can pick the
wrong booted device because the current selector takes the first booted UDID
across all runtimes. Update the `tvos-sim-install` booted-device selection logic
to filter the `xcrun simctl list devices booted -j` results by tvOS runtime
before choosing the UDID, keeping the change localized to the `BOOTED`
assignment used by that target.

Add a compiled brandassets catalog (imagestack/imagestacklayer) for the
tvOS UI test runner, wire up ASSETCATALOG_COMPILER_APPICON_NAME and
CFBundleDisplayName on the devicekit-tvosUITests target, and extend
patch-tvos-runner.sh to copy the compiled Assets.car and set
CFBundleIcons/CFBundleDisplayName on the Runner.app so the tvOS test
runner now shows the same Device Kit icon as the iOS one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant