home / skills / vladimirbrejcha / ios-ai-skills / swiftui-simulator-ui

swiftui-simulator-ui skill

/swiftui-simulator-ui

This skill helps you run SwiftUI apps in iOS Simulator to capture screenshots for visual verification and UI validation.

npx playbooks add skill vladimirbrejcha/ios-ai-skills --skill swiftui-simulator-ui

Review the files below or copy the command above to add this skill to your agents.

Files (9)
SKILL.md
11.2 KB
---
name: swiftui-simulator-ui
description: Run SwiftUI apps in iOS Simulator and capture screenshots for visual verification. Use when building UI, reviewing layouts, validating designs, or when you need to see what SwiftUI code looks like. Essential for any UI work requiring visual feedback.
allowed-tools: Bash, Read, Grep, Glob, Write
---

# SwiftUI Simulator UI Feedback Loop

Build, run, and capture SwiftUI apps in iOS Simulator for visual verification. This skill enables AI agents to see what their UI code produces.

## When to Use This Skill

Use this skill when:
- Building or modifying SwiftUI views and need visual verification
- Reviewing UI layouts, spacing, colors, or typography
- Validating designs against specifications
- Debugging visual issues (clipping, alignment, overlapping)
- Capturing screenshots for documentation or handoffs
- Testing different device sizes or appearances (dark mode, Dynamic Type)

## Prerequisites

Before starting:
1. **Xcode installed** with iOS Simulator support
2. **Project builds successfully** (run `xcodebuild -list` to verify)
3. **At least one iOS Simulator** available (`xcrun simctl list devices available`)

## Quick Start (5-Step Workflow)

### Step 1: Find Your Project Structure

```bash
# For .xcworkspace (if using CocoaPods/SPM packages)
xcodebuild -workspace /path/to/App.xcworkspace -list

# For .xcodeproj
xcodebuild -project /path/to/App.xcodeproj -list

# Output shows available schemes - pick the one you want to run
```

### Step 2: Find Available Simulators

```bash
# List available iPhone simulators
xcrun simctl list devices available | grep iPhone

# Get UDID for a specific device (e.g., iPhone 16)
xcrun simctl list devices available | sed -nE '/iPhone 16/{s/.*\(([A-F0-9-]+)\).*/\1/p; q;}'
```

### Step 3: Build the App

```bash
# Set your variables
PROJECT_PATH="/path/to/App.xcodeproj"  # or .xcworkspace
SCHEME="YourScheme"
DEVICE_NAME="iPhone 16"
DERIVED_DATA="/tmp/MyAppBuild"

# Get UDID
UDID=$(xcrun simctl list devices available | sed -nE "/$DEVICE_NAME/{s/.*\(([A-F0-9-]+)\).*/\1/p; q;}")

# Build
xcodebuild \
  -project "$PROJECT_PATH" \
  -scheme "$SCHEME" \
  -destination "platform=iOS Simulator,id=$UDID" \
  -derivedDataPath "$DERIVED_DATA" \
  build
```

### Step 4: Install and Launch

```bash
# Find the built .app
APP_PATH=$(find "$DERIVED_DATA" -name "*.app" -type d | head -1)

# Boot simulator if needed
xcrun simctl boot "$UDID" 2>/dev/null || true

# Install
xcrun simctl install "$UDID" "$APP_PATH"

# Launch (get bundle ID from Info.plist or build settings)
BUNDLE_ID="com.example.yourapp"
xcrun simctl launch "$UDID" "$BUNDLE_ID"
```

### Step 5: Capture Screenshot

```bash
# Create output directory
mkdir -p /tmp/UIScreenshots

# Take screenshot
xcrun simctl io "$UDID" screenshot "/tmp/UIScreenshots/screenshot-$(date +%Y%m%d-%H%M%S).png"

# The path is printed - use this to view the screenshot
```

## Complete Workflow Script

Copy and adapt this script for your project:

```bash
#!/usr/bin/env bash
set -euo pipefail

# ============ CONFIGURATION (EDIT THESE) ============
PROJECT_PATH="${PROJECT_PATH:-/path/to/App.xcodeproj}"  # or .xcworkspace
SCHEME="${SCHEME:-YourScheme}"
BUNDLE_ID="${BUNDLE_ID:-com.example.yourapp}"
DEVICE_NAME="${DEVICE_NAME:-iPhone 16}"
DERIVED_DATA="${DERIVED_DATA:-/tmp/AppBuild}"
SCREENSHOT_DIR="${SCREENSHOT_DIR:-/tmp/UIScreenshots}"
# ====================================================

# Resolve simulator UDID
resolve_udid() {
  xcrun simctl list devices available | sed -nE "/$1/{s/.*\(([A-F0-9-]+)\).*/\1/p; q;}"
}

UDID=$(resolve_udid "$DEVICE_NAME")
if [[ -z "$UDID" ]]; then
  echo "error: Simulator '$DEVICE_NAME' not found. Available devices:"
  xcrun simctl list devices available | grep iPhone
  exit 1
fi

echo "Using simulator: $DEVICE_NAME ($UDID)"

# Boot simulator
if ! xcrun simctl list devices booted | grep -q "$UDID"; then
  echo "Booting simulator..."
  xcrun simctl boot "$UDID" 2>/dev/null || true
  sleep 2
fi

# Build
echo "Building..."
xcodebuild \
  -project "$PROJECT_PATH" \
  -scheme "$SCHEME" \
  -destination "platform=iOS Simulator,id=$UDID" \
  -derivedDataPath "$DERIVED_DATA" \
  -quiet \
  build

# Find app
APP_PATH=$(find "$DERIVED_DATA" -name "*.app" -type d | head -1)
if [[ ! -d "$APP_PATH" ]]; then
  echo "error: Built app not found in $DERIVED_DATA"
  exit 1
fi

# Install
echo "Installing..."
xcrun simctl install "$UDID" "$APP_PATH"

# Terminate if running, then launch
xcrun simctl terminate "$UDID" "$BUNDLE_ID" 2>/dev/null || true
xcrun simctl launch "$UDID" "$BUNDLE_ID"

echo "Launched $SCHEME on $DEVICE_NAME"

# Screenshot
mkdir -p "$SCREENSHOT_DIR"
SCREENSHOT_PATH="$SCREENSHOT_DIR/$(date +%Y%m%d-%H%M%S).png"
sleep 1  # Wait for app to render
xcrun simctl io "$UDID" screenshot "$SCREENSHOT_PATH"

echo "Screenshot saved: $SCREENSHOT_PATH"
```

## Advanced Patterns

### Pattern 1: Navigate to Specific Screen via Deep Link

```bash
# Launch app with deep link
xcrun simctl openurl "$UDID" "yourapp://settings/profile"

# Wait and screenshot
sleep 1
xcrun simctl io "$UDID" screenshot /tmp/profile-screen.png
```

### Pattern 2: Configure App State via UserDefaults

```bash
# Write defaults before launch
xcrun simctl spawn "$UDID" defaults write "$BUNDLE_ID" ShowOnboarding -bool false
xcrun simctl spawn "$UDID" defaults write "$BUNDLE_ID" DebugMode -bool true
xcrun simctl spawn "$UDID" defaults write "$BUNDLE_ID" MockUserName -string "Test User"

# Terminate and relaunch to pick up changes
xcrun simctl terminate "$UDID" "$BUNDLE_ID"
xcrun simctl launch "$UDID" "$BUNDLE_ID"
```

### Pattern 3: Test Different Appearances

```bash
# Enable dark mode
xcrun simctl ui "$UDID" appearance dark
sleep 0.5
xcrun simctl io "$UDID" screenshot /tmp/dark-mode.png

# Enable light mode
xcrun simctl ui "$UDID" appearance light
sleep 0.5
xcrun simctl io "$UDID" screenshot /tmp/light-mode.png
```

### Pattern 4: Override Status Bar for Clean Screenshots

```bash
# Set time to 9:41, full battery, WiFi
xcrun simctl status_bar "$UDID" override \
  --time "9:41" \
  --batteryLevel 100 \
  --batteryState charged \
  --dataNetwork wifi \
  --wifiBars 3

xcrun simctl io "$UDID" screenshot /tmp/clean-screenshot.png

# Clear overrides
xcrun simctl status_bar "$UDID" clear
```

### Pattern 5: Test Multiple Device Sizes

```bash
# Test on different devices
for DEVICE in "iPhone SE (3rd generation)" "iPhone 16" "iPhone 16 Pro Max"; do
  UDID=$(xcrun simctl list devices available | sed -nE "/$DEVICE/{s/.*\(([A-F0-9-]+)\).*/\1/p; q;}")
  if [[ -n "$UDID" ]]; then
    xcrun simctl boot "$UDID" 2>/dev/null || true
    xcrun simctl install "$UDID" "$APP_PATH"
    xcrun simctl launch "$UDID" "$BUNDLE_ID"
    sleep 2
    xcrun simctl io "$UDID" screenshot "/tmp/${DEVICE// /-}.png"
    xcrun simctl shutdown "$UDID"
  fi
done
```

### Pattern 6: Preview Lab Target (Isolated UI Testing)

Create a dedicated target for UI preview:

```swift
// In a PreviewLab target
@main
struct PreviewLabApp: App {
    var body: some Scene {
        WindowGroup {
            // Show specific view based on launch argument
            switch ProcessInfo.processInfo.environment["PREVIEW_VIEW"] {
            case "settings":
                SettingsView()
            case "profile":
                ProfileView()
            default:
                ContentView()
            }
        }
    }
}
```

Launch with environment:

```bash
# Set environment variable
xcrun simctl spawn "$UDID" launchctl setenv PREVIEW_VIEW settings

# Launch
xcrun simctl launch "$UDID" "$BUNDLE_ID"
```

### Pattern 7: Record Video of Interaction

```bash
# Start recording in background
xcrun simctl io "$UDID" recordVideo /tmp/recording.mp4 &
RECORD_PID=$!

# Wait for interaction (or interact manually)
sleep 5

# Stop recording
kill -INT $RECORD_PID
```

## Finding Bundle ID

If you don't know the bundle ID:

```bash
# From build settings
xcodebuild -project "$PROJECT_PATH" -scheme "$SCHEME" -showBuildSettings | grep PRODUCT_BUNDLE_IDENTIFIER

# From installed app
xcrun simctl listapps "$UDID" | grep -A1 CFBundleIdentifier

# From .app Info.plist after build
/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "$APP_PATH/Info.plist"
```

## Troubleshooting

### Build Fails: "No matching destination"

```bash
# Check available destinations
xcodebuild -project "$PROJECT_PATH" -scheme "$SCHEME" -showDestinations

# Use exact simulator name from list
xcrun simctl list devices available
```

### Simulator Won't Boot

```bash
# Check current state
xcrun simctl list devices | grep -A5 "== Devices =="

# Force shutdown and reboot
xcrun simctl shutdown "$UDID"
xcrun simctl boot "$UDID"

# If still failing, reset simulator
xcrun simctl erase "$UDID"
```

### App Won't Install

```bash
# Check if app is built correctly
ls -la "$APP_PATH"

# Check architecture (must be x86_64 or arm64 for sim)
file "$APP_PATH/$(basename "$APP_PATH" .app)"

# Check minimum deployment target
/usr/libexec/PlistBuddy -c "Print MinimumOSVersion" "$APP_PATH/Info.plist"
```

### Screenshot Shows Wrong Simulator

When multiple simulators are booted:

```bash
# List booted simulators
xcrun simctl list devices booted

# Always specify UDID explicitly
xcrun simctl io "$UDID" screenshot /tmp/screenshot.png

# Or shutdown other simulators
xcrun simctl shutdown all
xcrun simctl boot "$UDID"
```

### App Launches but UI Doesn't Load

```bash
# Check console output
xcrun simctl launch --console "$UDID" "$BUNDLE_ID"

# Stream logs
xcrun simctl spawn "$UDID" log stream --predicate 'processImagePath CONTAINS "YourApp"' --level debug
```

### Screenshot is Black

```bash
# Wait longer for app to render
sleep 2
xcrun simctl io "$UDID" screenshot /tmp/screenshot.png

# Ensure simulator window is visible (not minimized)
# Or run in headless mode properly
```

## Environment Variables Reference

| Variable | Description | Default |
|----------|-------------|---------|
| `PROJECT_PATH` | Path to .xcodeproj or .xcworkspace | Required |
| `SCHEME` | Xcode build scheme | Required |
| `BUNDLE_ID` | App bundle identifier | Required |
| `DEVICE_NAME` | Simulator device name | `iPhone 16` |
| `DEVICE_UDID` | Specific simulator UDID | Auto-resolved |
| `DERIVED_DATA` | Build output directory | `/tmp/AppBuild` |
| `SCREENSHOT_DIR` | Screenshot output directory | `/tmp/UIScreenshots` |

## Detailed References

- **references/simulator-commands.md** - Complete `xcrun simctl` command reference
- **references/xcodebuild-patterns.md** - Build patterns and configurations
- **references/troubleshooting.md** - Extended troubleshooting guide
- **assets/run_app.sh** - Reusable run script template
- **assets/screenshot.sh** - Standalone screenshot script

## Integration with Handoffs

When using this skill for UI work:

1. Run the UI feedback loop after making changes
2. Capture a screenshot
3. Include the screenshot path in your handoff file
4. Example handoff entry:

```markdown
## Progress

- [x] Implemented ProfileView header
- Screenshot: `/tmp/UIScreenshots/20250131-143022.png`
- Notes: Layout matches spec, tested on iPhone 16 and iPhone SE
```

## Key Principles

1. **Always verify visually** - Don't assume UI code is correct; run it and see
2. **Use consistent device** - Pick one simulator and stick with it for consistency
3. **Clean screenshots** - Override status bar for professional captures
4. **Test edge cases** - Dark mode, small screens, Dynamic Type
5. **Document with screenshots** - Include paths in handoffs for traceability

Overview

This skill runs SwiftUI apps in the iOS Simulator and captures screenshots for visual verification. It provides a repeatable workflow to build, install, launch, and image your app across devices and appearances. Use it to get fast visual feedback during UI development, testing, and documentation.

How this skill works

The skill uses xcodebuild to compile a specified scheme into a DerivedData build output, then locates the .app bundle. It uses xcrun simctl to resolve a simulator UDID, boot or prepare the simulator, install and launch the app, and capture screenshots or recordings. It supports deep links, environment overrides, status bar overrides, appearance toggles, and multiple device loops for comprehensive visual checks.

When to use it

  • After changing SwiftUI views to verify layout, spacing, and typography
  • When validating designs against specs or QA acceptance criteria
  • To debug visual issues like clipping, alignment, or missing assets
  • Preparing screenshots for documentation, handoffs, or release notes
  • Testing UI across device sizes, dark mode, and Dynamic Type

Best practices

  • Ensure Xcode and iOS Simulator are installed and the project builds locally
  • Resolve the exact simulator UDID and use it explicitly to avoid wrong targets
  • Override the status bar for clean, consistent screenshots
  • Use environment variables or deep links to reach specific screens deterministically
  • Test on a small set of representative devices rather than every simulator

Example use cases

  • Run a PreviewLab target to show an isolated view, then screenshot for a design review
  • Boot iPhone 16 and iPhone SE, install the app, and capture both screenshots for responsive checks
  • Set UserDefaults mocks via simctl spawn, relaunch the app, and verify onboarding is skipped
  • Toggle dark and light appearances and capture screenshots for theme QA
  • Record a short video of an interaction to demonstrate an animation or bug

FAQ

What prerequisites are required?

Xcode with Simulator support and a project that builds successfully. Also ensure at least one available simulator.

How do I target a specific screen?

Use deep links, environment vars (launchctl setenv), or a PreviewLab app target to present the desired view before taking a screenshot.