AI-driven XCUITest automation for interactive UI control and screenshot capture
npx claudepluginhub gestrich/xcode-sim-automationAI-driven XCUITest automation for interactive UI control and screenshot capture
Claude Code marketplace entries for the plugin-safe Antigravity Awesome Skills library and its compatible editorial bundles.
Production-ready workflow orchestration with 79 focused plugins, 184 specialized agents, and 150 skills - optimized for granular installation and minimal token usage
Curated collection of 141 specialized Claude Code subagents organized into 10 focused categories
A Swift package that provides file-based interactive control of iOS apps through XCUITest. Designed for AI agents (like Claude) to drive UI interactions via a simple JSON protocol.
Claude (AI) → CLI → JSON file → XCUITest polling loop → iOS app
Add the package to your project via Swift Package Manager:
dependencies: [
.package(url: "https://github.com/gestrich/xcode-sim-automation.git", from: "1.0.0")
]
Then add XCUITestControl to your UI test target's dependencies:
.testTarget(
name: "MyAppUITests",
dependencies: [
.product(name: "XCUITestControl", package: "xcode-sim-automation")
]
)
Create a UI test that launches the control loop:
import XCTest
import XCUITestControl
final class InteractiveControlTests: XCTestCase {
@MainActor
func testInteractiveControl() throws {
let app = XCUIApplication()
app.launch()
InteractiveControlLoop().run(app: app)
}
}
Run the test:
xcodebuild test \
-workspace MyApp.xcworkspace \
-scheme MyAppUITests \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-only-testing:MyAppUITests/InteractiveControlTests/testInteractiveControl
Once the test is running, use the CLI to send commands:
Tools/xcuitest-control screenshot
Tools/xcuitest-control tap --target "Login"
Tools/xcuitest-control type --value "user@example.com" --target "Email"
Tools/xcuitest-control done
The wrapper script auto-builds the Swift CLI binary on first run and whenever source files change. A Python fallback (Tools/xcuitest-control.py) is also available.
InteractiveControlLoop accepts a Configuration struct with these properties:
| Property | Type | Default | Description |
|---|---|---|---|
commandPath | String | /tmp/xcuitest-command.json | Path to the JSON command file |
hierarchyPath | String | /tmp/xcuitest-hierarchy.txt | Path where UI hierarchy is written |
screenshotPath | String | /tmp/xcuitest-screenshot.png | Path where screenshots are written |
pollingInterval | TimeInterval | 0.5 | Seconds between command file polls |
sessionTimeout | TimeInterval | 300 | Seconds before the loop exits if no commands arrive |
elementWaitTimeout | TimeInterval | 10 | Seconds to wait for an element to appear |
tapRetryCount | Int | 3 | Number of tap attempts before failing |
tapRetryDelay | TimeInterval | 0.5 | Seconds between tap retries |
Example with custom configuration:
let config = InteractiveControlLoop.Configuration(
sessionTimeout: 600,
tapRetryCount: 5
)
InteractiveControlLoop(configuration: config).run(app: app)
The CLI wrapper lives at Tools/xcuitest-control. Clone this repo to get it:
git clone https://github.com/gestrich/xcode-sim-automation.git
xcode-sim-automation/Tools/xcuitest-control --help
| Command | Description | Example |
|---|---|---|
tap | Tap an element by identifier | tap --target "Submit" --target-type button |
scroll | Scroll in a direction | scroll --direction down --target "tableView" |
type | Type text into a field | type --value "hello" --target "searchField" |
adjust | Set a slider value (0.0–1.0) | adjust --target "volume" --value 0.75 |
pinch | Pinch to zoom | pinch --scale 2.0 --velocity 1.0 |
wait | Pause for a duration | wait --value 2.0 |
screenshot | Capture screenshot and hierarchy | screenshot |
done | Exit the test loop | done |
status | Check current command status | status |
Override default file paths by setting these before running the CLI:
| Variable | Default |
|---|---|
XCUITEST_COMMAND_PATH | /tmp/xcuitest-command.json |
XCUITEST_HIERARCHY_PATH | /tmp/xcuitest-hierarchy.txt |
XCUITEST_SCREENSHOT_PATH | /tmp/xcuitest-screenshot.png |
Commands are exchanged as JSON through the command file. The polling loop reads commands and writes results back to the same file.