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 Controls Forms?
name: audit-swiftui-controls-forms description: Audit macOS SwiftUI controls forms 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 Controls & Forms
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 iOS-trained control habits read as non-native on a
Mac: an ungrouped Form, a custom view that drops out of the Tab order, an icon-only button with no
tooltip, the wrong listStyle/buttonStyle/pickerStyle, oversized controlSize density, a
.pickerStyle(.wheel) that has no macOS arm (compile error), and a .controlSize(.extraLarge) that
silently resolves to .large. 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 control generator.
The Mac is pointer-and-keyboard-driven, not touch. The training corpus is overwhelmingly iOS, where a
Form already looks grouped, there is no Tab-key focus ring, and control density is fixed β so AI never
learns to ask for .formStyle(.grouped), .focusable(), .help, or a compact controlSize. The result
compiles and looks plausible on iOS but reads as "an iPad app in a window" on macOS. Be suspicious wherever
AI built a settings pane, a custom focus-taking control, or a styled List/Button/Picker.
Boundary / seam note (stay in lane)
Seam verdicts are the reconciled truth in <swiftui-plugin-root>/references/_shared/cross-ref-graph.md
β apply them in the tells and emit cross_ref on a shared-seam finding; do not double-own.
- The pointer/gesture HALF is not mine.
.onHovercursor affordances,pointerStyle/onContinuousHovercursor shape, right-click.contextMenu, and drag gestures all belong toaudit-swiftui-pointer-gestures. This skill owns the control-styling + keyboard-focus half only. Note a pointer smell in one line andcross_ref: pointer-gesturesβ do not audit it here. - Keyboard focus vs VoiceOver focus.
@FocusState/.focusable()(Tab order, focus ring) is this skill (cf-02).AccessibilityFocusState/.accessibilityFocused(VoiceOver focus) isaudit-swiftui-accessibilityβ different wrappers;cross_refit, don't claim it. - Icon-only button with no label. The missing
.helptooltip is this skill (cf-03); the missing.accessibilityLabelisaudit-swiftui-accessibility. This is a keep-both seam β file the.helpfinding here,cross_ref: accessibility, and a11y reuses this skill's.helptext for its label. controlSizeis a split axis. The density/style variant (.controlSizeas a control-style tuning, the.extraLargeno-op) is this skill (cf-08).controlSizeas a layout sizing axis isaudit-swiftui-layout-and-tables;cross_refit when the issue is layout sizing, not control density.- Color / material crossover (a control's tint/material choice) belongs to
audit-swiftui-appearance-color.
The five control rules (the judgment core)
- A macOS
Formis grouped..formStyle(.grouped)is the System-Settings idiom; the macOS default is ungrouped and foreign β iOS forms are grouped out of the box, so AI omits it (cf-01). - Custom focus-taking views must join the Tab order.
.focusable()+@FocusState+.focused($_)make a custom control keyboard-reachable and draw the focus ring; nativeTextField/Buttonare already focusable, a hand-rolled control is not (cf-02). - Every icon-only control needs a
.helptooltip (title case) β the standard macOS tooltip on pointer rest, also fed to accessibility; there is no iOS tooltip analog so AI omits it (cf-03). - Pick the Mac style + density explicitly.
.listStyle(.sidebar)for a source list, a.buttonStyle(.bordered)/.borderless, a.pickerStyle(.menu)pop-up, and a.controlSize(.small)/.minifor dense panes β iOS defaults read oversized (cf-04/05/06). - Never reach for a touch-only style on a Mac.
.pickerStyle(.wheel)has no macOS arm (compile error, cf-07);.controlSize(.extraLarge)resolves to.largeon macOS (a no-op, cf-08).
Full βββ
+ the canonical native-settings-pane exemplar: references/forms-and-focus.md and
references/control-styles-density.md.
Defect index (cf-01 β¦ cf-08)
id Β· tell Β· severity Β· fix Β· open reference. Severities: hard-fail (no macOS arm / never-correct),
warning (compiles but non-native), advisory (judgment / density). auto = mechanical single-answer
fix; flag = show the β
, dev applies.
| id | One-line tell | Sev | Fix | Reference |
|---|---|---|---|---|
| cf-01 | Form { β¦ } with no .formStyle(.grouped) in its chain β ungrouped, non-native settings pane |
warning | flag | forms-and-focus.md |
| cf-02 | a custom View with a focus-taking control and no .focusable() / @FocusState β skipped by Tab, no focus ring |
warning | flag | forms-and-focus.md |
| cf-03 | icon-only Button { } label: { Image(systemName:) } with no .help(β¦) β no tooltip |
warning | flag | forms-and-focus.md |
| cf-04 | sidebar List (in a NavigationSplitView) with no .listStyle(.sidebar) β wrong sidebar material |
advisory | flag | control-styles-density.md |
| cf-05 | Button in a dense pane with no .buttonStyle (.bordered/.borderless/.plain/.link) β oversized default |
advisory | flag | control-styles-density.md |
| cf-06 | Picker with no .pickerStyle(.menu) / .segmented β not the Mac pop-up |
advisory | flag | control-styles-density.md |
| cf-07 | .pickerStyle(.wheel) / WheelPickerStyle on a Mac target β NO macOS arm (compile error) |
hard-fail | flag | control-styles-density.md |
| cf-08 | .controlSize(.extraLarge) / ControlSize.extraLarge β resolves to .large on macOS (a no-op) |
advisory | flag | control-styles-density.md |
cf-07 is the only hard-fail; cf-02 and cf-03 cross-ref siblings. WheelPickerStyle is macOS ABSENT in floors-master.md β it is platform-wrong, not under-gated; never wrap it in
#available(macOS β¦), replace it with .menu/.segmented/.inline. A swiftui-ctx lookup WheelPickerStyle
exit 3 corroborates it (no shipping Mac app uses it β confirmed during the build, see VERIFY).
The real API, at a glance
Real (exist on macOS; floors are the reconciled truth in floors-master.md β read, never restate):
formStyle(_:) (.grouped/.columns/.automatic), focusable(_:), focused(_:), @FocusState,
help(_:), listStyle(_:) (.sidebar/.inset/.bordered/.plain), buttonStyle(_:)
(.bordered/.borderless/.plain/.link/.accessoryBar/.accessoryBarAction/.borderedProminent), pickerStyle(_:)
(.menu/.segmented/.inline/.radioGroup), controlSize(_:) (.mini/.small/.regular/.large).
Glass.interactive / .buttonStyle(.glass) are macOS 26.0+ and owned by audit-swiftui-liquid-glass;
note in one line and cross_ref, don't gate them here.
Platform-wrong (never a Mac API): .pickerStyle(.wheel) / WheelPickerStyle (macOS ABSENT β
compile error on non-Catalyst macOS; cf-07). No-op trap: ControlSize.extraLarge exists (macOS 14.0+)
but resolves to .large on macOS β the practical case list is .mini/.small/.regular/.large (cf-08).
No invented names are central to this domain; if audited code reaches for a control/style symbol you can't
place, confirm via swiftui-ctx (lookup exit 3 = likely hallucination or no-macOS-arm) + Sosumi before
flagging, and cross-check the canonical invented-name list in
<swiftui-plugin-root>/references/_shared/hallucination-blacklist.md. Signatures + full βββ
:
references/forms-and-focus.md, references/control-styles-density.md.
The 8-step audit workflow (execute verbatim)
- ORIENT.
tree/findthe SwiftUI sources. Read the deployment target (project.pbxprojMACOSX_DEPLOYMENT_TARGET, orPackage.swiftplatforms:) β it sets which floor a fix may rely on (focusable/focused/@FocusState= macOS 12.0+,help= macOS 11.0+,formStyle= macOS 13.0+,controlSize= macOS 10.15+;accessoryBar*button styles = macOS 14.0+). A fix that uses a floor above the target needs a#available(macOS NN, *)gate. Record it. - LOCATE. Run the shared hybrid lint runner:
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-controls-forms --dir <sources> --json /tmp/cf.json --sarif /tmp/cf.sarif. It runs this skill's tier-1 grep tells (lint/grep-tells.tsv, cf-01β¦cf-08) + tier-2 structural ast-grep rules (lint/ast-grep/*.ymlβ cf-01 Form-without-formStyle, cf-02 custom-view-not-focusable), plus a per-file parse probe, and emits unified JSON + SARIF. 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 β never treat a hit as a finding. Engine + rule-file format + degradation:<swiftui-plugin-root>/references/_shared/lint-architecture.md. - READ. Open every located file in full β never pattern-match-and-patch blind. Whether a
Form's chain carries.formStyle, whether a customViewactually takes focus (vs a label-only view), whether aButtonis genuinely icon-only, whether aListis the sidebar column of aNavigationSplitView, and whether a pane is genuinely dense are all invisible to grep. Build a per-file inventory: eachForm+ its style; each custom interactiveView+ its focus wiring; each icon-onlyButton+ its.help; eachList/Button/Picker+ its style +controlSize. - DETECT. Apply the index. Assign each candidate a confidence; report a finding only at 100%
certainty (e.g. a
Formwhose chain carries no.formStyle, a.pickerStyle(.wheel)on a Mac target, a.controlSize(.extraLarge)). A native control that is already focusable (TextField/Button), a non-settingsForm, or a glanceableListof plain strings is not a defect β judge it. - VERIFY. For anything β€ ~70% confidence (a style case you can't place, a floor you're unsure of, the
canonical shape, whether a picker style exists on macOS), run both evidence sources. (a) Practice
β
bash <swiftui-plugin-root>/scripts/swiftui-ctx lookup <api> --json(andswiftui-ctx deprecated <api>for a currency/deprecation rule): read itsconsensus(the canonical shape),deprecated+replacement,recommendedpermalink,introduced_macos, andco_occurs_with; alookupexit 3 (not-found, with a did-you-meansuggestion) corroborates a hallucination or a no-macOS-arm symbol β no shipping Mac app uses it (this is exactly how cf-07WheelPickerStylewas confirmed). (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 β note the corpus reportsfocusableat 10.15 butfloors-master.mdcorrects it to macOS 12.0; the reconciled floor wins. The CLI contract is<swiftui-plugin-root>/references/_shared/swiftui-ctx-reference.md. Promote with the citation or discard. Deeper corpus evidence (settings/Form vocab): to judge whether aFormis a genuine settings pane and which controls the native idiom uses, ground it in the corpus βbash <swiftui-plugin-root>/scripts/swiftui-ctx recipe settings-formfor the canonicalForm { Section { Toggle/Picker/LabeledContent } }.formStyle(.grouped)shape, orbash <swiftui-plugin-root>/scripts/swiftui-ctx settingsfor the real ranked vocabulary (8,579 settings screens across 1,157 repos: Toggle 2,207 Β· Section 1,998 Β· Form 1,681 Β· Picker 1,678) plus permalinked exemplar screens inscreens[]β cite it to defend a cf-01 "this is a settings Form" call or "this Picker/Toggle is the Mac idiom". - 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(none in this domain are auto β every fix is a judgment/structural call, so all areflag-only), 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-26 example fetched withbash <swiftui-plugin-root>/scripts/swiftui-ctx file <recommended.id> --smartwhose GitHub permalink (plus the Sosumidoc:) goes in## Sourceas the canonical example. The cf-01 β is grounded in the liveswiftui-ctx lookup formStyleconsensus (.formStyle(.grouped), 100%) + its recommended macOS-26 Gifski permalink (seereferences/forms-and-focus.md). Leaveflag-onlyfindingsopenwith that β in## Correct. - DOUBLE-CHECK. Re-grep / re-run the lint over each fixed file to confirm the tell no longer matches;
record the evidence in
## Fix applied?. Re-confirm every citation still resolves. If a fix introduced a new tell (e.g. a.focusable()you added under a < macOS 12 target now needs a gate, or aFormyou grouped now wants per-control density), 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. No defect in this domain is auto-fixed: every correct fix is
a structural/judgment call (which view takes focus, which density a pane wants, which picker style fits the
data, whether a Form is a settings pane at all), so all are fix_mode: flag-only. cf-07 is a hard-fail but
still flag-only β the replacement style (.menu vs .segmented vs .inline) depends on the data.
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/controls-forms/<context>/NN-slug.md(one finding per file, zero-padded, ordered). Per-run index:swiftui-audits/controls-forms/_index.md. domain: controls-forms. Frontmatter is the canonical schema;fix_modeisflag-onlyfor every defect.availabilityreads fromfloors-master.md.sourceis an Apple URL + access date (fetched via Sosumi) orverify against Xcode 26 SDK. Emitcross_refon cf-02 (βaccessibilitywhen VoiceOver focus is also at stake), cf-03 (βaccessibility, the keep-both label seam), cf-08 (βlayout-and-tableswhen the issue is layout sizing not density), and any hover/cursor/right-click note (βpointer-gestures).
Starter <context> folders (file here whenβ¦):
<context> |
File a finding here when⦠|
|---|---|
grouped-form/ |
a Form lacks .formStyle(.grouped) and reads as an ungrouped, non-native settings pane (cf-01) |
keyboard-focus/ |
a custom focus-taking View has no .focusable() / @FocusState and drops out of the Tab order (cf-02) β cross_ref accessibility |
tooltips-help/ |
an icon-only control has no .help tooltip (cf-03) β cross_ref accessibility (keep-both label) |
control-style/ |
a List/Button/Picker carries the wrong or default style for Mac density (cf-04, cf-05, cf-06) |
picker-platform/ |
a touch-only .pickerStyle(.wheel) / WheelPickerStyle appears on a Mac target (cf-07) |
control-density/ |
a .controlSize(.extraLarge) no-op, or a dense pane left at default density (cf-08) β cross_ref layout-and-tables when sizing |
New-folder rule: if a finding does not fit any existing context folder, create a new one under
swiftui-audits/controls-forms/ 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/forms-and-focus.md |
the ungrouped Form, custom-view keyboard focus + @FocusState wiring, and the icon-only .help tooltip (cf-01/02/03) + the canonical native-settings-pane exemplar |
references/control-styles-density.md |
listStyle/buttonStyle/pickerStyle choice, the .wheel no-macOS-arm trap, and the controlSize density / .extraLarge no-op (cf-04/05/06/07/08) |
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 cf-01β¦cf-08 + tier-2 structural cf-01 Form-without-formStyle / cf-02 custom-view-not-focusable); 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 (the reconciled truth β focusable/focused/@FocusState 12.0, help 11.0, formStyle 13.0, controlSize 10.15, ControlSize.extraLarge no-op, WheelPickerStyle macOS ABSENT) |
<swiftui-plugin-root>/references/_shared/hallucination-blacklist.md |
the canonical invented-name list (cross-check a made-up style/control symbol) |
<swiftui-plugin-root>/references/_shared/macos-arm-gating.md |
the macOS-arm gating rule + the macOS ABSENT-is-not-a-low-floor trap (cf-07 is replaced, never gated; a macOS-12 focus fix under a lower target needs a gate) |
<swiftui-plugin-root>/references/_shared/finding-schema.md |
the unified finding schema + frontmatter keys + context-folder ownership |
<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 (pointer half, @FocusState-vs-AccessibilityFocusState, .help-vs-label keep-both, controlSize split axis) |
Detection accelerator
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-controls-forms --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, cf-01β¦cf-08) + tier-2 ast-grep
structural rules (lint/ast-grep/*.yml β cf-01 form-without-formstyle, cf-02 custom-view-not-focusable)
that grep cannot express (the absence of .formStyle across a Form closure's chain, the absence of
a .focusable()/@FocusState inside a custom View that uses a focus-taking control β both anchored on a
kind). It runs a per-file parse probe (surfaces "did not fully parse" so a structural miss can't look
clean), emits unified JSON + SARIF, exits 2 on any hard-fail (cf-07) for a CI gate, 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). It only LOCATES β always READ each hit in full before reporting (step 3). The thin
scripts/cf-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 Controls Forms 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-controls-forms ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Score
64 / 100
Good