opencode-skills-antigravity 1.0.12 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/app-store-changelog/SKILL.md +75 -0
- package/bundled-skills/app-store-changelog/agents/openai.yaml +4 -0
- package/bundled-skills/app-store-changelog/references/release-notes-guidelines.md +34 -0
- package/bundled-skills/app-store-changelog/scripts/collect_release_changes.sh +33 -0
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/sources/sources.md +10 -0
- package/bundled-skills/docs/users/bundles.md +9 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/faq.md +36 -0
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +14 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/github/SKILL.md +76 -0
- package/bundled-skills/github/agents/openai.yaml +4 -0
- package/bundled-skills/ios-debugger-agent/SKILL.md +59 -0
- package/bundled-skills/ios-debugger-agent/agents/openai.yaml +4 -0
- package/bundled-skills/macos-menubar-tuist-app/SKILL.md +109 -0
- package/bundled-skills/macos-menubar-tuist-app/agents/openai.yaml +4 -0
- package/bundled-skills/macos-spm-app-packaging/SKILL.md +105 -0
- package/bundled-skills/macos-spm-app-packaging/agents/openai.yaml +4 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/Package.swift +17 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/Resources/.keep +0 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/main.swift +11 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/version.env +2 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/build_icon.sh +49 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/compile_and_run.sh +63 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/launch.sh +28 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/make_appcast.sh +82 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/package_app.sh +206 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +52 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +52 -0
- package/bundled-skills/macos-spm-app-packaging/assets/templates/version.env +2 -0
- package/bundled-skills/macos-spm-app-packaging/references/packaging.md +17 -0
- package/bundled-skills/macos-spm-app-packaging/references/release.md +32 -0
- package/bundled-skills/macos-spm-app-packaging/references/scaffold.md +79 -0
- package/bundled-skills/orchestrate-batch-refactor/SKILL.md +97 -0
- package/bundled-skills/orchestrate-batch-refactor/agents/openai.yaml +4 -0
- package/bundled-skills/orchestrate-batch-refactor/references/agent-prompt-templates.md +53 -0
- package/bundled-skills/orchestrate-batch-refactor/references/work-packet-template.md +31 -0
- package/bundled-skills/project-skill-audit/SKILL.md +190 -0
- package/bundled-skills/project-skill-audit/agents/openai.yaml +4 -0
- package/bundled-skills/react-component-performance/SKILL.md +135 -0
- package/bundled-skills/react-component-performance/agents/openai.yaml +4 -0
- package/bundled-skills/react-component-performance/references/examples.md +88 -0
- package/bundled-skills/simplify-code/SKILL.md +179 -0
- package/bundled-skills/snowflake-development/SKILL.md +233 -0
- package/bundled-skills/swift-concurrency-expert/SKILL.md +113 -0
- package/bundled-skills/swift-concurrency-expert/agents/openai.yaml +4 -0
- package/bundled-skills/swift-concurrency-expert/references/approachable-concurrency.md +63 -0
- package/bundled-skills/swift-concurrency-expert/references/swift-6-2-concurrency.md +272 -0
- package/bundled-skills/swift-concurrency-expert/references/swiftui-concurrency-tour-wwdc.md +33 -0
- package/bundled-skills/swiftui-liquid-glass/SKILL.md +98 -0
- package/bundled-skills/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/bundled-skills/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/bundled-skills/swiftui-performance-audit/SKILL.md +114 -0
- package/bundled-skills/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/bundled-skills/swiftui-performance-audit/references/code-smells.md +150 -0
- package/bundled-skills/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/bundled-skills/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/bundled-skills/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/bundled-skills/swiftui-performance-audit/references/report-template.md +47 -0
- package/bundled-skills/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/bundled-skills/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/bundled-skills/swiftui-ui-patterns/SKILL.md +103 -0
- package/bundled-skills/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/bundled-skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/bundled-skills/swiftui-ui-patterns/references/async-state.md +96 -0
- package/bundled-skills/swiftui-ui-patterns/references/components-index.md +50 -0
- package/bundled-skills/swiftui-ui-patterns/references/controls.md +57 -0
- package/bundled-skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/bundled-skills/swiftui-ui-patterns/references/focus.md +90 -0
- package/bundled-skills/swiftui-ui-patterns/references/form.md +97 -0
- package/bundled-skills/swiftui-ui-patterns/references/grids.md +71 -0
- package/bundled-skills/swiftui-ui-patterns/references/haptics.md +71 -0
- package/bundled-skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/bundled-skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/bundled-skills/swiftui-ui-patterns/references/list.md +86 -0
- package/bundled-skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/bundled-skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/bundled-skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/bundled-skills/swiftui-ui-patterns/references/media.md +73 -0
- package/bundled-skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/bundled-skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/bundled-skills/swiftui-ui-patterns/references/overlay.md +45 -0
- package/bundled-skills/swiftui-ui-patterns/references/performance.md +62 -0
- package/bundled-skills/swiftui-ui-patterns/references/previews.md +48 -0
- package/bundled-skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/bundled-skills/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/bundled-skills/swiftui-ui-patterns/references/searchable.md +71 -0
- package/bundled-skills/swiftui-ui-patterns/references/sheets.md +155 -0
- package/bundled-skills/swiftui-ui-patterns/references/split-views.md +72 -0
- package/bundled-skills/swiftui-ui-patterns/references/tabview.md +114 -0
- package/bundled-skills/swiftui-ui-patterns/references/theming.md +71 -0
- package/bundled-skills/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/bundled-skills/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/bundled-skills/swiftui-view-refactor/SKILL.md +210 -0
- package/bundled-skills/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/bundled-skills/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/bundled-skills/wordpress/SKILL.md +281 -4
- package/bundled-skills/wordpress-penetration-testing/SKILL.md +106 -1
- package/bundled-skills/wordpress-plugin-development/SKILL.md +296 -3
- package/bundled-skills/wordpress-theme-development/SKILL.md +316 -3
- package/bundled-skills/wordpress-woocommerce-development/SKILL.md +442 -2
- package/package.json +1 -1
package/bundled-skills/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Demystify SwiftUI Performance (WWDC23) (Summary)
|
|
2
|
+
|
|
3
|
+
Context: WWDC23 session on building a mental model for SwiftUI performance and triaging hangs/hitches.
|
|
4
|
+
|
|
5
|
+
## Performance loop
|
|
6
|
+
|
|
7
|
+
- Measure -> Identify -> Optimize -> Re-measure.
|
|
8
|
+
- Focus on concrete symptoms (slow navigation, broken animations, spinning cursor).
|
|
9
|
+
|
|
10
|
+
## Dependencies and updates
|
|
11
|
+
|
|
12
|
+
- Views form a dependency graph; dynamic properties are a frequent source of updates.
|
|
13
|
+
- Use `Self._printChanges()` in debug only to inspect extra dependencies.
|
|
14
|
+
- Eliminate unnecessary dependencies by extracting views or narrowing state.
|
|
15
|
+
- Consider `@Observable` for more granular property tracking.
|
|
16
|
+
|
|
17
|
+
## Common causes of slow updates
|
|
18
|
+
|
|
19
|
+
- Expensive view bodies (string interpolation, filtering, formatting).
|
|
20
|
+
- Dynamic property instantiation and state initialization in `body`.
|
|
21
|
+
- Slow identity resolution in lists/tables.
|
|
22
|
+
- Hidden work: bundle lookups, heap allocations, repeated string construction.
|
|
23
|
+
|
|
24
|
+
## Avoid slow initialization in view bodies
|
|
25
|
+
|
|
26
|
+
- Don’t create heavy models synchronously in view bodies.
|
|
27
|
+
- Use `.task` to fetch async data and keep `init` lightweight.
|
|
28
|
+
|
|
29
|
+
## Lists and tables identity rules
|
|
30
|
+
|
|
31
|
+
- Stable identity is critical for performance and animation.
|
|
32
|
+
- Ensure a constant number of views per element in `ForEach`.
|
|
33
|
+
- Avoid inline filtering in `ForEach`; pre-filter and cache collections.
|
|
34
|
+
- Avoid `AnyView` in list rows; it hides identity and increases cost.
|
|
35
|
+
- Flatten nested `ForEach` when possible to reduce overhead.
|
|
36
|
+
|
|
37
|
+
## Table specifics
|
|
38
|
+
|
|
39
|
+
- `TableRow` resolves to a single row; row count must be constant.
|
|
40
|
+
- Prefer the streamlined `Table` initializer to enforce constant rows.
|
|
41
|
+
- Use explicit IDs for back deployment when needed.
|
|
42
|
+
|
|
43
|
+
## Debugging aids
|
|
44
|
+
|
|
45
|
+
- Use Instruments for hangs and hitches.
|
|
46
|
+
- Use `_printChanges` to validate dependency assumptions during debug.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Optimizing SwiftUI Performance with Instruments (Summary)
|
|
2
|
+
|
|
3
|
+
Context: WWDC session introducing the next-generation SwiftUI Instrument in Instruments 26 and how to diagnose SwiftUI-specific bottlenecks.
|
|
4
|
+
|
|
5
|
+
## Key takeaways
|
|
6
|
+
|
|
7
|
+
- Profile SwiftUI issues with the SwiftUI template (SwiftUI instrument + Time Profiler + Hangs/Hitches).
|
|
8
|
+
- Long view body updates are a common bottleneck; use "Long View Body Updates" to identify slow bodies.
|
|
9
|
+
- Set inspection range on a long update and correlate with Time Profiler to find expensive frames.
|
|
10
|
+
- Keep work out of `body`: move formatting, sorting, image decoding, and other expensive work into cached or precomputed paths.
|
|
11
|
+
- Use Cause & Effect Graph to diagnose *why* updates occur; SwiftUI is declarative, so backtraces are often unhelpful.
|
|
12
|
+
- Avoid broad dependencies that trigger many updates (e.g., `@Observable` arrays or global environment reads).
|
|
13
|
+
- Prefer granular view models and scoped state so only the affected view updates.
|
|
14
|
+
- Environment values update checks still cost time; avoid placing fast-changing values (timers, geometry) in environment.
|
|
15
|
+
- Profile early and often during feature development to catch regressions.
|
|
16
|
+
|
|
17
|
+
## Suggested workflow (condensed)
|
|
18
|
+
|
|
19
|
+
1. Record a trace in Release mode using the SwiftUI template.
|
|
20
|
+
2. Inspect "Long View Body Updates" and "Other Long Updates."
|
|
21
|
+
3. Zoom into a long update, then inspect Time Profiler for hot frames.
|
|
22
|
+
4. Fix slow body work by moving heavy logic into precomputed/cache paths.
|
|
23
|
+
5. Use Cause & Effect Graph to identify unintended update fan-out.
|
|
24
|
+
6. Re-record and compare the update counts and hitch frequency.
|
|
25
|
+
|
|
26
|
+
## Example patterns from the session
|
|
27
|
+
|
|
28
|
+
- Caching formatted distance strings in a location manager instead of computing in `body`.
|
|
29
|
+
- Replacing a dependency on a global favorites array with per-item view models to reduce update fan-out.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Profiling intake and collection checklist
|
|
2
|
+
|
|
3
|
+
## Intent
|
|
4
|
+
|
|
5
|
+
Use this checklist when code review alone cannot explain the SwiftUI performance issue and you need runtime evidence from the user.
|
|
6
|
+
|
|
7
|
+
## Ask for first
|
|
8
|
+
|
|
9
|
+
- Exact symptom: CPU spike, dropped frames, memory growth, hangs, or excessive view updates.
|
|
10
|
+
- Exact interaction: scrolling, typing, initial load, navigation push/pop, animation, sheet presentation, or background refresh.
|
|
11
|
+
- Target device and OS version.
|
|
12
|
+
- Whether the issue was reproduced on a real device or only in Simulator.
|
|
13
|
+
- Build configuration: Debug or Release.
|
|
14
|
+
- Whether the user already has a baseline or before/after comparison.
|
|
15
|
+
|
|
16
|
+
## Default profiling request
|
|
17
|
+
|
|
18
|
+
Ask the user to:
|
|
19
|
+
- Run the app in a Release build when possible.
|
|
20
|
+
- Use the SwiftUI Instruments template.
|
|
21
|
+
- Reproduce the exact problematic interaction only long enough to capture the issue.
|
|
22
|
+
- Capture the SwiftUI timeline and Time Profiler together.
|
|
23
|
+
- Export the trace or provide screenshots of the key SwiftUI lanes and the Time Profiler call tree.
|
|
24
|
+
|
|
25
|
+
## Ask for these artifacts
|
|
26
|
+
|
|
27
|
+
- Trace export or screenshots of the relevant SwiftUI lanes
|
|
28
|
+
- Time Profiler call tree screenshot or export
|
|
29
|
+
- Device/OS/build configuration
|
|
30
|
+
- A short note describing what action was happening at the time of the capture
|
|
31
|
+
- If memory is involved, the memory graph or Allocations data if available
|
|
32
|
+
|
|
33
|
+
## When to ask for more
|
|
34
|
+
|
|
35
|
+
- Ask for a second capture if the first run mixes multiple interactions.
|
|
36
|
+
- Ask for a before/after pair if the user has already tried a fix.
|
|
37
|
+
- Ask for a device capture if the issue only appears in Simulator or if scrolling smoothness matters.
|
|
38
|
+
|
|
39
|
+
## Common traps
|
|
40
|
+
|
|
41
|
+
- Debug builds can distort SwiftUI timing and allocation behavior.
|
|
42
|
+
- Simulator traces can miss device-only rendering or memory issues.
|
|
43
|
+
- Mixed interactions in one capture make attribution harder.
|
|
44
|
+
- Screenshots without the reproduction note are much harder to interpret.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Audit output template
|
|
2
|
+
|
|
3
|
+
## Intent
|
|
4
|
+
|
|
5
|
+
Use this structure when reporting SwiftUI performance findings so the user can quickly see the symptom, evidence, likely cause, and next validation step.
|
|
6
|
+
|
|
7
|
+
## Template
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
[One short paragraph on the most likely bottleneck and whether the conclusion is code-backed or trace-backed.]
|
|
13
|
+
|
|
14
|
+
## Findings
|
|
15
|
+
|
|
16
|
+
1. [Issue title]
|
|
17
|
+
- Symptom: [what the user sees]
|
|
18
|
+
- Likely cause: [root cause]
|
|
19
|
+
- Evidence: [code reference or profiling evidence]
|
|
20
|
+
- Fix: [specific change]
|
|
21
|
+
- Validation: [what to measure after the fix]
|
|
22
|
+
|
|
23
|
+
2. [Issue title]
|
|
24
|
+
- Symptom: ...
|
|
25
|
+
- Likely cause: ...
|
|
26
|
+
- Evidence: ...
|
|
27
|
+
- Fix: ...
|
|
28
|
+
- Validation: ...
|
|
29
|
+
|
|
30
|
+
## Metrics
|
|
31
|
+
|
|
32
|
+
| Metric | Before | After | Notes |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| CPU | [value] | [value] | [note] |
|
|
35
|
+
| Frame drops / hitching | [value] | [value] | [note] |
|
|
36
|
+
| Memory peak | [value] | [value] | [note] |
|
|
37
|
+
|
|
38
|
+
## Next step
|
|
39
|
+
|
|
40
|
+
[One concrete next action: apply a fix, capture a better trace, or validate on device.]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Notes
|
|
44
|
+
|
|
45
|
+
- Order findings by impact, not by file order.
|
|
46
|
+
- Say explicitly when a conclusion is still a hypothesis.
|
|
47
|
+
- If no metrics are available, omit the table and say what should be measured next.
|
package/bundled-skills/swiftui-performance-audit/references/understanding-hangs-in-your-app.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Understanding Hangs in Your App (Summary)
|
|
2
|
+
|
|
3
|
+
Context: Apple guidance on identifying hangs caused by long-running main-thread work and understanding the main run loop.
|
|
4
|
+
|
|
5
|
+
## Key concepts
|
|
6
|
+
|
|
7
|
+
- A hang is a noticeable delay in a discrete interaction (typically >100 ms).
|
|
8
|
+
- Hangs almost always come from long-running work on the main thread.
|
|
9
|
+
- The main run loop processes UI events, timers, and main-queue work sequentially.
|
|
10
|
+
|
|
11
|
+
## Main-thread work stages
|
|
12
|
+
|
|
13
|
+
- Event delivery to the correct view/handler.
|
|
14
|
+
- Your code: state updates, data fetch, UI changes.
|
|
15
|
+
- Core Animation commit to the render server.
|
|
16
|
+
|
|
17
|
+
## Why the main run loop matters
|
|
18
|
+
|
|
19
|
+
- Only the main thread can update UI safely.
|
|
20
|
+
- The run loop is the foundation that executes main-queue work.
|
|
21
|
+
- If the run loop is busy, it can’t handle new events; this causes hangs.
|
|
22
|
+
|
|
23
|
+
## Diagnosing hangs
|
|
24
|
+
|
|
25
|
+
- Observe the main run loop’s busy periods: healthy loops sleep most of the time.
|
|
26
|
+
- Hang detection typically flags busy periods >250 ms.
|
|
27
|
+
- The Hangs instrument can be configured to lower thresholds.
|
|
28
|
+
|
|
29
|
+
## Practical takeaways
|
|
30
|
+
|
|
31
|
+
- Keep main-thread work short; offload heavy work from event handlers.
|
|
32
|
+
- Avoid long-running tasks on the main dispatch queue or main actor.
|
|
33
|
+
- Use run loop behavior as a proxy for user-perceived responsiveness.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Understanding and Improving SwiftUI Performance (Summary)
|
|
2
|
+
|
|
3
|
+
Context: Apple guidance on diagnosing SwiftUI performance with Instruments and applying design patterns to reduce long or frequent updates.
|
|
4
|
+
|
|
5
|
+
## Core concepts
|
|
6
|
+
|
|
7
|
+
- SwiftUI is declarative; view updates are driven by state, environment, and observable data dependencies.
|
|
8
|
+
- View bodies must compute quickly to meet frame deadlines; slow or frequent updates lead to hitches.
|
|
9
|
+
- Instruments is the primary tool to find long-running updates and excessive update frequency.
|
|
10
|
+
|
|
11
|
+
## Instruments workflow
|
|
12
|
+
|
|
13
|
+
1. Profile via Product > Profile.
|
|
14
|
+
2. Choose the SwiftUI template and record.
|
|
15
|
+
3. Exercise the target interaction.
|
|
16
|
+
4. Stop recording and inspect the SwiftUI track + Time Profiler.
|
|
17
|
+
|
|
18
|
+
## SwiftUI timeline lanes
|
|
19
|
+
|
|
20
|
+
- Update Groups: overview of time SwiftUI spends calculating updates.
|
|
21
|
+
- Long View Body Updates: orange >500us, red >1000us.
|
|
22
|
+
- Long Platform View Updates: AppKit/UIKit hosting in SwiftUI.
|
|
23
|
+
- Other Long Updates: geometry/text/layout and other SwiftUI work.
|
|
24
|
+
- Hitches: frame misses where UI wasn’t ready in time.
|
|
25
|
+
|
|
26
|
+
## Diagnose long view body updates
|
|
27
|
+
|
|
28
|
+
- Expand the SwiftUI track; inspect module-specific subtracks.
|
|
29
|
+
- Set Inspection Range and correlate with Time Profiler.
|
|
30
|
+
- Use call tree or flame graph to identify expensive frames.
|
|
31
|
+
- Repeat the update to gather enough samples for analysis.
|
|
32
|
+
- Filter to a specific update (Show Calls Made by `MySwiftUIView.body`).
|
|
33
|
+
|
|
34
|
+
## Diagnose frequent updates
|
|
35
|
+
|
|
36
|
+
- Use Update Groups to find long active groups without long updates.
|
|
37
|
+
- Set inspection range on the group and analyze update counts.
|
|
38
|
+
- Use Cause graph ("Show Causes") to see what triggers updates.
|
|
39
|
+
- Compare causes with expected data flow; prioritize the highest-frequency causes.
|
|
40
|
+
|
|
41
|
+
## Remediation patterns
|
|
42
|
+
|
|
43
|
+
- Move expensive work out of `body` and cache results.
|
|
44
|
+
- Use `Observable()` macro to scope dependencies to properties actually read.
|
|
45
|
+
- Avoid broad dependencies that fan out updates to many views.
|
|
46
|
+
- Reduce layout churn; isolate state-dependent subtrees from layout readers.
|
|
47
|
+
- Avoid storing closures that capture parent state; precompute child views.
|
|
48
|
+
- Gate frequent updates (e.g., geometry changes) by thresholds.
|
|
49
|
+
|
|
50
|
+
## Verification
|
|
51
|
+
|
|
52
|
+
- Re-record after changes to confirm reduced update counts and fewer hitches.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swiftui-ui-patterns
|
|
3
|
+
description: Apply proven SwiftUI UI patterns for navigation, sheets, async state, and reusable screens.
|
|
4
|
+
risk: safe
|
|
5
|
+
source: "Dimillian/Skills (MIT)"
|
|
6
|
+
date_added: "2026-03-25"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# SwiftUI UI Patterns
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- When creating or refactoring SwiftUI screens, flows, or reusable UI components.
|
|
16
|
+
- When you need guidance on navigation, sheets, async state, previews, or component patterns.
|
|
17
|
+
|
|
18
|
+
Choose a track based on your goal:
|
|
19
|
+
|
|
20
|
+
### Existing project
|
|
21
|
+
|
|
22
|
+
- Identify the feature or screen and the primary interaction model (list, detail, editor, settings, tabbed).
|
|
23
|
+
- Find a nearby example in the repo with `rg "TabView\("` or similar, then read the closest SwiftUI view.
|
|
24
|
+
- Apply local conventions: prefer SwiftUI-native state, keep state local when possible, and use environment injection for shared dependencies.
|
|
25
|
+
- Choose the relevant component reference from `references/components-index.md` and follow its guidance.
|
|
26
|
+
- If the interaction reveals secondary content by dragging or scrolling the primary content away, read `references/scroll-reveal.md` before implementing gestures manually.
|
|
27
|
+
- Build the view with small, focused subviews and SwiftUI-native data flow.
|
|
28
|
+
|
|
29
|
+
### New project scaffolding
|
|
30
|
+
|
|
31
|
+
- Start with `references/app-wiring.md` to wire TabView + NavigationStack + sheets.
|
|
32
|
+
- Add a minimal `AppTab` and `RouterPath` based on the provided skeletons.
|
|
33
|
+
- Choose the next component reference based on the UI you need first (TabView, NavigationStack, Sheets).
|
|
34
|
+
- Expand the route and sheet enums as new screens are added.
|
|
35
|
+
|
|
36
|
+
## General rules to follow
|
|
37
|
+
|
|
38
|
+
- Use modern SwiftUI state (`@State`, `@Binding`, `@Observable`, `@Environment`) and avoid unnecessary view models.
|
|
39
|
+
- If the deployment target includes iOS 16 or earlier and cannot use the Observation API introduced in iOS 17, fall back to `ObservableObject` with `@StateObject` for root ownership, `@ObservedObject` for injected observation, and `@EnvironmentObject` only for truly shared app-level state.
|
|
40
|
+
- Prefer composition; keep views small and focused.
|
|
41
|
+
- Use async/await with `.task` and explicit loading/error states. For restart, cancellation, and debouncing guidance, read `references/async-state.md`.
|
|
42
|
+
- Keep shared app services in `@Environment`, but prefer explicit initializer injection for feature-local dependencies and models. For root wiring patterns, read `references/app-wiring.md`.
|
|
43
|
+
- Prefer the newest SwiftUI API that fits the deployment target and call out the minimum OS whenever a pattern depends on it.
|
|
44
|
+
- Maintain existing legacy patterns only when editing legacy files.
|
|
45
|
+
- Follow the project's formatter and style guide.
|
|
46
|
+
- **Sheets**: Prefer `.sheet(item:)` over `.sheet(isPresented:)` when state represents a selected model. Avoid `if let` inside a sheet body. Sheets should own their actions and call `dismiss()` internally instead of forwarding `onCancel`/`onConfirm` closures.
|
|
47
|
+
- **Scroll-driven reveals**: Prefer deriving a normalized progress value from scroll offset and driving the visual state from that single source of truth. Avoid parallel gesture state machines unless scroll alone cannot express the interaction.
|
|
48
|
+
|
|
49
|
+
## State ownership summary
|
|
50
|
+
|
|
51
|
+
Use the narrowest state tool that matches the ownership model:
|
|
52
|
+
|
|
53
|
+
| Scenario | Preferred pattern |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| Local UI state owned by one view | `@State` |
|
|
56
|
+
| Child mutates parent-owned value state | `@Binding` |
|
|
57
|
+
| Root-owned reference model on iOS 17+ | `@State` with an `@Observable` type |
|
|
58
|
+
| Child reads or mutates an injected `@Observable` model on iOS 17+ | Pass it explicitly as a stored property |
|
|
59
|
+
| Shared app service or configuration | `@Environment(Type.self)` |
|
|
60
|
+
| Legacy reference model on iOS 16 and earlier | `@StateObject` at the root, `@ObservedObject` when injected |
|
|
61
|
+
|
|
62
|
+
Choose the ownership location first, then pick the wrapper. Do not introduce a reference model when plain value state is enough.
|
|
63
|
+
|
|
64
|
+
## Cross-cutting references
|
|
65
|
+
|
|
66
|
+
- `references/navigationstack.md`: navigation ownership, per-tab history, and enum routing.
|
|
67
|
+
- `references/sheets.md`: centralized modal presentation and enum-driven sheets.
|
|
68
|
+
- `references/deeplinks.md`: URL handling and routing external links into app destinations.
|
|
69
|
+
- `references/app-wiring.md`: root dependency graph, environment usage, and app shell wiring.
|
|
70
|
+
- `references/async-state.md`: `.task`, `.task(id:)`, cancellation, debouncing, and async UI state.
|
|
71
|
+
- `references/previews.md`: `#Preview`, fixtures, mock environments, and isolated preview setup.
|
|
72
|
+
- `references/performance.md`: stable identity, observation scope, lazy containers, and render-cost guardrails.
|
|
73
|
+
|
|
74
|
+
## Anti-patterns
|
|
75
|
+
|
|
76
|
+
- Giant views that mix layout, business logic, networking, routing, and formatting in one file.
|
|
77
|
+
- Multiple boolean flags for mutually exclusive sheets, alerts, or navigation destinations.
|
|
78
|
+
- Live service calls directly inside `body`-driven code paths instead of view lifecycle hooks or injected models/services.
|
|
79
|
+
- Reaching for `AnyView` to work around type mismatches that should be solved with better composition.
|
|
80
|
+
- Defaulting every shared dependency to `@EnvironmentObject` or a global router without a clear ownership reason.
|
|
81
|
+
|
|
82
|
+
## Workflow for a new SwiftUI view
|
|
83
|
+
|
|
84
|
+
1. Define the view's state, ownership location, and minimum OS assumptions before writing UI code.
|
|
85
|
+
2. Identify which dependencies belong in `@Environment` and which should stay as explicit initializer inputs.
|
|
86
|
+
3. Sketch the view hierarchy, routing model, and presentation points; extract repeated parts into subviews. For complex navigation, read `references/navigationstack.md`, `references/sheets.md`, or `references/deeplinks.md`. **Build and verify no compiler errors before proceeding.**
|
|
87
|
+
4. Implement async loading with `.task` or `.task(id:)`, plus explicit loading and error states when needed. Read `references/async-state.md` when the work depends on changing inputs or cancellation.
|
|
88
|
+
5. Add previews for the primary and secondary states, then add accessibility labels or identifiers when the UI is interactive. Read `references/previews.md` when the view needs fixtures or injected mock dependencies.
|
|
89
|
+
6. Validate with a build: confirm no compiler errors, check that previews render without crashing, ensure state changes propagate correctly, and sanity-check that list identity and observation scope will not cause avoidable re-renders. Read `references/performance.md` if the screen is large, scroll-heavy, or frequently updated. For common SwiftUI compilation errors — missing `@State` annotations, ambiguous `ViewBuilder` closures, or mismatched generic types — resolve them before updating callsites. **If the build fails:** read the error message carefully, fix the identified issue, then rebuild before proceeding to the next step. If a preview crashes, isolate the offending subview, confirm its state initialisation is valid, and re-run the preview before continuing.
|
|
90
|
+
|
|
91
|
+
## Component references
|
|
92
|
+
|
|
93
|
+
Use `references/components-index.md` as the entry point. Each component reference should include:
|
|
94
|
+
- Intent and best-fit scenarios.
|
|
95
|
+
- Minimal usage pattern with local conventions.
|
|
96
|
+
- Pitfalls and performance notes.
|
|
97
|
+
- Paths to existing examples in the current repo.
|
|
98
|
+
|
|
99
|
+
## Adding a new component reference
|
|
100
|
+
|
|
101
|
+
- Create `references/<component>.md`.
|
|
102
|
+
- Keep it short and actionable; link to concrete files in the current repo.
|
|
103
|
+
- Update `references/components-index.md` with the new entry.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# App wiring and dependency graph
|
|
2
|
+
|
|
3
|
+
## Intent
|
|
4
|
+
|
|
5
|
+
Show how to wire the app shell (TabView + NavigationStack + sheets) and install a global dependency graph (environment objects, services, streaming clients, SwiftData ModelContainer) in one place.
|
|
6
|
+
|
|
7
|
+
## Recommended structure
|
|
8
|
+
|
|
9
|
+
1) Root view sets up tabs, per-tab routers, and sheets.
|
|
10
|
+
2) A dedicated view modifier installs global dependencies and lifecycle tasks (auth state, streaming watchers, push tokens, data containers).
|
|
11
|
+
3) Feature views pull only what they need from the environment; feature-specific state stays local.
|
|
12
|
+
|
|
13
|
+
## Dependency selection
|
|
14
|
+
|
|
15
|
+
- Use `@Environment` for app-level services, shared clients, theme/configuration, and values that many descendants genuinely need.
|
|
16
|
+
- Prefer initializer injection for feature-local dependencies and models. Do not move a dependency into the environment just to avoid passing one or two arguments.
|
|
17
|
+
- Keep mutable feature state out of the environment unless it is intentionally shared across broad parts of the app.
|
|
18
|
+
- Use `@EnvironmentObject` only as a legacy fallback or when the project already standardizes on it for a truly shared object.
|
|
19
|
+
|
|
20
|
+
## Root shell example (generic)
|
|
21
|
+
|
|
22
|
+
```swift
|
|
23
|
+
@MainActor
|
|
24
|
+
struct AppView: View {
|
|
25
|
+
@State private var selectedTab: AppTab = .home
|
|
26
|
+
@State private var tabRouter = TabRouter()
|
|
27
|
+
|
|
28
|
+
var body: some View {
|
|
29
|
+
TabView(selection: $selectedTab) {
|
|
30
|
+
ForEach(AppTab.allCases) { tab in
|
|
31
|
+
let router = tabRouter.router(for: tab)
|
|
32
|
+
NavigationStack(path: tabRouter.binding(for: tab)) {
|
|
33
|
+
tab.makeContentView()
|
|
34
|
+
}
|
|
35
|
+
.withSheetDestinations(sheet: Binding(
|
|
36
|
+
get: { router.presentedSheet },
|
|
37
|
+
set: { router.presentedSheet = $0 }
|
|
38
|
+
))
|
|
39
|
+
.environment(router)
|
|
40
|
+
.tabItem { tab.label }
|
|
41
|
+
.tag(tab)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
.withAppDependencyGraph()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Minimal `AppTab` example:
|
|
50
|
+
|
|
51
|
+
```swift
|
|
52
|
+
@MainActor
|
|
53
|
+
enum AppTab: Identifiable, Hashable, CaseIterable {
|
|
54
|
+
case home, notifications, settings
|
|
55
|
+
var id: String { String(describing: self) }
|
|
56
|
+
|
|
57
|
+
@ViewBuilder
|
|
58
|
+
func makeContentView() -> some View {
|
|
59
|
+
switch self {
|
|
60
|
+
case .home: HomeView()
|
|
61
|
+
case .notifications: NotificationsView()
|
|
62
|
+
case .settings: SettingsView()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ViewBuilder
|
|
67
|
+
var label: some View {
|
|
68
|
+
switch self {
|
|
69
|
+
case .home: Label("Home", systemImage: "house")
|
|
70
|
+
case .notifications: Label("Notifications", systemImage: "bell")
|
|
71
|
+
case .settings: Label("Settings", systemImage: "gear")
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Router skeleton:
|
|
78
|
+
|
|
79
|
+
```swift
|
|
80
|
+
@MainActor
|
|
81
|
+
@Observable
|
|
82
|
+
final class RouterPath {
|
|
83
|
+
var path: [Route] = []
|
|
84
|
+
var presentedSheet: SheetDestination?
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
enum Route: Hashable {
|
|
88
|
+
case detail(id: String)
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Dependency graph modifier (generic)
|
|
93
|
+
|
|
94
|
+
Use a single modifier to install environment objects and handle lifecycle hooks when the active account/client changes. This keeps wiring consistent and avoids forgetting a dependency in call sites.
|
|
95
|
+
|
|
96
|
+
```swift
|
|
97
|
+
extension View {
|
|
98
|
+
func withAppDependencyGraph(
|
|
99
|
+
accountManager: AccountManager = .shared,
|
|
100
|
+
currentAccount: CurrentAccount = .shared,
|
|
101
|
+
currentInstance: CurrentInstance = .shared,
|
|
102
|
+
userPreferences: UserPreferences = .shared,
|
|
103
|
+
theme: Theme = .shared,
|
|
104
|
+
watcher: StreamWatcher = .shared,
|
|
105
|
+
pushNotifications: PushNotificationsService = .shared,
|
|
106
|
+
intentService: AppIntentService = .shared,
|
|
107
|
+
quickLook: QuickLook = .shared,
|
|
108
|
+
toastCenter: ToastCenter = .shared,
|
|
109
|
+
namespace: Namespace.ID? = nil,
|
|
110
|
+
isSupporter: Bool = false
|
|
111
|
+
) -> some View {
|
|
112
|
+
environment(accountManager)
|
|
113
|
+
.environment(accountManager.currentClient)
|
|
114
|
+
.environment(quickLook)
|
|
115
|
+
.environment(currentAccount)
|
|
116
|
+
.environment(currentInstance)
|
|
117
|
+
.environment(userPreferences)
|
|
118
|
+
.environment(theme)
|
|
119
|
+
.environment(watcher)
|
|
120
|
+
.environment(pushNotifications)
|
|
121
|
+
.environment(intentService)
|
|
122
|
+
.environment(toastCenter)
|
|
123
|
+
.environment(\.isSupporter, isSupporter)
|
|
124
|
+
.task(id: accountManager.currentClient.id) {
|
|
125
|
+
let client = accountManager.currentClient
|
|
126
|
+
if let namespace { quickLook.namespace = namespace }
|
|
127
|
+
currentAccount.setClient(client: client)
|
|
128
|
+
currentInstance.setClient(client: client)
|
|
129
|
+
userPreferences.setClient(client: client)
|
|
130
|
+
await currentInstance.fetchCurrentInstance()
|
|
131
|
+
watcher.setClient(client: client, instanceStreamingURL: currentInstance.instance?.streamingURL)
|
|
132
|
+
if client.isAuth {
|
|
133
|
+
watcher.watch(streams: [.user, .direct])
|
|
134
|
+
} else {
|
|
135
|
+
watcher.stopWatching()
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
.task(id: accountManager.pushAccounts.map(\.token)) {
|
|
139
|
+
pushNotifications.tokens = accountManager.pushAccounts.map(\.token)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Notes:
|
|
146
|
+
- The `.task(id:)` hooks respond to account/client changes, re-seeding services and watcher state.
|
|
147
|
+
- Keep the modifier focused on global wiring; feature-specific state stays within features.
|
|
148
|
+
- Adjust types (AccountManager, StreamWatcher, etc.) to match your project.
|
|
149
|
+
|
|
150
|
+
## SwiftData / ModelContainer
|
|
151
|
+
|
|
152
|
+
Install your `ModelContainer` at the root so all feature views share the same store. Keep the list minimal to the models that need persistence.
|
|
153
|
+
|
|
154
|
+
```swift
|
|
155
|
+
extension View {
|
|
156
|
+
func withModelContainer() -> some View {
|
|
157
|
+
modelContainer(for: [Draft.self, LocalTimeline.self, TagGroup.self])
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Why: a single container avoids duplicated stores per sheet or tab and keeps data consistent.
|
|
163
|
+
|
|
164
|
+
## Sheet routing (enum-driven)
|
|
165
|
+
|
|
166
|
+
Centralize sheets with a small enum and a helper modifier.
|
|
167
|
+
|
|
168
|
+
```swift
|
|
169
|
+
enum SheetDestination: Identifiable {
|
|
170
|
+
case composer
|
|
171
|
+
case settings
|
|
172
|
+
var id: String { String(describing: self) }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
extension View {
|
|
176
|
+
func withSheetDestinations(sheet: Binding<SheetDestination?>) -> some View {
|
|
177
|
+
sheet(item: sheet) { destination in
|
|
178
|
+
switch destination {
|
|
179
|
+
case .composer:
|
|
180
|
+
ComposerView().withEnvironments()
|
|
181
|
+
case .settings:
|
|
182
|
+
SettingsView().withEnvironments()
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Why: enum-driven sheets keep presentation centralized and testable; adding a new sheet means adding one enum case and one switch branch.
|
|
190
|
+
|
|
191
|
+
## When to use
|
|
192
|
+
|
|
193
|
+
- Apps with multiple packages/modules that share environment objects and services.
|
|
194
|
+
- Apps that need to react to account/client changes and rewire streaming/push safely.
|
|
195
|
+
- Any app that wants consistent TabView + NavigationStack + sheet wiring without repeating environment setup.
|
|
196
|
+
|
|
197
|
+
## Caveats
|
|
198
|
+
|
|
199
|
+
- Keep the dependency modifier slim; do not put feature state or heavy logic there.
|
|
200
|
+
- Ensure `.task(id:)` work is lightweight or cancelled appropriately; long-running work belongs in services.
|
|
201
|
+
- If unauthenticated clients exist, gate streaming/watch calls to avoid reconnect spam.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Async state and task lifecycle
|
|
2
|
+
|
|
3
|
+
## Intent
|
|
4
|
+
|
|
5
|
+
Use this pattern when a view loads data, reacts to changing input, or coordinates async work that should follow the SwiftUI view lifecycle.
|
|
6
|
+
|
|
7
|
+
## Core rules
|
|
8
|
+
|
|
9
|
+
- Use `.task` for load-on-appear work that belongs to the view lifecycle.
|
|
10
|
+
- Use `.task(id:)` when async work should restart for a changing input such as a query, selection, or identifier.
|
|
11
|
+
- Treat cancellation as a normal path for view-driven tasks. Check `Task.isCancelled` in longer flows and avoid surfacing cancellation as a user-facing error.
|
|
12
|
+
- Debounce or coalesce user-driven async work such as search before it fans out into repeated requests.
|
|
13
|
+
- Keep UI-facing models and mutations main-actor-safe; do background work in services, then publish the result back to UI state.
|
|
14
|
+
|
|
15
|
+
## Example: load on appear
|
|
16
|
+
|
|
17
|
+
```swift
|
|
18
|
+
struct DetailView: View {
|
|
19
|
+
let id: String
|
|
20
|
+
@State private var state: LoadState<Item> = .idle
|
|
21
|
+
@Environment(ItemClient.self) private var client
|
|
22
|
+
|
|
23
|
+
var body: some View {
|
|
24
|
+
content
|
|
25
|
+
.task {
|
|
26
|
+
await load()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@ViewBuilder
|
|
31
|
+
private var content: some View {
|
|
32
|
+
switch state {
|
|
33
|
+
case .idle, .loading:
|
|
34
|
+
ProgressView()
|
|
35
|
+
case .loaded(let item):
|
|
36
|
+
ItemContent(item: item)
|
|
37
|
+
case .failed(let error):
|
|
38
|
+
ErrorView(error: error)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private func load() async {
|
|
43
|
+
state = .loading
|
|
44
|
+
do {
|
|
45
|
+
state = .loaded(try await client.fetch(id: id))
|
|
46
|
+
} catch is CancellationError {
|
|
47
|
+
return
|
|
48
|
+
} catch {
|
|
49
|
+
state = .failed(error)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Example: restart on input change
|
|
56
|
+
|
|
57
|
+
```swift
|
|
58
|
+
struct SearchView: View {
|
|
59
|
+
@State private var query = ""
|
|
60
|
+
@State private var results: [ResultItem] = []
|
|
61
|
+
@Environment(SearchClient.self) private var client
|
|
62
|
+
|
|
63
|
+
var body: some View {
|
|
64
|
+
List(results) { item in
|
|
65
|
+
Text(item.title)
|
|
66
|
+
}
|
|
67
|
+
.searchable(text: $query)
|
|
68
|
+
.task(id: query) {
|
|
69
|
+
try? await Task.sleep(for: .milliseconds(250))
|
|
70
|
+
guard !Task.isCancelled, !query.isEmpty else {
|
|
71
|
+
results = []
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
do {
|
|
75
|
+
results = try await client.search(query)
|
|
76
|
+
} catch is CancellationError {
|
|
77
|
+
return
|
|
78
|
+
} catch {
|
|
79
|
+
results = []
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## When to move work out of the view
|
|
87
|
+
|
|
88
|
+
- If the async flow spans multiple screens or must survive view dismissal, move it into a service or model.
|
|
89
|
+
- If the view is mostly coordinating app-level lifecycle or account changes, wire it at the app shell in `app-wiring.md`.
|
|
90
|
+
- If retry, caching, or offline policy becomes complex, keep the policy in the client/service and leave the view with simple state transitions.
|
|
91
|
+
|
|
92
|
+
## Pitfalls
|
|
93
|
+
|
|
94
|
+
- Do not start network work directly from `body`.
|
|
95
|
+
- Do not ignore cancellation for searches, typeahead, or rapidly changing selections.
|
|
96
|
+
- Avoid storing derived async state in multiple places when one source of truth is enough.
|