Agent skill
snapshot-test-setup
Set up SwiftUI visual regression testing with swift-snapshot-testing. Generates snapshot test boilerplate and CI configuration. Use for UI regression prevention.
Install this agent skill to your Project
npx add-skill https://github.com/rshankras/claude-code-apple-skills/tree/main/skills/testing/snapshot-test-setup
SKILL.md
Snapshot Test Setup
Generate SwiftUI snapshot/visual regression tests using Point-Free's swift-snapshot-testing library. Catches unintended UI changes by comparing rendered views against reference images.
When This Skill Activates
Use this skill when the user:
- Wants "snapshot tests" or "visual regression tests"
- Says "I want to catch UI regressions"
- Asks about "screenshot testing" or "preview testing"
- Wants to verify SwiftUI views don't change unexpectedly
- Mentions "swift-snapshot-testing" or "Point-Free"
Why Snapshot Tests
Without snapshots: With snapshots:
Change a modifier Change a modifier
→ Looks fine locally → Snapshot test fails
→ Push to main → Shows exact visual diff
→ User reports UI bug → Fix before merging
→ Embarrassing → Confidence in UI changes
Pre-Setup Checks
1. Project Context
Glob: **/Package.swift or **/*.xcodeproj
Grep: "swift-snapshot-testing" (already added?)
Grep: "SnapshotTesting" in test files
2. Configuration Questions
Ask via AskUserQuestion:
-
Package manager?
- Swift Package Manager
- CocoaPods
- Tuist
-
Platform?
- iOS
- macOS
- Both
-
What to test?
- Specific views (user provides names)
- All screens
- Component library
Setup Process
Step 1: Add Dependency
Swift Package Manager
// Package.swift
dependencies: [
.package(
url: "https://github.com/pointfreeco/swift-snapshot-testing",
from: "1.17.0"
)
]
// Test target
.testTarget(
name: "YourAppTests",
dependencies: [
"YourApp",
.product(name: "SnapshotTesting", package: "swift-snapshot-testing")
]
)
Xcode Project
- File → Add Package Dependencies
- URL:
https://github.com/pointfreeco/swift-snapshot-testing - Add
SnapshotTestingto your test target
Step 2: Create Snapshot Test Base
import Testing
import SnapshotTesting
import SwiftUI
@testable import YourApp
// MARK: - Snapshot Configuration
enum SnapshotConfig {
// iOS devices to test
static let iPhoneConfigs: [String: ViewImageConfig] = [
"iPhone_SE": .iPhoneSe,
"iPhone_16": .iPhone13, // Similar dimensions
"iPhone_16_Pro_Max": .iPhone13ProMax
]
// macOS window sizes
static let macOSConfigs: [String: CGSize] = [
"compact": CGSize(width: 400, height: 600),
"regular": CGSize(width: 800, height: 600),
"wide": CGSize(width: 1200, height: 800)
]
// Color schemes to test
static let colorSchemes: [ColorScheme] = [.light, .dark]
}
Step 3: Generate Snapshot Tests
iOS View Snapshot
@Suite("Snapshots: HomeView")
struct HomeViewSnapshotTests {
@Test("matches reference - light mode")
func lightMode() {
let view = HomeView(items: Item.sampleList)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
@Test("matches reference - dark mode")
func darkMode() {
let view = HomeView(items: Item.sampleList)
.environment(\.colorScheme, .dark)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
@Test("matches reference - empty state")
func emptyState() {
let view = HomeView(items: [])
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
@Test("matches reference - dynamic type XXL")
func dynamicTypeXXL() {
let view = HomeView(items: Item.sampleList)
.environment(\.sizeCategory, .accessibilityExtraExtraLarge)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
}
macOS View Snapshot
@Suite("Snapshots: SettingsView")
struct SettingsViewSnapshotTests {
@Test("matches reference - standard size")
func standardSize() {
let view = SettingsView()
.frame(width: 500, height: 400)
assertSnapshot(
of: NSHostingController(rootView: view),
as: .image(size: CGSize(width: 500, height: 400))
)
}
@Test("matches reference - dark mode")
func darkMode() {
let view = SettingsView()
.frame(width: 500, height: 400)
.environment(\.colorScheme, .dark)
assertSnapshot(
of: NSHostingController(rootView: view),
as: .image(size: CGSize(width: 500, height: 400))
)
}
}
Component Snapshot (Reusable)
@Suite("Snapshots: ItemCard")
struct ItemCardSnapshotTests {
@Test("default state")
func defaultState() {
let view = ItemCard(item: .sample)
.frame(width: 300)
assertSnapshot(of: view, as: .image)
}
@Test("selected state")
func selectedState() {
let view = ItemCard(item: .sample, isSelected: true)
.frame(width: 300)
assertSnapshot(of: view, as: .image)
}
@Test("long title wraps")
func longTitle() {
let item = Item(title: "This is a very long title that should wrap to multiple lines")
let view = ItemCard(item: item)
.frame(width: 300)
assertSnapshot(of: view, as: .image)
}
}
Step 4: Recording Reference Images
First run records reference images (golden masters):
# Record all snapshots (first run)
xcodebuild test -scheme YourApp \
-destination 'platform=iOS Simulator,name=iPhone 16'
Important: Reference images are stored in __Snapshots__/ directories next to test files. Commit these to git.
Tests/SnapshotTests/
├── __Snapshots__/
│ └── HomeViewSnapshotTests/
│ ├── lightMode.1.png
│ ├── darkMode.1.png
│ ├── emptyState.1.png
│ └── dynamicTypeXXL.1.png
├── HomeViewSnapshotTests.swift
└── ItemCardSnapshotTests.swift
Step 5: Re-record When Intentional Changes
When you intentionally change a view:
// Temporarily set record mode
@Test("matches reference - light mode")
func lightMode() {
withSnapshotTesting(record: .all) {
let view = HomeView(items: Item.sampleList)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
}
Or use environment variable:
SNAPSHOT_TESTING_RECORD=all xcodebuild test -scheme YourApp
What to Snapshot
High Value (Always Snapshot)
- Screens/pages with multiple states (empty, loaded, error)
- Reusable components in all configurations
- Dark mode vs. light mode
- Dynamic type at standard and accessibility sizes
Medium Value (Selectively Snapshot)
- Navigation flows (each step)
- Onboarding screens
- Paywall/subscription views
- Settings screens
Low Value (Skip)
- Views that are 100% system components (plain List, NavigationStack)
- Views that change frequently during active development
- Views dependent on live data
CI Integration
GitHub Actions
- name: Run Snapshot Tests
run: |
xcodebuild test \
-scheme YourApp \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.0' \
-only-testing "YourAppTests/Snapshots" \
-resultBundlePath TestResults.xcresult
- name: Upload Failed Snapshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: failed-snapshots
path: "**/Failures/**"
Xcode Cloud
# ci_scripts/ci_post_xcodebuild.sh
if [ "$CI_XCODEBUILD_ACTION" = "test" ]; then
# Upload snapshot failures as artifacts
if [ -d "$CI_DERIVED_DATA_PATH" ]; then
find "$CI_DERIVED_DATA_PATH" -name "Failures" -type d \
-exec cp -r {} "$CI_RESULT_BUNDLE_PATH/" \;
fi
fi
Output Format
## Snapshot Tests Setup
### Dependency Added
swift-snapshot-testing 1.17.0 via SPM
### Tests Generated
| View | Configurations | Tests |
|------|---------------|-------|
| HomeView | light, dark, empty, XXL type | 4 |
| SettingsView | light, dark | 2 |
| ItemCard | default, selected, long title | 3 |
| **Total** | | **9** |
### Files Created
- `Tests/SnapshotTests/HomeViewSnapshotTests.swift`
- `Tests/SnapshotTests/SettingsViewSnapshotTests.swift`
- `Tests/SnapshotTests/ItemCardSnapshotTests.swift`
### Next Steps
1. Run tests once to record reference images
2. Commit `__Snapshots__/` directories to git
3. Add snapshot test step to CI pipeline
References
- swift-snapshot-testing
generators/test-generator/— for unit/integration test generationtesting/tdd-feature/— for TDD workflow with UI features
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
legal
Legal document generation and compliance guidance for indie Apple developers. Covers privacy policies, terms of service, EULAs, GDPR/CCPA/DPDP compliance, and Apple App Store legal requirements. Use when user needs legal documents or compliance guidance.
privacy-policy
Generate privacy policies, terms of service, and EULAs for Apple platform apps. Detects data collection patterns, third-party SDKs, and generates region-specific legal documents with Apple Privacy Nutrition Label mapping. Use when user needs legal documents or data collection disclosure for App Store submission.
ios-development
Comprehensive iOS development guidance including Swift best practices, SwiftUI patterns, UI/UX review against HIG, and app planning. Use for iOS code review, best practices, accessibility audits, or planning new iOS apps.
assistive-access
Assistive Access implementation for cognitive accessibility including simplified scenes, navigation icons, runtime detection, and design principles. Use when optimizing apps for Assistive Access mode.
ui-review
Review SwiftUI code for iOS/watchOS Human Interface Guidelines compliance, font usage, Dynamic Type support, and accessibility. Use when user mentions UI review, HIG, accessibility audit, font checks, or wants to verify interface design against Apple standards.
navigation-patterns
SwiftUI navigation architecture patterns including NavigationStack, NavigationSplitView, TabView, programmatic navigation, and custom transitions. Use when reviewing or building navigation, fixing navigation bugs, or architecting app flow.
Didn't find tool you were looking for?