Why This Matters

Screenshot generation is one of those tasks teams postpone until release week, then panic because one tiny UI change breaks ten marketing images. A better pattern is to treat screenshots as a deterministic build artifact.

Today we are going to build a practical screenshot pipeline for SwiftUI apps that removes manual repeat work.

Step 1: Define scenes as data, not one-off scripts

Think of scenes like camera presets in a studio. You should be able to replay them in any order.

struct ShotScene {
    let id: String
    let route: String
    let locale: String
    let colorScheme: ColorScheme
}

let scenes: [ShotScene] = [
    .init(id: "home-light-en", route: "/home", locale: "en", colorScheme: .light),
    .init(id: "home-dark-en", route: "/home", locale: "en", colorScheme: .dark),
    .init(id: "wordbook-ja", route: "/wordbook", locale: "ja", colorScheme: .light)
]

Step 2: Force deterministic UI state before rendering

If data is random at render time, your screenshots are random too. Seed your demo data and freeze time-sensitive UI where possible.

@MainActor
func makePreviewStore() -> AppStore {
    let store = AppStore()
    store.session.userName = "Demo User"
    store.wordbook.items = ["context", "pipeline", "latency"]
    store.stats.streakDays = 14
    return store
}

Step 3: Batch render with explicit destinations

Render commands should be boring and repeatable. A build machine should produce the same output as your laptop.

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

SCHEME="MyApp"
OUT_DIR="./artifacts/screenshots"
mkdir -p "$OUT_DIR"

xcodebuild test \
  -scheme "$SCHEME" \
  -destination 'platform=iOS Simulator,name=iPhone 15' \
  -resultBundlePath "$OUT_DIR/result-bundle"

Step 4: Add a visual regression check

Store known-good images and compare them with the current run. Fail fast when layout drifts.

func hasUnexpectedDrift(_ baseline: UIImage, _ candidate: UIImage, tolerance: Double) -> Bool {
    let diffRatio = PixelDiff.ratio(baseline, candidate)
    return diffRatio > tolerance
}

Pitfalls People Hit First

  • Mixing production API calls into screenshot test flows.
  • Using user-generated content that changes each run.
  • Saving images with non-deterministic filenames.

Verification Checklist

  • Two consecutive runs produce identical files for the same commit.
  • Locale and dark-mode shots are both generated every run.
  • Regression check fails when text wraps unexpectedly.

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.