NOASSERTIONupdated 1mo ago
Let be the absolute plugin directory two levels above this SKILL.md. Resolve that path before running commands or opening shared references. When these instructions say swiftui-ctx, invoke /scripts/swiftui-ctx; do not assume the command is on PATH.
What can you do with Audit Swiftui Previews?
name: audit-swiftui-previews description: Audit macOS SwiftUI previews for correctness, current APIs, and production conventions. Use for that domain or as part of a full audit.
Bundled resource root
Let <swiftui-plugin-root> be the absolute plugin directory two levels above this SKILL.md. Resolve that path before running commands or opening shared references. When these instructions say swiftui-ctx, invoke <swiftui-plugin-root>/scripts/swiftui-ctx; do not assume the command is on PATH.
Audit SwiftUI Previews
AUDIT-ONLY Β· macOS-only Β· SwiftUI-only. Run this on a finished or in-progress macOS SwiftUI
project to detect β and where certain, fix β every way the #Preview macro and the Xcode canvas go
wrong: the legacy PreviewProvider struct, bare @State/@Binding/@Bindable in a #Preview body
without @Previewable, hand-rolled EnvironmentKey boilerplate instead of @Entry, the visionOS-only
Preview(windowStyle:) overload, manual .frame sizing instead of traits, the wrong .environmentObject
injector, and the two canvas-crash traps (a @Query/SwiftData view with no in-memory container, an
@Environment-dependent view with no injected dependency). Findings are written to disk in the toolkit's
unified schema; certain mechanical defects are fixed under the fix-safety protocol. This is never a
from-scratch preview generator.
A preview instantiates the view for real β so a missing container or @Environment dependency traps
the canvas, not the app. The preview tooling is also where AI goes stale: it emits the
PreviewProvider struct (dominant 2019β2023) and forgets the modern macro era's @Previewable / @Entry.
Boundary / seam note (stay in lane)
- SwiftData model design (
@Model, schema, relationships,@ModelActor) belongs toaudit-swiftui-swiftdata. This skill owns the preview-construction angle of the canvas crash (the missing in-memory container in a#Preview); emitcross_ref: audit-swiftui-swiftdataand let it own the model itself. - Live-app
@Observablewiring (where the model lives, the sample factory) belongs toaudit-swiftui-state-observation. This skill owns only the preview injection of a sample@Observable;cross_refit for the factory. @Entry/FocusedValueKeyis a context-conditional seam: if the pattern is co-located with aCommandMenu/CommandGroupβaudit-swiftui-menus-commandsowns it; in a preview / general environment setup β this skill owns it (per the shared cross-ref graph).cross_refthe other way.- The blanket "is every floored API gated" sweep is
audit-swiftui-availability-gating; macro modernity (PreviewProvider-as-deprecated-era) shares a seam withaudit-swiftui-api-currency.
The three non-negotiable preview rules
#Previewmacro, notPreviewProvider. The freestanding macro is the modern path (Xcode 15+, macOS 14+). One#Previewdeclaration per named preview β never the legacystruct β¦_Previews: PreviewProvider { static var previews }for new code.- A
#Previewbody is an expanded view scope β tag dynamic state@Previewable. Bare@State/@Binding/@Bindableat that scope is a compile error;@Previewable @State var β¦is the only legal stateful-preview shape. - Previews run real code β inject every dependency. A
@Query/SwiftData view needs an in-memoryModelContainer; an@Environment(Model.self)view needs a sample injected with.environment(_:). Omit either and the canvas crashes on launch.
The instantiation test: ask "does this view read state or a dependency the preview never provides?"
If yes, the canvas traps β supply it (@Previewable, .modelContainer(β¦ inMemory: true),
.environment(_:)). Full reasoning: references/preview-crashes-and-injection.md.
Defect index (prev-01 β¦ prev-09)
id Β· tell Β· severity Β· fix Β· open reference. Severities: hard-fail (compile error / canvas crash),
warning (compiles but stale/non-idiomatic), advisory (judgment / polish). auto = mechanical
single-answer fix; flag = show the β
, dev applies.
| id | One-line tell | Sev | Fix | Reference |
|---|---|---|---|---|
| prev-01 | struct *_Previews: PreviewProvider (legacy struct for new code) |
warning | flag | preview-macro-and-state.md |
| prev-02 | bare @State/@Binding/@Bindable in a #Preview body, no @Previewable |
hard-fail | flag | preview-macro-and-state.md |
| prev-03 | struct *Key: EnvironmentKey + extension EnvironmentValues boilerplate (use @Entry) |
warning | flag | entry-and-environment.md |
| prev-04 | manual .frame(width:height:) in a #Preview body (use a trait) |
advisory | flag | preview-macro-and-state.md |
| prev-05 | Preview(β¦, windowStyle:) on a Mac target (visionOS-only overload) |
hard-fail | flag | preview-macro-and-state.md |
| prev-06 | #Preview of a @Query/SwiftData view, no .modelContainer(β¦ inMemory: true) |
warning | flag | preview-crashes-and-injection.md |
| prev-07 | #Preview of a view reading @Environment(Model.self), no .environment(_:) injection |
advisory | flag | preview-crashes-and-injection.md |
| prev-08 | .environmentObject(β¦) in a preview whose model is @Observable (wrong injector) |
warning | flag | entry-and-environment.md |
| prev-09 | repeated .modelContainer(for:inMemory:true) + re-seed across many #Previews (use PreviewModifier) |
advisory | flag | preview-modifier-shared.md |
Two claims need a floor check at audit time β confirm in VERIFY, never assert from memory: the
PreviewModifier / .modifier(_:) floor (macOS 15.0+, carried verify-SDK in floors-master.md); and
that @Previewable @Query itself needs macOS 15 (prev-09 fallback path). Both reduce to a gating note,
not a hallucination.
The real API, at a glance
Real (exist on macOS): #Preview / Preview(_:traits:_:body:) macro (macOS 14.0+), @Previewable
(macOS 14.0+), @Entry (macOS 10.15+, back-deploys; Xcode 15+ to expand β practical floor macOS 14),
.fixedLayout(width:height:) / .sizeThatFitsLayout / .defaultLayout traits, PreviewModifier +
.modifier(_:) trait (macOS 15.0+), .modelContainer(for:inMemory:), .environment(_:),
PreviewProvider (legacy but not deprecated β flag as stale-for-new-code, never as invented).
Hallucinated / platform-wrong (never on macOS): Preview(_:windowStyle:traits:body:) is
visionOS-only β there is no windowStyle: #Preview overload on macOS; .environmentObject(_:)
for an @Observable is the wrong injector (it takes only an ObservableObject).
β Correct β grounded in real macOS code (swiftui-ctx consensus)
The modern shape, from swiftui-ctx lookup Preview β file <recommended.id> --smart
(repo utmapp/UTM, 34kβ
) β the freestanding macro, one declaration per named preview, no
PreviewProvider struct:
@available(macOS 13, *)
#Preview {
UTMServerView()
}
- Source (real permalink): https://github.com/utmapp/UTM/blob/e4a4c34b671284263fc69f81b607de494d7e9b65/Platform/macOS/UTMServerView.swift#L170
- Apple doc (Sosumi):
doc:https://sosumi.ai/documentation/swiftui/preview (#Previewmacro,introduced_macos: 10.15β DocC inheritance artifact; authoritative Apple docs badge is macOS 14.0+, same trap asAnimation.bouncyin floors files)
This is the live grounding for prev-01: the canonical #Preview { } that replaces the legacy
struct β¦_Previews: PreviewProvider. At FIX time, re-fetch the per-defect consensus shape
(swiftui-ctx file <recommended.id> --smart) for that defect's exact β
, never a hand-written snippet.
Floor values are the reconciled truth in
<swiftui-plugin-root>/references/_shared/floors-master.md and the canonical invented-name list in
<swiftui-plugin-root>/references/_shared/hallucination-blacklist.md β read, never restate them. The
real macOS-26 shape for any symbol comes from swiftui-ctx lookup <api> (step 5), not memory.
The 8-step audit workflow (execute verbatim)
- ORIENT.
tree/findthe SwiftUI sources. Read the deployment target (project.pbxprojMACOSX_DEPLOYMENT_TARGET, orPackage.swiftplatforms:). It is load-bearing for prev-09 (PreviewModifier/@Previewable @Queryneed macOS 15). Record it. - LOCATE. Run the shared hybrid lint runner:
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-previews --dir <sources> --json /tmp/prev.json --sarif /tmp/prev.sarif. It runs this skill's tier-1 grep tells (lint/grep-tells.tsv) + the one tier-2 structural ast-grep rule (lint/ast-grep/prev-02-*.ymlβ the bare-@State-inside-a-#Preview-body containment rule grep can't express), plus a per-file parse probe, and emits unified JSON + SARIF. (prev-06's un-injected-container case stays a grep co-occurrence tell β a precise structural rule would need cross-declaration data flow ast-grep can't resolve and would false-positive on the correct separate-#Previewinjection; READ the located site to confirm. Seelint-architecture.md.) Read itsparse_warningsβ a flagged file did not fully parse, so a structural miss can't masquerade as clean; READ those by hand. The runner only LOCATES. Engine + rule-file format:<swiftui-plugin-root>/references/_shared/lint-architecture.md. - READ. Open every located file in full β never pattern-match-and-patch blind. Whether a
@Statesits at#Previewbody scope vs inside the previewed view, whether a previewed view reads@Query/@Environment, and whether a container is already injected upstream are all invisible to grep. Build a per-file inventory: each#Preview+ what state it declares + what dependencies the previewed view reads + whether each is provided. - DETECT. Apply the index. Assign each candidate a confidence; report a finding only at 100%
certainty (e.g. a bare
@Stateat#Previewbody scope, awindowStyle:overload on a Mac target, a@Queryview with no injected container). - VERIFY. For anything β€ ~70% confidence (a symbol you're unsure exists, a floor you can't place,
a behavior claim), run both evidence sources. (a) Practice β
bash <swiftui-plugin-root>/scripts/swiftui-ctx lookup <api> --json(andbash <swiftui-plugin-root>/scripts/swiftui-ctx deprecated <api>for a currency/deprecation rule like prev-01): read itsconsensus(the canonical shape),deprecated+replacement,recommendedpermalink,introduced_macos, andco_occurs_with(e.g.modelContainerco-occurs withQuery/Modelβ exactly the prev-06 seam); alookupexit 3 (not-found, with a did-you-meansuggestion) corroborates a hallucination/platform-wrong finding. (b) Spec β confirm via Sosumi:curl -sSL https://sosumi.ai/<apple-path>usingreferences/source-directory.mdfor the path and<swiftui-plugin-root>/references/_shared/sosumi-reference.mdfor the protocol (neverWebFetchdeveloper.apple.com). Cross-checkintroduced_macosagainstfloors-master.mdand the Sosumidoc:floor. The CLI contract is<swiftui-plugin-root>/references/_shared/swiftui-ctx-reference.md. Promote with the citation or discard. Carry thePreviewModifierfloor and@Previewable @QuerymacOS-15 claim as gating notes withsource: verify against Xcode 26 SDKuntil VERIFY confirms them. - REPORT. Write each confirmed finding (output contract below). One finding per file, zero-padded,
ordered. Write the run's
_index.md. - FIX. Apply corrections under the fix-safety protocol
(
<swiftui-plugin-root>/references/_shared/fix-safety-protocol.md): clean-tree gate, findings-first, onlyfix_mode: auto(this domain ships none by default β every fix isflag-only; see below), one conventional commit per finding citing itsrule_id, never weaken a check. The β "Correct" is not a hand-written snippet β it is the swiftui-ctx consensus shape put in## Correct, backed by a real macOS example fetched withbash <swiftui-plugin-root>/scripts/swiftui-ctx file <recommended.id> --smartwhose GitHub permalink (plus the Sosumidoc:) goes in## Source. Leaveflag-onlyfindingsopenwith that β . - DOUBLE-CHECK. Re-grep each fixed file to confirm the tell no longer matches; record the evidence
in
## Fix applied?. Re-confirm every citation still resolves and still says the floor you claimed. If a fix introduced a new tell (e.g. a.environment(_:)you added now needs a sample factory that itself reads a dependency), loop that file back to DETECT.
Confidence gating (load-bearing)
Report a finding only at 100% certainty. Anything β€ ~70% goes to VERIFY (step 5) before it can
become a finding β never emit a speculative finding. Every defect in this domain is
fix_mode: flag-only: the correct preview body depends on the view's real dependencies and sample
data, which only the developer (or the swiftui-ctx consensus shape) can supply β there is no safe
mechanical single-answer rename. Surface every fix as a suggested diff with the β
, never auto-applied.
Output contract
Inherits the toolkit's unified contract (full schema + body sections + frontmatter keys:
<swiftui-plugin-root>/references/_shared/finding-schema.md β do not restate it). Specialized for this
domain:
- Findings:
swiftui-audits/previews/<context>/NN-slug.md(one finding per file, zero-padded, ordered). Per-run index:swiftui-audits/previews/_index.md. domain: previews. Frontmatter is the canonical schema;fix_modeisflag-onlyfor every prev-NN (see confidence gating).availabilityreads fromfloors-master.md.sourceis an Apple URL + access date (fetched via Sosumi) orverify against Xcode 26 SDK. Emitcross_refon the shared-seam findings (prev-06 β swiftdata, prev-07 β state-observation, prev-03 β menus-commands when command-co-located, prev-01 β api-currency).
Starter <context> folders (file here whenβ¦):
<context> |
File a finding here when⦠|
|---|---|
legacy-preview-struct/ |
a PreviewProvider struct stands in for the #Preview macro (prev-01) |
previewable-state/ |
bare @State/@Binding/@Bindable lives at #Preview body scope (prev-02) |
environment-entry/ |
hand-rolled EnvironmentKey boilerplate, or the wrong .environmentObject injector (prev-03, prev-08) |
preview-traits/ |
a manual .frame sizing hack, or the visionOS-only windowStyle: overload (prev-04, prev-05) |
canvas-crash-injection/ |
a @Query/SwiftData or @Environment view ships no injected dependency (prev-06, prev-07) |
preview-modifier/ |
repeated inline in-memory containers should collapse to one PreviewModifier (prev-09) |
New-folder rule: if a finding does not fit any existing context folder, create a new one under
swiftui-audits/previews/ with a lowercase-hyphen slug naming the sub-category, and note it in the
run's _index.md. Prefer an existing folder when the fit is reasonable; consistency across runs is a
hard requirement. Two runs over the same code produce structurally identical trees.
Reference routing
| File | Open when |
|---|---|
references/preview-macro-and-state.md |
the macro-vs-PreviewProvider call, @Previewable state, traits-vs-.frame, the windowStyle: platform trap (prev-01/02/04/05) |
references/entry-and-environment.md |
@Entry-vs-EnvironmentKey boilerplate and the .environment vs .environmentObject injector (prev-03/08) |
references/preview-crashes-and-injection.md |
the two canvas-crash traps β missing in-memory container, missing @Environment injection β and the instantiation test (prev-06/07) |
references/preview-modifier-shared.md |
collapsing repeated inline in-memory containers into one shared PreviewModifier (macOS 15+) (prev-09) |
references/source-directory.md |
step VERIFY β the Apple/WWDC/practitioner source map fetched via Sosumi |
lint/grep-tells.tsv + lint/ast-grep/*.yml |
step LOCATE β this skill's declarative lint rule set fed to the shared runner (tier-1 grep tells + tier-2 structural ast-grep); edit here to tune detection |
Shared toolkit references (point in, never restate):
| Shared file | For |
|---|---|
<swiftui-plugin-root>/references/_shared/floors-master.md |
every floor/availability value (@Previewable, @Entry, #Preview, PreviewModifier, ModelContainer.init) β the reconciled truth |
<swiftui-plugin-root>/references/_shared/hallucination-blacklist.md |
the canonical invented / platform-wrong name list |
<swiftui-plugin-root>/references/_shared/macos-arm-gating.md |
the macOS-arm gating rule (prev-09 floor gates) + the macOS ABSENT (visionOS-only windowStyle:) rule |
<swiftui-plugin-root>/references/_shared/finding-schema.md |
the unified finding schema + frontmatter keys |
<swiftui-plugin-root>/references/_shared/fix-safety-protocol.md |
the 8-point fix-safety protocol (step 7) |
<swiftui-plugin-root>/references/_shared/sosumi-reference.md |
the Apple-doc spec fetch protocol (step 5 VERIFY) |
<swiftui-plugin-root>/references/_shared/swiftui-ctx-reference.md |
the practice-corpus CLI contract β lookup/deprecated/file --smart for the consensus shape + permalinked example (steps 5 VERIFY Β· 7 FIX) |
<swiftui-plugin-root>/references/_shared/cross-ref-graph.md |
seam ownership + cross_ref targets (the previews row + the @Entry/FocusedValueKey and preview-container tiebreakers) |
Detection accelerator
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-previews --dir <files-or-dir> [--json out.json] [--sarif out.sarif] β the toolkit's one shared hybrid lint engine, fed this
skill's declarative rules: tier-1 grep tells (lint/grep-tells.tsv,
prev-01/03/04/05/06/07/08/09 flat presence) + one tier-2 ast-grep structural rule
(lint/ast-grep/prev-02-*.yml β bare-@State-inside-a-#Preview-body containment, which grep cannot
express). prev-06's un-injected @Query view is a grep co-occurrence tell, not a structural rule (a
precise one needs cross-declaration data flow ast-grep can't resolve β lint-architecture.md). It runs a per-file
parse probe (surfaces "did not fully parse" so a structural miss can't look clean), emits unified
JSON + SARIF, and degrades to grep-only with a notice if ast-grep is unreachable
(npx --package @ast-grep/cli ast-grep; faster: brew install ast-grep). This domain ships no
tier-1 hard tell, so the runner does not exit 2 β it emits warnings/advisories only (no hard-fail
tells β nothing blocks the gate). The located traps (prev-02/05/06/07) are still confirmed by
the LLM in DETECT after READ, never by a flat grep. It only LOCATES β always READ each hit in full
before reporting (step 3). The thin scripts/preview-lint.sh is a pointer to this runner. Engine +
rule-file format + JSON/SARIF shape + safety rails:
<swiftui-plugin-root>/references/_shared/lint-architecture.md.
Install
Add Audit Swiftui Previews to your client. Pick the one you use.
npx skills add yigitkonur/plugin-swiftuiInstalls every skill in the repository, then prompts for which to keep.
/plugin marketplace add yigitkonur/plugin-swiftuiAdds the repository as a plugin marketplace; install individual plugins with `/plugin install`.
git clone https://github.com/yigitkonur/plugin-swiftui
cp -r plugins/swiftui/skills/audit-swiftui-previews ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Score
80 / 100
Excellent