iOS Test Skill
Overview
Use this skill to build an iOS/SwiftUI application, launch it in the Simulator, and visually test it using computer use. The skill navigates through every screen, screenshots each state, checks crash logs, and produces a structured test report — without writing a single line of test code.
Options
--flow=<name>: Test a specific user flow (e.g., onboarding, login, checkout)--screen=<ViewName>: Test only a specific screen--device=<name>: Choose a Simulator device (e.g., "iPhone 16", "iPhone SE")--scheme=<name>: Specify the Xcode scheme--states: Test empty, error, and loading states via launch arguments--screenshot-all: Screenshot every step--performance: Measure RAM per screen and check for memory leaks (skipped by default)
Workflow
1) Project Discovery
- Find
.xcodeprojor.xcworkspacein the working directory- Prefer
.xcworkspaceif it exists (CocoaPods/SPM workspace) - If multiple exist, ask the user which one to use
- If none found, stop with an error
- Prefer
- List available schemes:
xcodebuild -list -workspace MyApp.xcworkspace- Use
--schemeif provided - If only one scheme exists, use it automatically
- If multiple, ask the user
- Use
2) Simulator Selection (BEFORE build)
IMPORTANT: Simulator must be selected BEFORE building. The build command uses the selected device name.
- If
--deviceis provided: find that device viaxcrun simctl list devices --json - If
--deviceis not provided, check booted simulators:xcrun simctl list devices booted --json- No booted simulator: list available devices with
xcrun simctl list devices available --json, suggest the best iPhone match, ask for confirmation, then boot it - One booted simulator: use it directly, no questions
- Multiple booted: list them and ask the user which one to use
- No booted simulator: list available devices with
3) Accessibility Check (MANDATORY — DO NOT SKIP)
This step is MANDATORY. Runs BEFORE build. NEVER skip this step.
- Scan SwiftUI files in the project — look for
struct...: Viewpatterns in*.swiftfiles - Count interactive elements:
Button,TextField,SecureField,Toggle,Slider,Picker,DatePicker,NavigationLink,Image,.onTapGesture - Check how many have
.accessibilityIdentifier() - ALWAYS ask the user (this question cannot be skipped):
Accessibility Scan Result:
Total interactive elements: XX
With identifier: XX
Missing identifier: XX
Identifiers help me find elements more reliably during testing.
Run /add-accessibility to add identifiers now?
→ Yes: adds identifiers, then builds and continues to testing
→ No: builds directly and uses coordinate-based testing (slower and more fragile)
- WAIT for the user's response. Do not proceed without an answer.
- If yes → run the full
/add-accessibilityworkflow (scan, generate, apply), then continue to step 4 (Build) - If no → continue to step 4 (Build) directly
4) Build
- Build the selected scheme using the device chosen in step 2:
NEVER hardcode a device name. Always use the device selected in step 2.xcodebuild build \ -workspace MyApp.xcworkspace \ -scheme MyApp \ -destination 'platform=iOS Simulator,name=<SELECTED_DEVICE>' \ -derivedDataPath ./DerivedData \ 2>&1 - If build fails: analyze errors, suggest fixes, and STOP — never proceed to testing with a failed build
- Report build warnings (but don't stop)
5) Install & Launch
- Find the built
.appfile:find ./DerivedData -name "*.app" -path "*/Debug-iphonesimulator/*" | head -1 - Get the bundle identifier:
plutil -p /path/to/MyApp.app/Info.plist | grep CFBundleIdentifier - Install and launch:
xcrun simctl install booted /path/to/MyApp.app xcrun simctl launch booted <BUNDLE_ID>
6) Visual Testing with Computer Use
IMPORTANT: This phase requires the computer-use MCP server to be enabled.
If computer use is not enabled, tell the user:
Computer use is not enabled. Required for visual testing.
Run /mcp and enable the computer-use server.
Default test (no arguments)
Discover and test all main screens:
- TabView present → tap each tab, inspect each screen
- NavigationStack present → tap each navigation link, go back
- On each screen check:
- Layout renders correctly (no overflow, overlapping, or empty areas)
- Buttons are tappable
- Scroll works and content is visible
- Text is readable (not too small, not truncated)
- Screenshot each screen
With --flow=<name>
Test the specified user flow:
- onboarding: swipe through onboarding screens, complete all steps
- login: fill email/password fields (test@example.com / Test1234), tap login, verify next screen
- signup: fill the registration form, tap signup
- checkout: navigate to cart, complete the payment flow
- settings: open settings, try each toggle/slider, go back
- Other flows: ask the user "What steps should I follow for this flow?"
With --screen=<ViewName>
Find and test only the specified screen. Navigate to it if needed.
7) State Testing (--states only)
Test different app states using launch arguments.
Check if the app supports state overrides via
CommandLine.arguments:- Search SwiftUI files for
--show-empty-state,--show-error-state,--show-loading-state - Search for
@AppStorageorUserDefaultsdebug flags
- Search SwiftUI files for
If not found, ask the user:
No state test support found. Would you like me to add launch argument handling to your @main App struct? A #if DEBUG block will be added supporting: - --show-empty-state - --show-error-state - --show-loading-state Only active in DEBUG builds. Add it?If available, relaunch the app for each state:
xcrun simctl terminate booted com.example.MyApp xcrun simctl launch booted com.example.MyApp --show-empty-stateFor each state verify:
- Empty state: shows a meaningful message ("No content yet")
- Error state: clear error message with a retry button
- Loading state: loading indicator visible, UI not frozen
- Screenshot each state
8) Crash Log Analysis
Check for crashes during and after testing:
xcrun simctl spawn booted log show --predicate 'process == "MyApp" AND messageType == 21' --last 5m
If a crash is found:
- Analyze the stack trace
- Identify which screen / action triggered it
- Find the relevant source code and report the likely cause
9) Performance Analysis (--performance only)
ONLY run when --performance is provided. SKIP in default tests.
Measure RAM usage per screen and check for memory leaks.
Find the app's PID:
xcrun simctl spawn booted launchctl list | grep <BundleID>Take a baseline measurement on launch:
footprint -all <PID>On each screen transition:
- Navigate to the screen (via computer use)
- Wait 2 seconds (let rendering complete)
- Run
footprint -all <PID>, record RAM - Calculate diff from previous screen
Check for leaks on back navigation:
- Enter a screen → measure RAM
- Go back → measure RAM
- If delta > 5MB → flag as potential leak
Run a full leak scan at the end:
leaks <PID>Report: leak count, object types, stack traces
Performance report format:
Performance Report ━━━━━━━━━━━━━━━━━━━━━━━━━ Baseline (launch): 42 MB Screen Transitions: HomeView: 45 MB (+3 MB) SettingsView: 52 MB (+7 MB) ProfileView: 68 MB (+16 MB) ⚠️ → HomeView (back): 61 MB (-7 MB, 16 MB not released) ⚠️ Memory Leaks: 2 found - SettingsViewModel: 1 leak (closure retain cycle?) - ImageCache: 1 leak ⚠️ High RAM: ProfileView (+16 MB jump) ⚠️ Potential Leak: 16 MB not released on return to HomeView
10) Test Report
Show a summary report when testing is complete:
iOS Test Report
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
App: MyApp (com.example.MyApp)
Device: iPhone 16 (iOS 18.2)
Scheme: MyApp
Duration: 2m 34s
Screens Tested: 8
✅ HomeView - OK
✅ ProfileView - OK
⚠️ SettingsView - Toggle "notifications" not tappable
✅ OnboardingStep1 - OK
✅ OnboardingStep2 - OK
✅ OnboardingStep3 - OK
❌ CheckoutView - Crash (force unwrap on nil)
✅ SearchView - OK
Screenshots: 12 captured
Crashes: 1
CheckoutView.swift:42 - Force unwrap on nil optional
UI Issues: 1
SettingsView - notifications toggle not accessible
State Test Results:
✅ Empty State - OK
⚠️ Error State - Missing retry button
✅ Loading State - OK
Important Rules
- NEVER proceed to testing if the build fails
- Cannot do visual testing without computer use — tell the user to enable it
- Don't ask unnecessary questions about Simulator selection — use the one that's booted
- Report every error and warning but NEVER modify code without user approval
- Don't delete or modify the app's data during testing
- Don't suggest adding launch arguments unless
--statesis provided - When a crash is detected, show the stack trace alongside the relevant source code
- Save screenshots with meaningful names (e.g.,
test-home-screen.png) - Do NOT close the Simulator after testing — the user may want to continue
- Provide brief status messages at each phase ("Building...", "Launching Simulator...", etc.)