buildanything 1.7.0 → 1.8.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +55 -0
- package/README.md +71 -61
- package/agents/ios-app-review-guardian.md +49 -0
- package/agents/ios-foundation-models-specialist.md +46 -0
- package/agents/ios-storekit-specialist.md +52 -0
- package/agents/ios-swift-architect.md +102 -0
- package/agents/ios-swift-search.md +130 -0
- package/agents/ios-swift-ui-design.md +104 -0
- package/commands/build.md +80 -176
- package/commands/fix.md +65 -0
- package/commands/setup.md +73 -0
- package/commands/ux-review.md +63 -0
- package/commands/verify.md +72 -0
- package/hooks/session-start +18 -1
- package/package.json +5 -2
- package/protocols/brainstorm.md +99 -0
- package/protocols/build-fix.md +52 -0
- package/protocols/cleanup.md +54 -0
- package/protocols/design.md +269 -0
- package/protocols/eval-harness.md +61 -0
- package/protocols/fake-data-detector.md +64 -0
- package/protocols/ios-context.md +235 -0
- package/protocols/ios-frameworks-map.md +323 -0
- package/protocols/ios-phase-branches.md +162 -0
- package/protocols/ios-preflight.md +27 -0
- package/protocols/metric-loop.md +93 -0
- package/protocols/planning.md +87 -0
- package/protocols/smoke-test.md +110 -0
- package/protocols/verify.md +67 -0
- package/protocols/web-phase-branches.md +201 -0
- package/skills/ios/_VENDORED.md +60 -0
- package/skills/ios/activitykit/LICENSE +131 -0
- package/skills/ios/activitykit/SKILL.md +505 -0
- package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
- package/skills/ios/app-intents/LICENSE +131 -0
- package/skills/ios/app-intents/SKILL.md +494 -0
- package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
- package/skills/ios/apple-on-device-ai/LICENSE +131 -0
- package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
- package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
- package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/skills/ios/ios-26-platform/SKILL.md +53 -0
- package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
- package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
- package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
- package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
- package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
- package/skills/ios/ios-bootstrap/SKILL.md +98 -0
- package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
- package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
- package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
- package/skills/ios/ios-debugger-agent/LICENSE +21 -0
- package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
- package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
- package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
- package/skills/ios/ios-hig/SKILL.md +41 -0
- package/skills/ios/ios-hig/references/accessibility.md +81 -0
- package/skills/ios/ios-hig/references/content.md +142 -0
- package/skills/ios/ios-hig/references/feedback.md +123 -0
- package/skills/ios/ios-hig/references/interaction.md +199 -0
- package/skills/ios/ios-hig/references/performance-platform.md +129 -0
- package/skills/ios/ios-hig/references/privacy-permissions.md +181 -0
- package/skills/ios/ios-hig/references/visual-design.md +84 -0
- package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
- package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
- package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
- package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
- package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
- package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
- package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
- package/skills/ios/swift-accessibility/LICENSE +21 -0
- package/skills/ios/swift-accessibility/SKILL.md +371 -0
- package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
- package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
- package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
- package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
- package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
- package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
- package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
- package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
- package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
- package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
- package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
- package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
- package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
- package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
- package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
- package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
- package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
- package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
- package/skills/ios/swift-concurrency/LICENSE +21 -0
- package/skills/ios/swift-concurrency/SKILL.md +171 -0
- package/skills/ios/swift-concurrency/references/_index.md +50 -0
- package/skills/ios/swift-concurrency/references/actors.md +660 -0
- package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
- package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
- package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
- package/skills/ios/swift-concurrency/references/core-data.md +560 -0
- package/skills/ios/swift-concurrency/references/glossary.md +135 -0
- package/skills/ios/swift-concurrency/references/linting.md +155 -0
- package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
- package/skills/ios/swift-concurrency/references/migration.md +1104 -0
- package/skills/ios/swift-concurrency/references/performance.md +593 -0
- package/skills/ios/swift-concurrency/references/sendable.md +598 -0
- package/skills/ios/swift-concurrency/references/tasks.md +636 -0
- package/skills/ios/swift-concurrency/references/testing.md +592 -0
- package/skills/ios/swift-concurrency/references/threading.md +495 -0
- package/skills/ios/swift-security-expert/LICENSE +21 -0
- package/skills/ios/swift-security-expert/SKILL.md +470 -0
- package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
- package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
- package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
- package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
- package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
- package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
- package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
- package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
- package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
- package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
- package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
- package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
- package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
- package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
- package/skills/ios/swift-testing-expert/LICENSE +21 -0
- package/skills/ios/swift-testing-expert/SKILL.md +79 -0
- package/skills/ios/swift-testing-expert/references/_index.md +12 -0
- package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
- package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
- package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
- package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
- package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
- package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
- package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
- package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
- package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
- package/skills/ios/swiftdata-pro/LICENSE +21 -0
- package/skills/ios/swiftdata-pro/SKILL.md +102 -0
- package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
- package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
- package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
- package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
- package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
- package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
- package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
- package/skills/ios/swiftui-design-principles/LICENSE +21 -0
- package/skills/ios/swiftui-design-principles/README.md +41 -0
- package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
- package/skills/ios/swiftui-design-principles/metadata.json +10 -0
- package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
- package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
- package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
- package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
- package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
- package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/skills/ios/swiftui-pro/LICENSE +21 -0
- package/skills/ios/swiftui-pro/SKILL.md +108 -0
- package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
- package/skills/ios/swiftui-pro/references/api.md +39 -0
- package/skills/ios/swiftui-pro/references/data.md +43 -0
- package/skills/ios/swiftui-pro/references/design.md +31 -0
- package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
- package/skills/ios/swiftui-pro/references/navigation.md +14 -0
- package/skills/ios/swiftui-pro/references/performance.md +46 -0
- package/skills/ios/swiftui-pro/references/swift.md +56 -0
- package/skills/ios/swiftui-pro/references/views.md +35 -0
- package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
- package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
- package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
- package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
- package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
- package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
- package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
- package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
- package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
- package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
- package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
- package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
- package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
- package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
- package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
- package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
- package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
- package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/skills/ios/widgetkit/LICENSE +131 -0
- package/skills/ios/widgetkit/SKILL.md +502 -0
- package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swiftui-liquid-glass
|
|
3
|
+
description: Implement, review, or improve SwiftUI features using the iOS 26+ Liquid Glass API. Use when asked to adopt Liquid Glass in new SwiftUI UI, refactor an existing feature to Liquid Glass, or review Liquid Glass usage for correctness, performance, and design alignment.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SwiftUI Liquid Glass
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
Use this skill to build or review SwiftUI features that fully align with the iOS 26+ Liquid Glass API. Prioritize native APIs (`glassEffect`, `GlassEffectContainer`, glass button styles) and Apple design guidance. Keep usage consistent, interactive where needed, and performance aware.
|
|
10
|
+
|
|
11
|
+
## Workflow Decision Tree
|
|
12
|
+
Choose the path that matches the request:
|
|
13
|
+
|
|
14
|
+
### 1) Review an existing feature
|
|
15
|
+
- Inspect where Liquid Glass should be used and where it should not.
|
|
16
|
+
- Verify correct modifier order, shape usage, and container placement.
|
|
17
|
+
- Check for iOS 26+ availability handling and sensible fallbacks.
|
|
18
|
+
|
|
19
|
+
### 2) Improve a feature using Liquid Glass
|
|
20
|
+
- Identify target components for glass treatment (surfaces, chips, buttons, cards).
|
|
21
|
+
- Refactor to use `GlassEffectContainer` where multiple glass elements appear.
|
|
22
|
+
- Introduce interactive glass only for tappable or focusable elements.
|
|
23
|
+
|
|
24
|
+
### 3) Implement a new feature using Liquid Glass
|
|
25
|
+
- Design the glass surfaces and interactions first (shape, prominence, grouping).
|
|
26
|
+
- Add glass modifiers after layout/appearance modifiers.
|
|
27
|
+
- Add morphing transitions only when the view hierarchy changes with animation.
|
|
28
|
+
|
|
29
|
+
## Core Guidelines
|
|
30
|
+
- Prefer native Liquid Glass APIs over custom blurs.
|
|
31
|
+
- Use `GlassEffectContainer` when multiple glass elements coexist.
|
|
32
|
+
- Apply `.glassEffect(...)` after layout and visual modifiers.
|
|
33
|
+
- Use `.interactive()` for elements that respond to touch/pointer.
|
|
34
|
+
- Keep shapes consistent across related elements for a cohesive look.
|
|
35
|
+
- Gate with `#available(iOS 26, *)` and provide a non-glass fallback.
|
|
36
|
+
|
|
37
|
+
## Review Checklist
|
|
38
|
+
- **Availability**: `#available(iOS 26, *)` present with fallback UI.
|
|
39
|
+
- **Composition**: Multiple glass views wrapped in `GlassEffectContainer`.
|
|
40
|
+
- **Modifier order**: `glassEffect` applied after layout/appearance modifiers.
|
|
41
|
+
- **Interactivity**: `interactive()` only where user interaction exists.
|
|
42
|
+
- **Transitions**: `glassEffectID` used with `@Namespace` for morphing.
|
|
43
|
+
- **Consistency**: Shapes, tinting, and spacing align across the feature.
|
|
44
|
+
|
|
45
|
+
## Implementation Checklist
|
|
46
|
+
- Define target elements and desired glass prominence.
|
|
47
|
+
- Wrap grouped glass elements in `GlassEffectContainer` and tune spacing.
|
|
48
|
+
- Use `.glassEffect(.regular.tint(...).interactive(), in: .rect(cornerRadius: ...))` as needed.
|
|
49
|
+
- Use `.buttonStyle(.glass)` / `.buttonStyle(.glassProminent)` for actions.
|
|
50
|
+
- Add morphing transitions with `glassEffectID` when hierarchy changes.
|
|
51
|
+
- Provide fallback materials and visuals for earlier iOS versions.
|
|
52
|
+
|
|
53
|
+
## Quick Snippets
|
|
54
|
+
Use these patterns directly and tailor shapes/tints/spacing.
|
|
55
|
+
|
|
56
|
+
```swift
|
|
57
|
+
if #available(iOS 26, *) {
|
|
58
|
+
Text("Hello")
|
|
59
|
+
.padding()
|
|
60
|
+
.glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16))
|
|
61
|
+
} else {
|
|
62
|
+
Text("Hello")
|
|
63
|
+
.padding()
|
|
64
|
+
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```swift
|
|
69
|
+
GlassEffectContainer(spacing: 24) {
|
|
70
|
+
HStack(spacing: 24) {
|
|
71
|
+
Image(systemName: "scribble.variable")
|
|
72
|
+
.frame(width: 72, height: 72)
|
|
73
|
+
.font(.system(size: 32))
|
|
74
|
+
.glassEffect()
|
|
75
|
+
Image(systemName: "eraser.fill")
|
|
76
|
+
.frame(width: 72, height: 72)
|
|
77
|
+
.font(.system(size: 32))
|
|
78
|
+
.glassEffect()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```swift
|
|
84
|
+
Button("Confirm") { }
|
|
85
|
+
.buttonStyle(.glassProminent)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Resources
|
|
89
|
+
- Reference guide: `references/liquid-glass.md`
|
|
90
|
+
- Prefer Apple docs for up-to-date API details.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
Vendored from: https://github.com/Dimillian/Skills/tree/main/swiftui-liquid-glass
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Implementing Liquid Glass Design in SwiftUI
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Liquid Glass is a dynamic material introduced in iOS that combines the optical properties of glass with a sense of fluidity. It blurs content behind it, reflects color and light from surrounding content, and reacts to touch and pointer interactions in real time. This guide covers how to implement and customize Liquid Glass effects in SwiftUI applications. You should always seek guides on Liquid Glass when asked for help adopting new Apple design.
|
|
6
|
+
|
|
7
|
+
Key features of Liquid Glass:
|
|
8
|
+
- Blurs content behind the material
|
|
9
|
+
- Reflects color and light from surrounding content
|
|
10
|
+
- Reacts to touch and pointer interactions
|
|
11
|
+
- Can morph between shapes during transitions
|
|
12
|
+
- Available for standard and custom components
|
|
13
|
+
|
|
14
|
+
## Basic Implementation
|
|
15
|
+
|
|
16
|
+
### Adding Liquid Glass to a View
|
|
17
|
+
|
|
18
|
+
The simplest way to add Liquid Glass to a view is using the `glassEffect()` modifier:
|
|
19
|
+
|
|
20
|
+
```swift
|
|
21
|
+
Text("Hello, World!")
|
|
22
|
+
.font(.title)
|
|
23
|
+
.padding()
|
|
24
|
+
.glassEffect()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
By default, this applies the regular variant of Glass within a Capsule shape behind the view's content.
|
|
28
|
+
|
|
29
|
+
### Customizing the Shape
|
|
30
|
+
|
|
31
|
+
You can specify a different shape for the Liquid Glass effect:
|
|
32
|
+
|
|
33
|
+
```swift
|
|
34
|
+
Text("Hello, World!")
|
|
35
|
+
.font(.title)
|
|
36
|
+
.padding()
|
|
37
|
+
.glassEffect(in: .rect(cornerRadius: 16.0))
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Common shape options:
|
|
41
|
+
- `.capsule` (default)
|
|
42
|
+
- `.rect(cornerRadius: CGFloat)`
|
|
43
|
+
- `.circle`
|
|
44
|
+
|
|
45
|
+
## Customizing Liquid Glass Effects
|
|
46
|
+
|
|
47
|
+
### Glass Variants and Properties
|
|
48
|
+
|
|
49
|
+
You can customize the Liquid Glass effect by configuring the `Glass` structure:
|
|
50
|
+
|
|
51
|
+
```swift
|
|
52
|
+
Text("Hello, World!")
|
|
53
|
+
.font(.title)
|
|
54
|
+
.padding()
|
|
55
|
+
.glassEffect(.regular.tint(.orange).interactive())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Key customization options:
|
|
59
|
+
- `.regular` - Standard glass effect
|
|
60
|
+
- `.tint(Color)` - Add a color tint to suggest prominence
|
|
61
|
+
- `.interactive(Bool)` - Make the glass react to touch and pointer interactions
|
|
62
|
+
|
|
63
|
+
### Making Interactive Glass
|
|
64
|
+
|
|
65
|
+
To make Liquid Glass react to touch and pointer interactions:
|
|
66
|
+
|
|
67
|
+
```swift
|
|
68
|
+
Text("Hello, World!")
|
|
69
|
+
.font(.title)
|
|
70
|
+
.padding()
|
|
71
|
+
.glassEffect(.regular.interactive(true))
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or more concisely:
|
|
75
|
+
|
|
76
|
+
```swift
|
|
77
|
+
Text("Hello, World!")
|
|
78
|
+
.font(.title)
|
|
79
|
+
.padding()
|
|
80
|
+
.glassEffect(.regular.interactive())
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Working with Multiple Glass Effects
|
|
84
|
+
|
|
85
|
+
### Using GlassEffectContainer
|
|
86
|
+
|
|
87
|
+
When applying Liquid Glass effects to multiple views, use `GlassEffectContainer` for better rendering performance and to enable blending and morphing effects:
|
|
88
|
+
|
|
89
|
+
```swift
|
|
90
|
+
GlassEffectContainer(spacing: 40.0) {
|
|
91
|
+
HStack(spacing: 40.0) {
|
|
92
|
+
Image(systemName: "scribble.variable")
|
|
93
|
+
.frame(width: 80.0, height: 80.0)
|
|
94
|
+
.font(.system(size: 36))
|
|
95
|
+
.glassEffect()
|
|
96
|
+
|
|
97
|
+
Image(systemName: "eraser.fill")
|
|
98
|
+
.frame(width: 80.0, height: 80.0)
|
|
99
|
+
.font(.system(size: 36))
|
|
100
|
+
.glassEffect()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The `spacing` parameter controls how the Liquid Glass effects interact with each other:
|
|
106
|
+
- Smaller spacing: Views need to be closer to merge effects
|
|
107
|
+
- Larger spacing: Effects merge at greater distances
|
|
108
|
+
|
|
109
|
+
### Uniting Multiple Glass Effects
|
|
110
|
+
|
|
111
|
+
To combine multiple views into a single Liquid Glass effect, use the `glassEffectUnion` modifier:
|
|
112
|
+
|
|
113
|
+
```swift
|
|
114
|
+
@Namespace private var namespace
|
|
115
|
+
|
|
116
|
+
// Later in your view:
|
|
117
|
+
GlassEffectContainer(spacing: 20.0) {
|
|
118
|
+
HStack(spacing: 20.0) {
|
|
119
|
+
ForEach(symbolSet.indices, id: \.self) { item in
|
|
120
|
+
Image(systemName: symbolSet[item])
|
|
121
|
+
.frame(width: 80.0, height: 80.0)
|
|
122
|
+
.font(.system(size: 36))
|
|
123
|
+
.glassEffect()
|
|
124
|
+
.glassEffectUnion(id: item < 2 ? "1" : "2", namespace: namespace)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This is useful when creating views dynamically or with views that live outside of an HStack or VStack.
|
|
131
|
+
|
|
132
|
+
## Morphing Effects and Transitions
|
|
133
|
+
|
|
134
|
+
### Creating Morphing Transitions
|
|
135
|
+
|
|
136
|
+
To create morphing effects during transitions between views with Liquid Glass:
|
|
137
|
+
|
|
138
|
+
1. Create a namespace using the `@Namespace` property wrapper
|
|
139
|
+
2. Associate each Liquid Glass effect with a unique identifier using `glassEffectID`
|
|
140
|
+
3. Use animations when changing the view hierarchy
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
@State private var isExpanded: Bool = false
|
|
144
|
+
@Namespace private var namespace
|
|
145
|
+
|
|
146
|
+
var body: some View {
|
|
147
|
+
GlassEffectContainer(spacing: 40.0) {
|
|
148
|
+
HStack(spacing: 40.0) {
|
|
149
|
+
Image(systemName: "scribble.variable")
|
|
150
|
+
.frame(width: 80.0, height: 80.0)
|
|
151
|
+
.font(.system(size: 36))
|
|
152
|
+
.glassEffect()
|
|
153
|
+
.glassEffectID("pencil", in: namespace)
|
|
154
|
+
|
|
155
|
+
if isExpanded {
|
|
156
|
+
Image(systemName: "eraser.fill")
|
|
157
|
+
.frame(width: 80.0, height: 80.0)
|
|
158
|
+
.font(.system(size: 36))
|
|
159
|
+
.glassEffect()
|
|
160
|
+
.glassEffectID("eraser", in: namespace)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
Button("Toggle") {
|
|
166
|
+
withAnimation {
|
|
167
|
+
isExpanded.toggle()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
.buttonStyle(.glass)
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The morphing effect occurs when views with Liquid Glass appear or disappear due to view hierarchy changes.
|
|
175
|
+
|
|
176
|
+
## Button Styling with Liquid Glass
|
|
177
|
+
|
|
178
|
+
### Glass Button Style
|
|
179
|
+
|
|
180
|
+
SwiftUI provides built-in button styles for Liquid Glass:
|
|
181
|
+
|
|
182
|
+
```swift
|
|
183
|
+
Button("Click Me") {
|
|
184
|
+
// Action
|
|
185
|
+
}
|
|
186
|
+
.buttonStyle(.glass)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Glass Prominent Button Style
|
|
190
|
+
|
|
191
|
+
For a more prominent glass button:
|
|
192
|
+
|
|
193
|
+
```swift
|
|
194
|
+
Button("Important Action") {
|
|
195
|
+
// Action
|
|
196
|
+
}
|
|
197
|
+
.buttonStyle(.glassProminent)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Advanced Techniques
|
|
201
|
+
|
|
202
|
+
### Background Extension Effect
|
|
203
|
+
|
|
204
|
+
To stretch content behind a sidebar or inspector with the background extension effect:
|
|
205
|
+
|
|
206
|
+
```swift
|
|
207
|
+
NavigationSplitView {
|
|
208
|
+
// Sidebar content
|
|
209
|
+
} detail: {
|
|
210
|
+
// Detail content
|
|
211
|
+
.background {
|
|
212
|
+
// Background content that extends under the sidebar
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Extending Horizontal Scrolling Under Sidebar
|
|
218
|
+
|
|
219
|
+
To extend horizontal scroll views under a sidebar or inspector:
|
|
220
|
+
|
|
221
|
+
```swift
|
|
222
|
+
ScrollView(.horizontal) {
|
|
223
|
+
// Scrollable content
|
|
224
|
+
}
|
|
225
|
+
.scrollExtensionMode(.underSidebar)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Best Practices
|
|
229
|
+
|
|
230
|
+
1. **Container Usage**: Always use `GlassEffectContainer` when applying Liquid Glass to multiple views for better performance and morphing effects.
|
|
231
|
+
|
|
232
|
+
2. **Effect Order**: Apply the `.glassEffect()` modifier after other modifiers that affect the appearance of the view.
|
|
233
|
+
|
|
234
|
+
3. **Spacing Consideration**: Carefully choose spacing values in containers to control how and when glass effects merge.
|
|
235
|
+
|
|
236
|
+
4. **Animation**: Use animations when changing view hierarchies to enable smooth morphing transitions.
|
|
237
|
+
|
|
238
|
+
5. **Interactivity**: Add `.interactive()` to glass effects that should respond to user interaction.
|
|
239
|
+
|
|
240
|
+
6. **Consistent Design**: Maintain consistent shapes and styles across your app for a cohesive look and feel.
|
|
241
|
+
|
|
242
|
+
## Example: Custom Badge with Liquid Glass
|
|
243
|
+
|
|
244
|
+
```swift
|
|
245
|
+
struct BadgeView: View {
|
|
246
|
+
let symbol: String
|
|
247
|
+
let color: Color
|
|
248
|
+
|
|
249
|
+
var body: some View {
|
|
250
|
+
ZStack {
|
|
251
|
+
Image(systemName: "hexagon.fill")
|
|
252
|
+
.foregroundColor(color)
|
|
253
|
+
.font(.system(size: 50))
|
|
254
|
+
|
|
255
|
+
Image(systemName: symbol)
|
|
256
|
+
.foregroundColor(.white)
|
|
257
|
+
.font(.system(size: 30))
|
|
258
|
+
}
|
|
259
|
+
.glassEffect(.regular, in: .rect(cornerRadius: 16))
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Usage:
|
|
264
|
+
GlassEffectContainer(spacing: 20) {
|
|
265
|
+
HStack(spacing: 20) {
|
|
266
|
+
BadgeView(symbol: "star.fill", color: .blue)
|
|
267
|
+
BadgeView(symbol: "heart.fill", color: .red)
|
|
268
|
+
BadgeView(symbol: "leaf.fill", color: .green)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## References
|
|
274
|
+
|
|
275
|
+
- [Applying Liquid Glass to custom views](https://developer.apple.com/documentation/SwiftUI/Applying-Liquid-Glass-to-custom-views)
|
|
276
|
+
- [Landmarks: Building an app with Liquid Glass](https://developer.apple.com/documentation/SwiftUI/Landmarks-Building-an-app-with-Liquid-Glass)
|
|
277
|
+
- [SwiftUI View.glassEffect(_:in:isEnabled:)](https://developer.apple.com/documentation/SwiftUI/View/glassEffect(_:in:isEnabled:))
|
|
278
|
+
- [SwiftUI GlassEffectContainer](https://developer.apple.com/documentation/SwiftUI/GlassEffectContainer)
|
|
279
|
+
- [SwiftUI GlassEffectTransition](https://developer.apple.com/documentation/SwiftUI/GlassEffectTransition)
|
|
280
|
+
- [SwiftUI GlassButtonStyle](https://developer.apple.com/documentation/SwiftUI/GlassButtonStyle)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Ricouard
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swiftui-performance-audit
|
|
3
|
+
description: Audit and improve SwiftUI runtime performance from code review and architecture. Use for requests to diagnose slow rendering, janky scrolling, high CPU/memory usage, excessive view updates, or layout thrash in SwiftUI apps, and to provide guidance for user-run Instruments profiling when code review alone is insufficient.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SwiftUI Performance Audit
|
|
7
|
+
|
|
8
|
+
## Quick start
|
|
9
|
+
|
|
10
|
+
Use this skill to diagnose SwiftUI performance issues from code first, then request profiling evidence when code review alone cannot explain the symptoms.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
|
|
14
|
+
1. Classify the symptom: slow rendering, janky scrolling, high CPU, memory growth, hangs, or excessive view updates.
|
|
15
|
+
2. If code is available, start with a code-first review using `references/code-smells.md`.
|
|
16
|
+
3. If code is not available, ask for the smallest useful slice: target view, data flow, reproduction steps, and deployment target.
|
|
17
|
+
4. If code review is inconclusive or runtime evidence is required, guide the user through profiling with `references/profiling-intake.md`.
|
|
18
|
+
5. Summarize likely causes, evidence, remediation, and validation steps using `references/report-template.md`.
|
|
19
|
+
|
|
20
|
+
## 1. Intake
|
|
21
|
+
|
|
22
|
+
Collect:
|
|
23
|
+
- Target view or feature code.
|
|
24
|
+
- Symptoms and exact reproduction steps.
|
|
25
|
+
- Data flow: `@State`, `@Binding`, environment dependencies, and observable models.
|
|
26
|
+
- Whether the issue shows up on device or simulator, and whether it was observed in Debug or Release.
|
|
27
|
+
|
|
28
|
+
Ask the user to classify the issue if possible:
|
|
29
|
+
- CPU spike or battery drain
|
|
30
|
+
- Janky scrolling or dropped frames
|
|
31
|
+
- High memory or image pressure
|
|
32
|
+
- Hangs or unresponsive interactions
|
|
33
|
+
- Excessive or unexpectedly broad view updates
|
|
34
|
+
|
|
35
|
+
For the full profiling intake checklist, read `references/profiling-intake.md`.
|
|
36
|
+
|
|
37
|
+
## 2. Code-First Review
|
|
38
|
+
|
|
39
|
+
Focus on:
|
|
40
|
+
- Invalidation storms from broad observation or environment reads.
|
|
41
|
+
- Unstable identity in lists and `ForEach`.
|
|
42
|
+
- Heavy derived work in `body` or view builders.
|
|
43
|
+
- Layout thrash from complex hierarchies, `GeometryReader`, or preference chains.
|
|
44
|
+
- Large image decode or resize work on the main thread.
|
|
45
|
+
- Animation or transition work applied too broadly.
|
|
46
|
+
|
|
47
|
+
Use `references/code-smells.md` for the detailed smell catalog and fix guidance.
|
|
48
|
+
|
|
49
|
+
Provide:
|
|
50
|
+
- Likely root causes with code references.
|
|
51
|
+
- Suggested fixes and refactors.
|
|
52
|
+
- If needed, a minimal repro or instrumentation suggestion.
|
|
53
|
+
|
|
54
|
+
## 3. Guide the User to Profile
|
|
55
|
+
|
|
56
|
+
If code review does not explain the issue, ask for runtime evidence:
|
|
57
|
+
- A trace export or screenshots of the SwiftUI timeline and Time Profiler call tree.
|
|
58
|
+
- Device/OS/build configuration.
|
|
59
|
+
- The exact interaction being profiled.
|
|
60
|
+
- Before/after metrics if the user is comparing a change.
|
|
61
|
+
|
|
62
|
+
Use `references/profiling-intake.md` for the exact checklist and collection steps.
|
|
63
|
+
|
|
64
|
+
## 4. Analyze and Diagnose
|
|
65
|
+
|
|
66
|
+
- Map the evidence to the most likely category: invalidation, identity churn, layout thrash, main-thread work, image cost, or animation cost.
|
|
67
|
+
- Prioritize problems by impact, not by how easy they are to explain.
|
|
68
|
+
- Distinguish code-level suspicion from trace-backed evidence.
|
|
69
|
+
- Call out when profiling is still insufficient and what additional evidence would reduce uncertainty.
|
|
70
|
+
|
|
71
|
+
## 5. Remediate
|
|
72
|
+
|
|
73
|
+
Apply targeted fixes:
|
|
74
|
+
- Narrow state scope and reduce broad observation fan-out.
|
|
75
|
+
- Stabilize identities for `ForEach` and lists.
|
|
76
|
+
- Move heavy work out of `body` into derived state updated from inputs, model-layer precomputation, memoized helpers, or background preprocessing. Use `@State` only for view-owned state, not as an ad hoc cache for arbitrary computation.
|
|
77
|
+
- Use `equatable()` only when equality is cheaper than recomputing the subtree and the inputs are truly value-semantic.
|
|
78
|
+
- Downsample images before rendering.
|
|
79
|
+
- Reduce layout complexity or use fixed sizing where possible.
|
|
80
|
+
|
|
81
|
+
Use `references/code-smells.md` for examples, Observation-specific fan-out guidance, and remediation patterns.
|
|
82
|
+
|
|
83
|
+
## 6. Verify
|
|
84
|
+
|
|
85
|
+
Ask the user to re-run the same capture and compare with baseline metrics.
|
|
86
|
+
Summarize the delta (CPU, frame drops, memory peak) if provided.
|
|
87
|
+
|
|
88
|
+
## Outputs
|
|
89
|
+
|
|
90
|
+
Provide:
|
|
91
|
+
- A short metrics table (before/after if available).
|
|
92
|
+
- Top issues (ordered by impact).
|
|
93
|
+
- Proposed fixes with estimated effort.
|
|
94
|
+
|
|
95
|
+
Use `references/report-template.md` when formatting the final audit.
|
|
96
|
+
|
|
97
|
+
## References
|
|
98
|
+
|
|
99
|
+
- Profiling intake and collection checklist: `references/profiling-intake.md`
|
|
100
|
+
- Common code smells and remediation patterns: `references/code-smells.md`
|
|
101
|
+
- Audit output template: `references/report-template.md`
|
|
102
|
+
- Add Apple documentation and WWDC resources under `references/` as they are supplied by the user.
|
|
103
|
+
- Optimizing SwiftUI performance with Instruments: `references/optimizing-swiftui-performance-instruments.md`
|
|
104
|
+
- Understanding and improving SwiftUI performance: `references/understanding-improving-swiftui-performance.md`
|
|
105
|
+
- Understanding hangs in your app: `references/understanding-hangs-in-your-app.md`
|
|
106
|
+
- Demystify SwiftUI performance (WWDC23): `references/demystify-swiftui-performance-wwdc23.md`
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
Vendored from: https://github.com/Dimillian/Skills/tree/main/swiftui-performance-audit
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Common code smells and remediation patterns
|
|
2
|
+
|
|
3
|
+
## Intent
|
|
4
|
+
|
|
5
|
+
Use this reference during code-first review to map visible SwiftUI patterns to likely runtime costs and safer remediation guidance.
|
|
6
|
+
|
|
7
|
+
## High-priority smells
|
|
8
|
+
|
|
9
|
+
### Expensive formatters in `body`
|
|
10
|
+
|
|
11
|
+
```swift
|
|
12
|
+
var body: some View {
|
|
13
|
+
let number = NumberFormatter()
|
|
14
|
+
let measure = MeasurementFormatter()
|
|
15
|
+
Text(measure.string(from: .init(value: meters, unit: .meters)))
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Prefer cached formatters in a model or dedicated helper:
|
|
20
|
+
|
|
21
|
+
```swift
|
|
22
|
+
final class DistanceFormatter {
|
|
23
|
+
static let shared = DistanceFormatter()
|
|
24
|
+
let number = NumberFormatter()
|
|
25
|
+
let measure = MeasurementFormatter()
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Heavy computed properties
|
|
30
|
+
|
|
31
|
+
```swift
|
|
32
|
+
var filtered: [Item] {
|
|
33
|
+
items.filter { $0.isEnabled }
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Prefer deriving this once per meaningful input change in a model/helper, or store derived view-owned state only when the view truly owns the transformation lifecycle.
|
|
38
|
+
|
|
39
|
+
### Sorting or filtering inside `body`
|
|
40
|
+
|
|
41
|
+
```swift
|
|
42
|
+
List {
|
|
43
|
+
ForEach(items.sorted(by: sortRule)) { item in
|
|
44
|
+
Row(item)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Prefer sorting before render work begins:
|
|
50
|
+
|
|
51
|
+
```swift
|
|
52
|
+
let sortedItems = items.sorted(by: sortRule)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Inline filtering inside `ForEach`
|
|
56
|
+
|
|
57
|
+
```swift
|
|
58
|
+
ForEach(items.filter { $0.isEnabled }) { item in
|
|
59
|
+
Row(item)
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Prefer a prefiltered collection with stable identity.
|
|
64
|
+
|
|
65
|
+
### Unstable identity
|
|
66
|
+
|
|
67
|
+
```swift
|
|
68
|
+
ForEach(items, id: \.self) { item in
|
|
69
|
+
Row(item)
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Avoid `id: \.self` for non-stable values or collections that reorder. Use a stable domain identifier.
|
|
74
|
+
|
|
75
|
+
### Top-level conditional view swapping
|
|
76
|
+
|
|
77
|
+
```swift
|
|
78
|
+
var content: some View {
|
|
79
|
+
if isEditing {
|
|
80
|
+
editingView
|
|
81
|
+
} else {
|
|
82
|
+
readOnlyView
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Prefer one stable base view and localize conditions to sections or modifiers. This reduces root identity churn and makes diffing cheaper.
|
|
88
|
+
|
|
89
|
+
### Image decoding on the main thread
|
|
90
|
+
|
|
91
|
+
```swift
|
|
92
|
+
Image(uiImage: UIImage(data: data)!)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Prefer decode and downsample work off the main thread, then store the processed image.
|
|
96
|
+
|
|
97
|
+
## Observation fan-out
|
|
98
|
+
|
|
99
|
+
### Broad `@Observable` reads on iOS 17+
|
|
100
|
+
|
|
101
|
+
```swift
|
|
102
|
+
@Observable final class Model {
|
|
103
|
+
var items: [Item] = []
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
var body: some View {
|
|
107
|
+
Row(isFavorite: model.items.contains(item))
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If many views read the same broad collection or root model, small changes can fan out into wide invalidation. Prefer narrower derived inputs, smaller observable surfaces, or per-item state closer to the leaf views.
|
|
112
|
+
|
|
113
|
+
### Broad `ObservableObject` reads on iOS 16 and earlier
|
|
114
|
+
|
|
115
|
+
```swift
|
|
116
|
+
final class Model: ObservableObject {
|
|
117
|
+
@Published var items: [Item] = []
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The same warning applies to legacy observation. Avoid having many descendants observe a large shared object when they only need one derived field.
|
|
122
|
+
|
|
123
|
+
## Remediation notes
|
|
124
|
+
|
|
125
|
+
### `@State` is not a generic cache
|
|
126
|
+
|
|
127
|
+
Use `@State` for view-owned state and derived values that intentionally belong to the view lifecycle. Do not move arbitrary expensive computation into `@State` unless you also define when and why it updates.
|
|
128
|
+
|
|
129
|
+
Better alternatives:
|
|
130
|
+
- precompute in the model or store
|
|
131
|
+
- update derived state in response to a specific input change
|
|
132
|
+
- memoize in a dedicated helper
|
|
133
|
+
- preprocess on a background task before rendering
|
|
134
|
+
|
|
135
|
+
### `equatable()` is conditional guidance
|
|
136
|
+
|
|
137
|
+
Use `equatable()` only when:
|
|
138
|
+
- equality is cheaper than recomputing the subtree, and
|
|
139
|
+
- the view inputs are value-semantic and stable enough for meaningful equality checks
|
|
140
|
+
|
|
141
|
+
Do not apply `equatable()` as a blanket fix for all redraws.
|
|
142
|
+
|
|
143
|
+
## Triage order
|
|
144
|
+
|
|
145
|
+
When multiple smells appear together, prioritize in this order:
|
|
146
|
+
1. Broad invalidation and observation fan-out
|
|
147
|
+
2. Unstable identity and list churn
|
|
148
|
+
3. Main-thread work during render
|
|
149
|
+
4. Image decode or resize cost
|
|
150
|
+
5. Layout and animation complexity
|