Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate sample data and a multi-variant
.claude/skills/rshankras-preview-data-generator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 87% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 30% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 20% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -4% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 57% | 0% |
Generates two tightly-coupled things for a SwiftUI view:
#Preview matrix — the variant blocks you'd otherwise hand-write to prototype and QA a view across light/dark, Dynamic Type, locale/RTL, device sizes, and data states.This is the design-time counterpart to testing/test-data-factory (which makes fixtures for the test suite). Where a factory already exists, this skill reuses Model.fixture() instead of inventing parallel data.
Use this skill when the user:
@Model and needs an in-memory seeded container for previewsPreviewModifierJust need fixtures for unit tests? Use testing/test-data-factory instead. Want screenshot regression tests? Pair this with testing/snapshot-test-setup — the same data + variant matrix feeds snapshot tests.
Load both before generating:
| File | Purpose | |------|---------| | preview-data-patterns.md | Sample-data design, the edge-case catalog, SwiftData in-memory seeding, PreviewModifier (iOS 18), @Previewable, reusing test-data-factory | | preview-matrix.md | The variant axes, preview traits:, .environment overrides, deployment-target fallbacks (#Preview vs PreviewProvider), data-state previews |
Generators are context-aware. Before writing code, detect:
| Check | How | Why it matters | |-------|-----|----------------| | Deployment target | Read project/.xcodeproj or Package.swift | iOS 17+ → #Preview macro; iOS 18+ → PreviewModifier + @Previewable; below 17 → PreviewProvider fallback | | Target view + its models | Read the view file; Grep its init/properties for model types | Determines which types need sample data | | Existing fixtures | Grep "static func fixture\|extension .*{ static (let\|var) preview" | Reuse Model.fixture() / existing .preview — never duplicate | | SwiftData | Grep "@Model" on the model types | Use the in-memory .modelContainer(inMemory:) seed pattern, not plain structs | | View shape | Does it take a model, a ViewModel, or fetch its own data? | Drives whether to inject data, a mock VM, or a seeded container | | Platform | iOS / macOS / multiplatform | Device variants and some traits differ |
Ask via AskUserQuestion only what you can't infer — e.g. "Which states matter for this view: empty, loading, error, loaded, or all four?"
For each model the view needs, generate a Model.preview namespace with the realistic case and the edge cases (see preview-data-patterns.md for the full catalog):
swiftextension Article { /// A typical, realistic instance for the canvas. static var preview: Article { Article(id: UUID(), title: "Designing for the Smallest Screen", author: "Mei Chen", body: String(repeating: "Lorem ipsum. ", count: 40), imageURL: URL(string: "https://picsum.photos/seed/1/600/400"), readMinutes: 6, isBookmarked: false) } /// Edge cases that expose layout bugs. static var previewLongTitle: Article { .preview.with(title: "An Extraordinarily, Almost Unreasonably Long Headline That Wraps") } static var previewNoImage: Article { .preview.with(imageURL: nil) } static var previewList: [Article] { (1...12).map { .preview.with(id: UUID(), title: "Article \($0)") } } static var previewEmpty: [Article] { [] } }
If Article.fixture() already exists (from test-data-factory), build .preview on top of it rather than re-specifying every field.
Generate the #Preview blocks for the axes that matter (see preview-matrix.md). Always include data states — that's the UI-prototyping payoff:
swift#Preview("Loaded") { ArticleListView(state: .loaded(Article.previewList)) } #Preview("Empty") { ArticleListView(state: .empty) } #Preview("Loading") { ArticleListView(state: .loading) } #Preview("Error") { ArticleListView(state: .error("No connection")) } #Preview("Dark") { ArticleListView(state: .loaded(Article.previewList)).preferredColorScheme(.dark) } #Preview("XXL Text") { ArticleListView(state: .loaded(Article.previewList)).dynamicTypeSize(.accessibility3) } #Preview("German / RTL", traits: .sizeThatFitsLayout) { ArticleDetailView(article: .previewLongTitle).environment(\.locale, .init(identifier: "de")) }
Scale the matrix to the request — don't emit 20 previews for a label. A reasonable default per view: the relevant data states + dark mode + one large Dynamic Type + (if the view has text that localizes) one long-language/RTL check.
previewContainer seeded with sample models, attached via .modelContainer(previewContainer).PreviewModifier so the seeded container/data is built once and cached across every preview, applied with #Preview(traits: .modifier(SampleData())).@State/@Bindable → use @Previewable so state lives directly in the #Preview body.Article+Preview.swift (one file per model, in the model's group).ArticleListView+Previews.swift for large matrices. Ask if unsure.#if DEBUG so it never ships in release builds.After generating, always provide:
Article+Preview.swift, PreviewSupport.swift, etc.)#Preview blocks went, how to open the canvas#Preview / PreviewModifier / PreviewProvider)snapshot-test-setup✅ Generated previews for ArticleListView (iOS 18 target)
Files created:
• Article+Preview.swift — .preview, .previewLongTitle, .previewNoImage, .previewList, .previewEmpty
• PreviewSupport.swift — SampleData: PreviewModifier (cached in-memory container)
• ArticleListView+Previews.swift — 7 #Preview blocks (4 states, dark, XXL text, German)
Integration:
• Reused Article.fixture() from test-data-factory for base fields
• Open ArticleListView+Previews.swift → canvas shows all 7 variants
API used: #Preview + PreviewModifier (.modifier) — shared container built once, cached.testing/test-data-factorytesting/snapshot-test-setup (feed it this data)#Preview {} is enoughgenerators/localization-setup already covers locale preview helpers; reuse themgenerators/persistence-setup → defines @Model types
↓
testing/test-data-factory → Model.fixture() for tests
↓
generators/preview-data-generator (THIS SKILL)
• reuses .fixture() for canvas data
• builds the #Preview state/appearance matrix
↓
testing/snapshot-test-setup → renders the same matrix for regression.fixture()/.preview reused, not duplicated#Preview matrix covering the view's data states + relevant appearance axes#Preview / PreviewModifier / PreviewProvider)#if DEBUGGenerate the data and the variants together. A preview is only as useful as the data in it — and a view is only proven when you've seen it empty, overflowing, in the dark, and at AX5.
Other measured skills in the registry, with their headline benchmark lift.