Date: 2026-03-29
Context: UI verification and visual testing tools for Apple platforms (SwiftUI, UIKit, AppKit), integrated with Claude Code workflows.
| Type |
Strategy |
Output |
Platforms |
Any |
.description |
String |
All |
Any |
.dump |
String (sanitized mirror) |
All |
CALayer |
.image |
UIImage/NSImage |
iOS, macOS, tvOS |
CGPath |
.image |
UIImage/NSImage |
iOS, macOS, tvOS |
CGPath |
.elementsDescription |
String |
iOS, macOS, tvOS |
Encodable |
.json |
String (JSON) |
All |
Encodable |
.plist |
String (XML plist) |
All |
NSView |
.image |
NSImage |
macOS |
NSView |
.recursiveDescription |
String |
macOS |
NSViewController |
.image |
NSImage |
macOS |
NSViewController |
.recursiveDescription |
String |
macOS |
UIView |
.image |
UIImage |
iOS, tvOS |
UIView |
.recursiveDescription |
String |
iOS, tvOS |
UIViewController |
.image |
UIImage |
iOS, tvOS |
UIViewController |
.hierarchy |
String |
iOS, tvOS |
UIViewController |
.recursiveDescription |
String |
iOS, tvOS |
UIImage |
.image |
UIImage |
iOS, tvOS |
NSImage |
.image |
NSImage |
macOS |
UIBezierPath |
.image / .elementsDescription |
UIImage / String |
iOS, tvOS |
NSBezierPath |
.image / .elementsDescription |
NSImage / String |
macOS |
SCNScene |
.image |
UIImage/NSImage |
iOS, macOS, tvOS |
SKScene |
.image |
UIImage/NSImage |
iOS, macOS, tvOS |
String |
.lines |
String (line diff) |
All |
URLRequest |
.curl |
String |
All |
URLRequest |
.raw |
String |
All |
CaseIterable |
.func(into:) |
CSV |
All |
- Record mode: Set
isRecording = true globally or per-test to regenerate reference snapshots. As of 1.17, use withSnapshotTesting(record: .all) or withSnapshotTesting(record: .failed).
- Precision: Image strategies accept a
precision parameter (0.0-1.0, default 1.0) for fuzzy matching.
- Device simulation: Render on a specific device without needing that simulator:
.image(on: .iPhoneSe), .image(on: .iPadPro12_9).
- Trait collections: Override size classes, content size categories, and layout direction.
- Snapshot directory: Defaults to
__Snapshots__ alongside the test file. Override with snapshotDirectory parameter.
- Reference images are committed to the repo alongside test files.
- Image snapshots are pixel-sensitive to OS version, simulator model, and scale factor. Pin CI to a specific Xcode/simulator version.
- Text-based strategies (
.dump, .recursiveDescription, .json) are more portable across environments than image strategies.
- Set
SNAPSHOT_ARTIFACTS environment variable to export failed snapshot diffs to a CI artifacts directory.
- Add snapshot tests in a test target. Run with
swift test or xcodebuild test.
- Use
.dump or .recursiveDescription strategies when Claude Code cannot view images -- these produce text diffs readable in the terminal.
- Set
isRecording = true to regenerate baselines after intentional UI changes.
- Link: developer.apple.com/documentation/xctest/ui_testing
- Description: Apple's first-party UI testing framework built into XCTest. Tests run in a separate process from the app, interacting through accessibility APIs. Supports element queries, gestures, screenshots, and assertions.
- Platforms: iOS, macOS, tvOS, watchOS (limited), visionOS
| Class |
Purpose |
XCUIApplication |
Proxy to launch, monitor, and terminate the app under test |
XCUIElement |
Represents a single UI element; supports taps, swipes, typing, value assertions |
XCUIElementQuery |
Queries for elements by type, identifier, predicate, or index |
XCUIScreen |
Represents a physical screen; used for screenshots |
XCUIScreenshot |
A captured image of a screen, app, or element |
XCTAttachment |
Wraps screenshots or data for inclusion in test results |
let app = XCUIApplication()
app.launch()
// By accessibility identifier
let button = app.buttons["submitButton"]
// By label text
let label = app.staticTexts["Welcome"]
// By predicate
let cells = app.cells.matching(NSPredicate(format: "label CONTAINS 'Item'"))
// Descendants of any type (useful for SwiftUI)
let element = app.descendants(matching: .any)["myIdentifier"]
element.tap()
element.doubleTap()
element.press(forDuration: 1.0)
element.swipeUp()
element.swipeDown()
element.swipeLeft()
element.swipeRight()
element.pinch(withScale: 2.0, velocity: 1.0) // iOS only
element.rotate(0.5, withVelocity: 1.0) // iOS only
element.typeText("Hello")
let screenshot = app.screenshot()
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = "Home Screen"
attachment.lifetime = .keepAlways
add(attachment)
- Configure parallel testing, language/locale overrides, and environment variables via
.xctestplan files in Xcode.
- Parallel testing distributes test classes across multiple simulator clones.
- Enable in scheme settings or via
xcodebuild test -parallel-testing-enabled YES.
- Run tests:
xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16' -only-testing:MyUITests
- Extract results:
xcresulttool get --path path/to/Test.xcresult --format json
- Screenshots from test results appear as XCTAttachments in the xcresult bundle.
// In your PreviewProvider:
struct MyView_Previews: PreviewProvider {
static var snapshots: PreviewSnapshots<String> {
PreviewSnapshots(
configurations: [
.init(name: "Default", state: "Hello"),
.init(name: "Long Text", state: "A very long string"),
],
configure: { state in MyView(text: state) }
)
}
static var previews: some View {
snapshots.previews
}
}
// In your test file:
func testMyView() {
MyView_Previews.snapshots.assertSnapshots()
}
Does not work with the #Preview macro (only PreviewProvider).
- Parses all source files for
#Preview and PreviewProvider blocks at build time
- Caches parsed results using SHA-256 fingerprints (only re-parses changed files)
- Generates snapshot test code via Stencil templates
- Optionally generates a Playbook view for browsing all UI components in-app
test_configuration:
target: MyApp
snapshot_devices:
- iPhone 16
- iPad Pro 13-inch
playbook_configuration:
preview_default_enabled: true
#Preview {
MyView()
.prefireEnabled() // Include in generated tests
.previewUserStory("Login") // Group in playbook
.previewState("Error") // Label the state
}
- After adding Prefire, run
swift build to trigger code generation, then swift test to run generated snapshot tests.
- The Playbook view (
PlaybookView) provides an in-app component gallery.
| Feature |
Description |
| SnapshotTest |
Base class for auto-generating PNG snapshots from all discovered previews |
| PreviewGallery |
Interactive browsable gallery of all previews, usable inside the app |
| AccessibilityPreviewTest |
Runs VoiceOver audits on previews with configurable audit types |
| Filtering |
Override methods to include/exclude specific previews |
- Subclass
SnapshotTest in your test target -- no manual test methods needed.
- Run with
xcodebuild test to generate snapshots for every preview in the project.
- Link: WWDC23: Perform accessibility audits for your app
- Description: Built-in XCTest method (Xcode 15+) that audits any
XCUIElement for accessibility issues. Fails the test automatically when issues are found. Scoped audits let you check the entire app, a single screen, or an individual component.
- Platforms: iOS, macOS, tvOS, visionOS
// Basic audit -- checks everything on screen
func testAccessibility() throws {
let app = XCUIApplication()
app.launch()
try app.performAccessibilityAudit()
}
// Scoped audit with category filter and known-issue suppression
func testAccessibilityFiltered() throws {
let app = XCUIApplication()
app.launch()
try app.performAccessibilityAudit(for: [.dynamicType, .contrast]) { issue in
// Ignore known issue on a specific element
if let element = issue.element,
element.label == "decorativeImage",
issue.auditType == .contrast {
return true // true = ignore this issue
}
return false
}
}
| Category |
What It Checks |
.dynamicType |
Text scales properly with Dynamic Type settings |
.contrast |
Sufficient color contrast ratios |
.elementDetection |
Elements are properly exposed to accessibility |
.hitRegion |
Touch targets meet minimum size requirements |
.sufficientElementDescription |
Elements have meaningful labels |
.textClipped |
Text is not clipped or truncated |
.trait |
Correct accessibility traits assigned |
- Only audits elements currently visible on screen. Navigate to each view and audit separately.
- Does not replace manual VoiceOver testing -- supplements it.
- Link: developer.apple.com/documentation/accessibility
- Description: Xcode's built-in tool for inspecting accessibility properties of any element in the simulator or on-device. Shows labels, values, traits, and hierarchy. Includes a one-click audit that runs the same checks as
performAccessibilityAudit.
- Platforms: iOS, macOS, tvOS, watchOS, visionOS (via simulator or device)
# Open Accessibility Inspector
open -a "Accessibility Inspector"
# Accessibility Inspector is GUI-only, but the audit categories
# it exposes are the same ones available in performAccessibilityAudit()
- Use
performAccessibilityAudit() in XCTest for automated checks.
- Accessibility Inspector is a GUI tool -- not directly usable from Claude Code. Instead, rely on XCTest audit methods and
XCUIElement property assertions:
XCTAssertEqual(element.label, "Submit Order")
XCTAssertTrue(element.isEnabled)
XCTAssertNotNil(element.value)
- Link: Part of SnapshotPreviews
- Description: Runs VoiceOver accessibility audits on all discovered Xcode previews. Subclass
AccessibilityPreviewTest to automatically audit every preview in your project.
- Platforms: iOS, macOS, watchOS, tvOS, visionOS
- Link: WWDC25 Session 344
- Description: New Xcode 17 UI automation features for recording, replaying, and reviewing UI interactions. Includes improved accessibility-driven automation and stable element identification for SwiftUI views.
- Platforms: iOS, macOS, visionOS
- Link: screenshotbot.io
- Description: Cloud-hosted visual regression service for iOS, Android, and web. Integrates with CI to compare screenshots across builds, posting diff reports to pull requests (GitHub, GitLab). Stores screenshot history -- no Git LFS needed.
- Platforms: iOS, Android, Web (screenshot source agnostic)
- Generate screenshots via swift-snapshot-testing or XCUITest
- Call the Screenshotbot CLI in CI to upload screenshots
- Screenshotbot compares against the base branch and posts a PR comment with visual diffs
- Review Screenshotbot PR comments for visual regressions.
- Not directly invocable from Claude Code -- operates in CI.
- Link: applitools.com
- Description: AI-powered visual testing platform. Uses Visual AI to detect meaningful visual differences while ignoring rendering noise. Supports design-to-code comparison via Figma plugin. Eyes 10.22 added Storybook and Figma design validation.
- Platforms: iOS (via Appium/XCUITest), Android, Web
- Pricing: Commercial (free tier available)
- Export Figma frames as baselines via the Eyes Figma Plugin.
- Compare live app screenshots against design baselines.
- Visual AI flags meaningful differences, ignoring minor rendering shifts.
- Link: percy.io
- Description: Visual review platform that captures and compares screenshots across builds. App Percy extends support to native iOS and Android apps on real devices.
- Platforms: iOS, Android, Web
- Pricing: Commercial (free tier for open source)
# Extract all screenshots from a test result
xcparse screenshots /path/to/Test.xcresult /output/directory
# Filter by test status (only failed tests)
xcparse screenshots --test-status "Failure" /path/to/Test.xcresult /output/directory
# Filter by activity type
xcparse screenshots --activity-type "com.apple.dt.xctest.activity-type.userCreated" /path/to/Test.xcresult /output/directory
# Extract code coverage
xcparse codecov /path/to/Test.xcresult /output/directory
Uses --legacy flag for Xcode 16+ where some xcresulttool commands were deprecated.
- Run
xcparse screenshots after xcodebuild test to extract screenshots for review.
- Combine with image diff tools for automated visual regression in CI.
- Link: Built into Xcode Command Line Tools
- Description: Apple's command-line interface for interacting with iOS Simulator. Captures screenshots and records video directly from the simulator process.
- Platforms: iOS, watchOS, tvOS, visionOS (all simulator-based platforms)
# Capture screenshot of booted simulator
xcrun simctl io booted screenshot screenshot.png
# Specify format (png, tiff, bmp, gif, jpeg)
xcrun simctl io booted screenshot --type=jpeg screenshot.jpg
# Record video
xcrun simctl io booted recordVideo video.mp4
# Override status bar for clean screenshots
xcrun simctl status_bar booted override --time "9:41" --batteryState charged --batteryLevel 100
# Reset status bar
xcrun simctl status_bar booted clear
- Capture screenshots directly:
xcrun simctl io booted screenshot /tmp/screen.png
- Override status bar before screenshot for consistent CI images.
- Combine with diff tools for before/after comparison.
// Capture app screenshot
let screenshot = XCUIApplication().screenshot()
// Capture specific screen
let screen = XCUIScreen.main.screenshot()
// Attach to test results with permanent retention
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = "Login Screen - Dark Mode"
attachment.lifetime = .keepAlways // .deleteOnSuccess is default
add(attachment)
- Use
.xctestplan files to configure screenshot capture across multiple locales and devices.
- Each test plan configuration runs the same tests with different settings, generating screenshots for every combination.
- Link: github.com/yorifuji/mcp-ios-simulator-screenshot
- Description: MCP server that captures iOS Simulator screenshots and saves them to a specified directory. Lightweight, single-purpose tool.
- Platforms: iOS Simulator
- Install:
{
"mcpServers": {
"ios-simulator-screenshot": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-ios-simulator-screenshot", "--output-dir", "/tmp/screenshots"]
}
}
}
- Requirements: Node.js >= 18, Xcode with simulator
- Directly captures simulator screenshots through MCP tool calls.
- Screenshots saved to configured output directory for comparison.
| Tool |
Description |
screenshot |
Captures JPEG screenshot (~2MB max), returns image for visual inspection |
get_ui_hierarchy |
Returns accessibility tree with element positions and labels |
tap |
Tap at x,y coordinates |
swipe |
Swipe between two points |
type_text |
Type text into focused field |
tap_and_type |
Combined tap-to-focus then type |
press_button |
Hardware buttons (home, lock, volume) |
launch_app / terminate_app |
App lifecycle control |
set_location |
GPS coordinate simulation |
- Inspect UI hierarchy to verify element placement and accessibility labels.
- Capture screenshots after interactions for visual verification.
- Automate tap/swipe sequences for end-to-end testing.
- Link: xcodebuildmcp.com
- Description: Comprehensive MCP server giving AI agents full control over Xcode. 59+ tools covering building, testing, debugging (LLDB), simulator interaction, and deployment. Auto-detects schemes and simulators.
- Platforms: iOS, macOS, watchOS, tvOS, visionOS
- Install:
{
"mcpServers": {
"XcodeBuildMCP": {
"command": "npx",
"args": ["-y", "xcodebuildmcp@latest", "mcp"]
}
}
}
| Capability |
Description |
| Build and test |
Run xcodebuild test with automatic error detection |
| Screenshot capture |
Capture simulator screenshots during test runs |
| LLDB debugging |
Set breakpoints, inspect variables, step through code |
| UI automation |
Tap, swipe, interact with simulator UI |
| Scheme detection |
Auto-discovers available schemes and destinations |
- Build, test, and debug Apple platform projects entirely through MCP tool calls.
- Capture screenshots and inspect UI state without leaving the Claude Code session.
- Attach LLDB debugger to investigate UI rendering issues.
- Link: applitools.com/docs/eyes/integrations/design-validation/figma-plugin
- Description: Export Figma frames as visual baselines, then compare live app screenshots against them. Visual AI detects meaningful differences, filtering out rendering noise and anti-aliasing artifacts.
- Platforms: Any (Figma source, app screenshots from any platform)
- Pricing: Commercial
- Designer exports Figma frames to Applitools Eyes
- Developer runs app, captures screenshots (XCUITest, simulator, or manual)
- Eyes compares screenshots to Figma baselines
- AI flags meaningful differences (layout shifts, color changes, missing elements)
- Review and approve/reject in the Eyes dashboard
- Link: figma.com/community
- Description: Apple's official Figma UI kit for iOS and iPadOS 26 (updated March 2026). Includes Liquid Glass components, system controls, and layout templates. Use as a reference to verify your app matches system design patterns.
- Platforms: iOS, iPadOS
When automated tools are unavailable or impractical:
- Screenshot capture:
xcrun simctl io booted screenshot design-check.png
- Side-by-side comparison: Open Figma design and screenshot together
- Overlay diff: Use image editing tools to overlay at 50% opacity
- Checklist verification:
- Layout spacing and alignment
- Typography (font, size, weight, line height)
- Color accuracy (sample with color picker)
- Touch target sizes (minimum 44x44pt)
- Dark mode / light mode variants
- Dynamic Type at all sizes
- Safe area compliance
- Capture simulator screenshots and describe expected layout.
- Use text-based snapshot strategies (
.dump, .recursiveDescription) to verify view hierarchy matches expectations.
- For projects with the Figma Claude Code plugin installed, use
figma-implement-design skill's built-in validation checklist (Step 7) to verify layout, typography, colors, interactive states, responsive behavior, and accessibility.
- Link: developer.apple.com/documentation/xctest/xctmetric
- Description: XCTest performance measurement framework. Wrap code in
measure(metrics:) blocks to collect CPU, memory, storage, timing, and animation metrics. Baselines are stored per-device and Xcode flags regressions automatically.
- Platforms: iOS, macOS, tvOS, watchOS, visionOS
| Metric Class |
What It Measures |
XCTClockMetric |
Wall clock time elapsed |
XCTCPUMetric |
CPU cycles, instructions retired, CPU time |
XCTMemoryMetric |
Physical memory usage (peak and average) |
XCTStorageMetric |
Logical bytes written to storage |
XCTApplicationLaunchMetric |
App launch duration (cold and warm) |
XCTOSSignpostMetric |
Duration of os_signpost intervals, plus animation-specific metrics |
func testAppLaunchPerformance() {
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
func testScrollPerformance() {
let app = XCUIApplication()
app.launch()
let list = app.collectionViews.firstMatch
measure(metrics: [XCTOSSignpostMetric.scrollDraggingMetric]) {
list.swipeUp(velocity: .fast)
}
}
| Preset |
What It Measures |
.scrollDraggingMetric |
Performance during active finger-drag scrolling |
.scrollDecelerationMetric |
Performance during scroll deceleration (after finger lift) |
.scrollingAndDecelerationMetric |
Combined drag + deceleration |
.navigationTransitionMetric |
Duration and smoothness of view transitions |
.customNavigationTransitionMetric |
Custom navigation transition performance |
When measuring animation signpost intervals, XCTest reports:
| Metric |
Description |
| Frame count |
Total frames rendered in the interval |
| Frame rate |
Average frames per second |
| Hitch count |
Number of frames delivered late |
| Hitch total duration |
Cumulative late delivery time (ms) |
| Hitch time ratio |
Hitch ms per second of animation (target: < 5 ms/s) |
| Hitch Time Ratio |
Severity |
| < 5 ms/s |
Good -- imperceptible to users |
| 5-10 ms/s |
Warning -- subtle but detectable |
| > 10 ms/s |
Critical -- clearly visible stutter |
For deeper investigation beyond XCTest:
# Profile with Instruments from command line
xcrun xctrace record --template "Animation Hitches" --device booted --launch -- /path/to/MyApp.app
# Export trace for analysis
xcrun xctrace export --input recording.trace --output /tmp/trace-export
- Add performance test methods to the UI test target.
- Run with
xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16' -only-testing:MyPerfTests.
- Xcode stores baselines and flags regressions automatically. Review results via
xcresulttool.
- For scroll hitch detection, use the signpost metrics in CI to catch frame rate regressions before shipping.