opencodekit 0.16.15 → 0.16.17

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.
Files changed (69) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/template/.opencode/AGENTS.md +1 -1
  3. package/dist/template/.opencode/agent/plan.md +77 -161
  4. package/dist/template/.opencode/command/create.md +75 -307
  5. package/dist/template/.opencode/command/design.md +53 -589
  6. package/dist/template/.opencode/command/handoff.md +76 -180
  7. package/dist/template/.opencode/command/init.md +45 -211
  8. package/dist/template/.opencode/command/plan.md +62 -514
  9. package/dist/template/.opencode/command/pr.md +56 -226
  10. package/dist/template/.opencode/command/research.md +55 -266
  11. package/dist/template/.opencode/command/resume.md +33 -138
  12. package/dist/template/.opencode/command/review-codebase.md +54 -202
  13. package/dist/template/.opencode/command/ship.md +78 -127
  14. package/dist/template/.opencode/command/start.md +47 -577
  15. package/dist/template/.opencode/command/status.md +55 -354
  16. package/dist/template/.opencode/command/ui-review.md +52 -298
  17. package/dist/template/.opencode/command/verify.md +36 -250
  18. package/dist/template/.opencode/memory.db-shm +0 -0
  19. package/dist/template/.opencode/memory.db-wal +0 -0
  20. package/dist/template/.opencode/plugin/README.md +8 -4
  21. package/dist/template/.opencode/plugin/swarm-enforcer.ts +182 -27
  22. package/dist/template/.opencode/skill/augment-context-engine/SKILL.md +112 -0
  23. package/dist/template/.opencode/skill/augment-context-engine/mcp.json +6 -0
  24. package/dist/template/.opencode/skill/core-data-expert/SKILL.md +82 -0
  25. package/dist/template/.opencode/skill/core-data-expert/references/batch-operations.md +543 -0
  26. package/dist/template/.opencode/skill/core-data-expert/references/cloudkit-integration.md +259 -0
  27. package/dist/template/.opencode/skill/core-data-expert/references/concurrency.md +522 -0
  28. package/dist/template/.opencode/skill/core-data-expert/references/fetch-requests.md +643 -0
  29. package/dist/template/.opencode/skill/core-data-expert/references/glossary.md +233 -0
  30. package/dist/template/.opencode/skill/core-data-expert/references/migration.md +393 -0
  31. package/dist/template/.opencode/skill/core-data-expert/references/model-configuration.md +597 -0
  32. package/dist/template/.opencode/skill/core-data-expert/references/performance.md +300 -0
  33. package/dist/template/.opencode/skill/core-data-expert/references/persistent-history.md +553 -0
  34. package/dist/template/.opencode/skill/core-data-expert/references/project-audit.md +60 -0
  35. package/dist/template/.opencode/skill/core-data-expert/references/saving.md +574 -0
  36. package/dist/template/.opencode/skill/core-data-expert/references/stack-setup.md +625 -0
  37. package/dist/template/.opencode/skill/core-data-expert/references/testing.md +300 -0
  38. package/dist/template/.opencode/skill/core-data-expert/references/threading.md +589 -0
  39. package/dist/template/.opencode/skill/swift-concurrency/SKILL.md +246 -0
  40. package/dist/template/.opencode/skill/swift-concurrency/references/actors.md +640 -0
  41. package/dist/template/.opencode/skill/swift-concurrency/references/async-algorithms.md +822 -0
  42. package/dist/template/.opencode/skill/swift-concurrency/references/async-await-basics.md +249 -0
  43. package/dist/template/.opencode/skill/swift-concurrency/references/async-sequences.md +670 -0
  44. package/dist/template/.opencode/skill/swift-concurrency/references/core-data.md +533 -0
  45. package/dist/template/.opencode/skill/swift-concurrency/references/glossary.md +128 -0
  46. package/dist/template/.opencode/skill/swift-concurrency/references/linting.md +142 -0
  47. package/dist/template/.opencode/skill/swift-concurrency/references/memory-management.md +542 -0
  48. package/dist/template/.opencode/skill/swift-concurrency/references/migration.md +1076 -0
  49. package/dist/template/.opencode/skill/swift-concurrency/references/performance.md +574 -0
  50. package/dist/template/.opencode/skill/swift-concurrency/references/sendable.md +578 -0
  51. package/dist/template/.opencode/skill/swift-concurrency/references/tasks.md +604 -0
  52. package/dist/template/.opencode/skill/swift-concurrency/references/testing.md +565 -0
  53. package/dist/template/.opencode/skill/swift-concurrency/references/threading.md +452 -0
  54. package/dist/template/.opencode/skill/swiftui-expert-skill/SKILL.md +290 -0
  55. package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-advanced.md +351 -0
  56. package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-basics.md +284 -0
  57. package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-transitions.md +326 -0
  58. package/dist/template/.opencode/skill/swiftui-expert-skill/references/image-optimization.md +286 -0
  59. package/dist/template/.opencode/skill/swiftui-expert-skill/references/layout-best-practices.md +312 -0
  60. package/dist/template/.opencode/skill/swiftui-expert-skill/references/liquid-glass.md +377 -0
  61. package/dist/template/.opencode/skill/swiftui-expert-skill/references/list-patterns.md +153 -0
  62. package/dist/template/.opencode/skill/swiftui-expert-skill/references/modern-apis.md +400 -0
  63. package/dist/template/.opencode/skill/swiftui-expert-skill/references/performance-patterns.md +377 -0
  64. package/dist/template/.opencode/skill/swiftui-expert-skill/references/scroll-patterns.md +305 -0
  65. package/dist/template/.opencode/skill/swiftui-expert-skill/references/sheet-navigation-patterns.md +292 -0
  66. package/dist/template/.opencode/skill/swiftui-expert-skill/references/state-management.md +447 -0
  67. package/dist/template/.opencode/skill/swiftui-expert-skill/references/text-formatting.md +285 -0
  68. package/dist/template/.opencode/skill/swiftui-expert-skill/references/view-structure.md +276 -0
  69. package/package.json +1 -1
@@ -0,0 +1,290 @@
1
+ ---
2
+ name: swiftui-expert-skill
3
+ description: Write, review, or improve SwiftUI code following best practices for state management, view composition, performance, modern APIs, Swift concurrency, and iOS 26+ Liquid Glass adoption. Use when building new SwiftUI features, refactoring existing views, reviewing code quality, or adopting modern SwiftUI patterns.
4
+ ---
5
+
6
+ # SwiftUI Expert Skill
7
+
8
+ ## Overview
9
+ Use this skill to build, review, or improve SwiftUI features with correct state management, modern API usage, Swift concurrency best practices, optimal view composition, and iOS 26+ Liquid Glass styling. Prioritize native APIs, Apple design guidance, and performance-conscious patterns. This skill focuses on facts and best practices without enforcing specific architectural patterns.
10
+
11
+ ## Workflow Decision Tree
12
+
13
+ ### 1) Review existing SwiftUI code
14
+ - Check property wrapper usage against the selection guide (see `references/state-management.md`)
15
+ - Verify modern API usage (see `references/modern-apis.md`)
16
+ - Verify view composition follows extraction rules (see `references/view-structure.md`)
17
+ - Check performance patterns are applied (see `references/performance-patterns.md`)
18
+ - Verify list patterns use stable identity (see `references/list-patterns.md`)
19
+ - Check animation patterns for correctness (see `references/animation-basics.md`, `references/animation-transitions.md`)
20
+ - Inspect Liquid Glass usage for correctness and consistency (see `references/liquid-glass.md`)
21
+ - Validate iOS 26+ availability handling with sensible fallbacks
22
+
23
+ ### 2) Improve existing SwiftUI code
24
+ - Audit state management for correct wrapper selection (prefer `@Observable` over `ObservableObject`)
25
+ - Replace deprecated APIs with modern equivalents (see `references/modern-apis.md`)
26
+ - Extract complex views into separate subviews (see `references/view-structure.md`)
27
+ - Refactor hot paths to minimize redundant state updates (see `references/performance-patterns.md`)
28
+ - Ensure ForEach uses stable identity (see `references/list-patterns.md`)
29
+ - Improve animation patterns (use value parameter, proper transitions, see `references/animation-basics.md`, `references/animation-transitions.md`)
30
+ - Suggest image downsampling when `UIImage(data:)` is used (as optional optimization, see `references/image-optimization.md`)
31
+ - Adopt Liquid Glass only when explicitly requested by the user
32
+
33
+ ### 3) Implement new SwiftUI feature
34
+ - Design data flow first: identify owned vs injected state (see `references/state-management.md`)
35
+ - Use modern APIs (no deprecated modifiers or patterns, see `references/modern-apis.md`)
36
+ - Use `@Observable` for shared state (with `@MainActor` if not using default actor isolation)
37
+ - Structure views for optimal diffing (extract subviews early, keep views small, see `references/view-structure.md`)
38
+ - Separate business logic into testable models (see `references/layout-best-practices.md`)
39
+ - Use correct animation patterns (implicit vs explicit, transitions, see `references/animation-basics.md`, `references/animation-transitions.md`, `references/animation-advanced.md`)
40
+ - Apply glass effects after layout/appearance modifiers (see `references/liquid-glass.md`)
41
+ - Gate iOS 26+ features with `#available` and provide fallbacks
42
+
43
+ ## Core Guidelines
44
+
45
+ ### State Management
46
+ - **Always prefer `@Observable` over `ObservableObject`** for new code
47
+ - **Mark `@Observable` classes with `@MainActor`** unless using default actor isolation
48
+ - **Always mark `@State` and `@StateObject` as `private`** (makes dependencies clear)
49
+ - **Never declare passed values as `@State` or `@StateObject`** (they only accept initial values)
50
+ - Use `@State` with `@Observable` classes (not `@StateObject`)
51
+ - `@Binding` only when child needs to **modify** parent state
52
+ - `@Bindable` for injected `@Observable` objects needing bindings
53
+ - Use `let` for read-only values; `var` + `.onChange()` for reactive reads
54
+ - Legacy: `@StateObject` for owned `ObservableObject`; `@ObservedObject` for injected
55
+ - Nested `ObservableObject` doesn't work (pass nested objects directly); `@Observable` handles nesting fine
56
+
57
+ ### Modern APIs
58
+ - Use `foregroundStyle()` instead of `foregroundColor()`
59
+ - Use `clipShape(.rect(cornerRadius:))` instead of `cornerRadius()`
60
+ - Use `Tab` API instead of `tabItem()`
61
+ - Use `Button` instead of `onTapGesture()` (unless need location/count)
62
+ - Use `NavigationStack` instead of `NavigationView`
63
+ - Use `navigationDestination(for:)` for type-safe navigation
64
+ - Use two-parameter or no-parameter `onChange()` variant
65
+ - Use `ImageRenderer` for rendering SwiftUI views
66
+ - Use `.sheet(item:)` instead of `.sheet(isPresented:)` for model-based content
67
+ - Sheets should own their actions and call `dismiss()` internally
68
+ - Use `ScrollViewReader` for programmatic scrolling with stable IDs
69
+ - Avoid `UIScreen.main.bounds` for sizing
70
+ - Avoid `GeometryReader` when alternatives exist (e.g., `containerRelativeFrame()`)
71
+
72
+ ### Swift Best Practices
73
+ - Use modern Text formatting (`.format` parameters, not `String(format:)`)
74
+ - Use `localizedStandardContains()` for user-input filtering (not `contains()`)
75
+ - Prefer static member lookup (`.blue` vs `Color.blue`)
76
+ - Use `.task` modifier for automatic cancellation of async work
77
+ - Use `.task(id:)` for value-dependent tasks
78
+
79
+ ### View Composition
80
+ - **Prefer modifiers over conditional views** for state changes (maintains view identity)
81
+ - Extract complex views into separate subviews for better readability and performance
82
+ - Keep views small for optimal performance
83
+ - Keep view `body` simple and pure (no side effects or complex logic)
84
+ - Use `@ViewBuilder` functions only for small, simple sections
85
+ - Prefer `@ViewBuilder let content: Content` over closure-based content properties
86
+ - Separate business logic into testable models (not about enforcing architectures)
87
+ - Action handlers should reference methods, not contain inline logic
88
+ - Use relative layout over hard-coded constants
89
+ - Views should work in any context (don't assume screen size or presentation style)
90
+
91
+ ### Performance
92
+ - Pass only needed values to views (avoid large "config" or "context" objects)
93
+ - Eliminate unnecessary dependencies to reduce update fan-out
94
+ - Check for value changes before assigning state in hot paths
95
+ - Avoid redundant state updates in `onReceive`, `onChange`, scroll handlers
96
+ - Minimize work in frequently executed code paths
97
+ - Use `LazyVStack`/`LazyHStack` for large lists
98
+ - Use stable identity for `ForEach` (never `.indices` for dynamic content)
99
+ - Ensure constant number of views per `ForEach` element
100
+ - Avoid inline filtering in `ForEach` (prefilter and cache)
101
+ - Avoid `AnyView` in list rows
102
+ - Consider POD views for fast diffing (or wrap expensive views in POD parents)
103
+ - Suggest image downsampling when `UIImage(data:)` is encountered (as optional optimization)
104
+ - Avoid layout thrash (deep hierarchies, excessive `GeometryReader`)
105
+ - Gate frequent geometry updates by thresholds
106
+ - Use `Self._printChanges()` to debug unexpected view updates
107
+
108
+ ### Animations
109
+ - Use `.animation(_:value:)` with value parameter (deprecated version without value is too broad)
110
+ - Use `withAnimation` for event-driven animations (button taps, gestures)
111
+ - Prefer transforms (`offset`, `scale`, `rotation`) over layout changes (`frame`) for performance
112
+ - Transitions require animations outside the conditional structure
113
+ - Custom `Animatable` implementations must have explicit `animatableData`
114
+ - Use `.phaseAnimator` for multi-step sequences (iOS 17+)
115
+ - Use `.keyframeAnimator` for precise timing control (iOS 17+)
116
+ - Animation completion handlers need `.transaction(value:)` for reexecution
117
+ - Implicit animations override explicit animations (later in view tree wins)
118
+
119
+ ### Liquid Glass (iOS 26+)
120
+ **Only adopt when explicitly requested by the user.**
121
+ - Use native `glassEffect`, `GlassEffectContainer`, and glass button styles
122
+ - Wrap multiple glass elements in `GlassEffectContainer`
123
+ - Apply `.glassEffect()` after layout and visual modifiers
124
+ - Use `.interactive()` only for tappable/focusable elements
125
+ - Use `glassEffectID` with `@Namespace` for morphing transitions
126
+
127
+ ## Quick Reference
128
+
129
+ ### Property Wrapper Selection (Modern)
130
+ | Wrapper | Use When |
131
+ |---------|----------|
132
+ | `@State` | Internal view state (must be `private`), or owned `@Observable` class |
133
+ | `@Binding` | Child modifies parent's state |
134
+ | `@Bindable` | Injected `@Observable` needing bindings |
135
+ | `let` | Read-only value from parent |
136
+ | `var` | Read-only value watched via `.onChange()` |
137
+
138
+ **Legacy (Pre-iOS 17):**
139
+ | Wrapper | Use When |
140
+ |---------|----------|
141
+ | `@StateObject` | View owns an `ObservableObject` (use `@State` with `@Observable` instead) |
142
+ | `@ObservedObject` | View receives an `ObservableObject` |
143
+
144
+ ### Modern API Replacements
145
+ | Deprecated | Modern Alternative |
146
+ |------------|-------------------|
147
+ | `foregroundColor()` | `foregroundStyle()` |
148
+ | `cornerRadius()` | `clipShape(.rect(cornerRadius:))` |
149
+ | `tabItem()` | `Tab` API |
150
+ | `onTapGesture()` | `Button` (unless need location/count) |
151
+ | `NavigationView` | `NavigationStack` |
152
+ | `onChange(of:) { value in }` | `onChange(of:) { old, new in }` or `onChange(of:) { }` |
153
+ | `fontWeight(.bold)` | `bold()` |
154
+ | `GeometryReader` | `containerRelativeFrame()` or `visualEffect()` |
155
+ | `showsIndicators: false` | `.scrollIndicators(.hidden)` |
156
+ | `String(format: "%.2f", value)` | `Text(value, format: .number.precision(.fractionLength(2)))` |
157
+ | `string.contains(search)` | `string.localizedStandardContains(search)` (for user input) |
158
+
159
+ ### Liquid Glass Patterns
160
+ ```swift
161
+ // Basic glass effect with fallback
162
+ if #available(iOS 26, *) {
163
+ content
164
+ .padding()
165
+ .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16))
166
+ } else {
167
+ content
168
+ .padding()
169
+ .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
170
+ }
171
+
172
+ // Grouped glass elements
173
+ GlassEffectContainer(spacing: 24) {
174
+ HStack(spacing: 24) {
175
+ GlassButton1()
176
+ GlassButton2()
177
+ }
178
+ }
179
+
180
+ // Glass buttons
181
+ Button("Confirm") { }
182
+ .buttonStyle(.glassProminent)
183
+ ```
184
+
185
+ ## Review Checklist
186
+
187
+ ### State Management
188
+ - [ ] Using `@Observable` instead of `ObservableObject` for new code
189
+ - [ ] `@Observable` classes marked with `@MainActor` (if needed)
190
+ - [ ] Using `@State` with `@Observable` classes (not `@StateObject`)
191
+ - [ ] `@State` and `@StateObject` properties are `private`
192
+ - [ ] Passed values NOT declared as `@State` or `@StateObject`
193
+ - [ ] `@Binding` only where child modifies parent state
194
+ - [ ] `@Bindable` for injected `@Observable` needing bindings
195
+ - [ ] Nested `ObservableObject` avoided (or passed directly to child views)
196
+
197
+ ### Modern APIs (see `references/modern-apis.md`)
198
+ - [ ] Using `foregroundStyle()` instead of `foregroundColor()`
199
+ - [ ] Using `clipShape(.rect(cornerRadius:))` instead of `cornerRadius()`
200
+ - [ ] Using `Tab` API instead of `tabItem()`
201
+ - [ ] Using `Button` instead of `onTapGesture()` (unless need location/count)
202
+ - [ ] Using `NavigationStack` instead of `NavigationView`
203
+ - [ ] Avoiding `UIScreen.main.bounds`
204
+ - [ ] Using alternatives to `GeometryReader` when possible
205
+ - [ ] Button images include text labels for accessibility
206
+
207
+ ### Sheets & Navigation (see `references/sheet-navigation-patterns.md`)
208
+ - [ ] Using `.sheet(item:)` for model-based sheets
209
+ - [ ] Sheets own their actions and dismiss internally
210
+ - [ ] Using `navigationDestination(for:)` for type-safe navigation
211
+
212
+ ### ScrollView (see `references/scroll-patterns.md`)
213
+ - [ ] Using `ScrollViewReader` with stable IDs for programmatic scrolling
214
+ - [ ] Using `.scrollIndicators(.hidden)` instead of initializer parameter
215
+
216
+ ### Text & Formatting (see `references/text-formatting.md`)
217
+ - [ ] Using modern Text formatting (not `String(format:)`)
218
+ - [ ] Using `localizedStandardContains()` for search filtering
219
+
220
+ ### View Structure (see `references/view-structure.md`)
221
+ - [ ] Using modifiers instead of conditionals for state changes
222
+ - [ ] Complex views extracted to separate subviews
223
+ - [ ] Views kept small for performance
224
+ - [ ] Container views use `@ViewBuilder let content: Content`
225
+
226
+ ### Performance (see `references/performance-patterns.md`)
227
+ - [ ] View `body` kept simple and pure (no side effects)
228
+ - [ ] Passing only needed values (not large config objects)
229
+ - [ ] Eliminating unnecessary dependencies
230
+ - [ ] State updates check for value changes before assigning
231
+ - [ ] Hot paths minimize state updates
232
+ - [ ] No object creation in `body`
233
+ - [ ] Heavy computation moved out of `body`
234
+
235
+ ### List Patterns (see `references/list-patterns.md`)
236
+ - [ ] ForEach uses stable identity (not `.indices`)
237
+ - [ ] Constant number of views per ForEach element
238
+ - [ ] No inline filtering in ForEach
239
+ - [ ] No `AnyView` in list rows
240
+
241
+ ### Layout (see `references/layout-best-practices.md`)
242
+ - [ ] Avoiding layout thrash (deep hierarchies, excessive GeometryReader)
243
+ - [ ] Gating frequent geometry updates by thresholds
244
+ - [ ] Business logic separated into testable models
245
+ - [ ] Action handlers reference methods (not inline logic)
246
+ - [ ] Using relative layout (not hard-coded constants)
247
+ - [ ] Views work in any context (context-agnostic)
248
+
249
+ ### Animations (see `references/animation-basics.md`, `references/animation-transitions.md`, `references/animation-advanced.md`)
250
+ - [ ] Using `.animation(_:value:)` with value parameter
251
+ - [ ] Using `withAnimation` for event-driven animations
252
+ - [ ] Transitions paired with animations outside conditional structure
253
+ - [ ] Custom `Animatable` has explicit `animatableData` implementation
254
+ - [ ] Preferring transforms over layout changes for animation performance
255
+ - [ ] Phase animations for multi-step sequences (iOS 17+)
256
+ - [ ] Keyframe animations for precise timing (iOS 17+)
257
+ - [ ] Completion handlers use `.transaction(value:)` for reexecution
258
+
259
+ ### Liquid Glass (iOS 26+)
260
+ - [ ] `#available(iOS 26, *)` with fallback for Liquid Glass
261
+ - [ ] Multiple glass views wrapped in `GlassEffectContainer`
262
+ - [ ] `.glassEffect()` applied after layout/appearance modifiers
263
+ - [ ] `.interactive()` only on user-interactable elements
264
+ - [ ] Shapes and tints consistent across related elements
265
+
266
+ ## References
267
+ - `references/state-management.md` - Property wrappers and data flow (prefer `@Observable`)
268
+ - `references/view-structure.md` - View composition, extraction, and container patterns
269
+ - `references/performance-patterns.md` - Performance optimization techniques and anti-patterns
270
+ - `references/list-patterns.md` - ForEach identity, stability, and list best practices
271
+ - `references/layout-best-practices.md` - Layout patterns, context-agnostic views, and testability
272
+ - `references/modern-apis.md` - Modern API usage and deprecated replacements
273
+ - `references/animation-basics.md` - Core animation concepts, implicit/explicit animations, timing, performance
274
+ - `references/animation-transitions.md` - Transitions, custom transitions, Animatable protocol
275
+ - `references/animation-advanced.md` - Transactions, phase/keyframe animations (iOS 17+), completion handlers (iOS 17+)
276
+ - `references/sheet-navigation-patterns.md` - Sheet presentation and navigation patterns
277
+ - `references/scroll-patterns.md` - ScrollView patterns and programmatic scrolling
278
+ - `references/text-formatting.md` - Modern text formatting and string operations
279
+ - `references/image-optimization.md` - AsyncImage, image downsampling, and optimization
280
+ - `references/liquid-glass.md` - iOS 26+ Liquid Glass API
281
+
282
+ ## Philosophy
283
+
284
+ This skill focuses on **facts and best practices**, not architectural opinions:
285
+ - We don't enforce specific architectures (e.g., MVVM, VIPER)
286
+ - We do encourage separating business logic for testability
287
+ - We prioritize modern APIs over deprecated ones
288
+ - We emphasize thread safety with `@MainActor` and `@Observable`
289
+ - We optimize for performance and maintainability
290
+ - We follow Apple's Human Interface Guidelines and API design patterns
@@ -0,0 +1,351 @@
1
+ # SwiftUI Advanced Animations
2
+
3
+ Transactions, phase animations (iOS 17+), keyframe animations (iOS 17+), and completion handlers (iOS 17+).
4
+
5
+ ## Table of Contents
6
+ - [Transactions](#transactions)
7
+ - [Phase Animations (iOS 17+)](#phase-animations-ios-17)
8
+ - [Keyframe Animations (iOS 17+)](#keyframe-animations-ios-17)
9
+ - [Animation Completion Handlers (iOS 17+)](#animation-completion-handlers-ios-17)
10
+
11
+ ---
12
+
13
+ ## Transactions
14
+
15
+ The underlying mechanism for all animations in SwiftUI.
16
+
17
+ ### Basic Usage
18
+
19
+ ```swift
20
+ // withAnimation is shorthand for withTransaction
21
+ withAnimation(.default) { flag.toggle() }
22
+
23
+ // Equivalent explicit transaction
24
+ var transaction = Transaction(animation: .default)
25
+ withTransaction(transaction) { flag.toggle() }
26
+ ```
27
+
28
+ ### The .transaction Modifier
29
+
30
+ ```swift
31
+ Rectangle()
32
+ .frame(width: flag ? 100 : 50, height: 50)
33
+ .transaction { t in
34
+ t.animation = .default
35
+ }
36
+ ```
37
+
38
+ **Note:** This behaves like the deprecated `.animation(_:)` without value parameter - it animates on every state change.
39
+
40
+ ### Animation Precedence
41
+
42
+ **Implicit animations override explicit animations** (later in view tree wins).
43
+
44
+ ```swift
45
+ Button("Tap") {
46
+ withAnimation(.linear) { flag.toggle() }
47
+ }
48
+ .animation(.bouncy, value: flag) // .bouncy wins!
49
+ ```
50
+
51
+ ### Disabling Animations
52
+
53
+ ```swift
54
+ // Prevent implicit animations from overriding
55
+ .transaction { t in
56
+ t.disablesAnimations = true
57
+ }
58
+
59
+ // Remove animation entirely
60
+ .transaction { $0.animation = nil }
61
+ ```
62
+
63
+ ### Custom Transaction Keys (iOS 17+)
64
+
65
+ Pass metadata through transactions.
66
+
67
+ ```swift
68
+ struct ChangeSourceKey: TransactionKey {
69
+ static let defaultValue: String = "unknown"
70
+ }
71
+
72
+ extension Transaction {
73
+ var changeSource: String {
74
+ get { self[ChangeSourceKey.self] }
75
+ set { self[ChangeSourceKey.self] = newValue }
76
+ }
77
+ }
78
+
79
+ // Set source
80
+ var transaction = Transaction(animation: .default)
81
+ transaction.changeSource = "server"
82
+ withTransaction(transaction) { flag.toggle() }
83
+
84
+ // Read in view tree
85
+ .transaction { t in
86
+ if t.changeSource == "server" {
87
+ t.animation = .smooth
88
+ } else {
89
+ t.animation = .bouncy
90
+ }
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Phase Animations (iOS 17+)
97
+
98
+ Cycle through discrete phases automatically. Each phase change is a separate animation.
99
+
100
+ ### Basic Usage
101
+
102
+ ```swift
103
+ // GOOD - triggered phase animation
104
+ Button("Shake") { trigger += 1 }
105
+ .phaseAnimator(
106
+ [0.0, -10.0, 10.0, -5.0, 5.0, 0.0],
107
+ trigger: trigger
108
+ ) { content, offset in
109
+ content.offset(x: offset)
110
+ }
111
+
112
+ // Infinite loop (no trigger)
113
+ Circle()
114
+ .phaseAnimator([1.0, 1.2, 1.0]) { content, scale in
115
+ content.scaleEffect(scale)
116
+ }
117
+ ```
118
+
119
+ ### Enum Phases (Recommended for Clarity)
120
+
121
+ ```swift
122
+ // GOOD - enum phases are self-documenting
123
+ enum BouncePhase: CaseIterable {
124
+ case initial, up, down, settle
125
+
126
+ var scale: CGFloat {
127
+ switch self {
128
+ case .initial: 1.0
129
+ case .up: 1.2
130
+ case .down: 0.9
131
+ case .settle: 1.0
132
+ }
133
+ }
134
+ }
135
+
136
+ Circle()
137
+ .phaseAnimator(BouncePhase.allCases, trigger: trigger) { content, phase in
138
+ content.scaleEffect(phase.scale)
139
+ }
140
+ ```
141
+
142
+ ### Custom Timing Per Phase
143
+
144
+ ```swift
145
+ .phaseAnimator([0, -20, 20], trigger: trigger) { content, offset in
146
+ content.offset(x: offset)
147
+ } animation: { phase in
148
+ switch phase {
149
+ case -20: .bouncy
150
+ case 20: .linear
151
+ default: .smooth
152
+ }
153
+ }
154
+ ```
155
+
156
+ ### Good vs Bad
157
+
158
+ ```swift
159
+ // GOOD - use phaseAnimator for multi-step sequences
160
+ .phaseAnimator([0, -10, 10, 0], trigger: trigger) { content, offset in
161
+ content.offset(x: offset)
162
+ }
163
+
164
+ // BAD - manual DispatchQueue sequencing
165
+ Button("Animate") {
166
+ withAnimation(.easeOut(duration: 0.1)) { offset = -10 }
167
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
168
+ withAnimation { offset = 10 }
169
+ }
170
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
171
+ withAnimation { offset = 0 }
172
+ }
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Keyframe Animations (iOS 17+)
179
+
180
+ Precise timing control with exact values at specific times.
181
+
182
+ ### Basic Usage
183
+
184
+ ```swift
185
+ Button("Bounce") { trigger += 1 }
186
+ .keyframeAnimator(
187
+ initialValue: AnimationValues(),
188
+ trigger: trigger
189
+ ) { content, value in
190
+ content
191
+ .scaleEffect(value.scale)
192
+ .offset(y: value.verticalOffset)
193
+ } keyframes: { _ in
194
+ KeyframeTrack(\.scale) {
195
+ SpringKeyframe(1.2, duration: 0.15)
196
+ SpringKeyframe(0.9, duration: 0.1)
197
+ SpringKeyframe(1.0, duration: 0.15)
198
+ }
199
+ KeyframeTrack(\.verticalOffset) {
200
+ LinearKeyframe(-20, duration: 0.15)
201
+ LinearKeyframe(0, duration: 0.25)
202
+ }
203
+ }
204
+
205
+ struct AnimationValues {
206
+ var scale: CGFloat = 1.0
207
+ var verticalOffset: CGFloat = 0
208
+ }
209
+ ```
210
+
211
+ ### Keyframe Types
212
+
213
+ | Type | Behavior |
214
+ |------|----------|
215
+ | `CubicKeyframe` | Smooth interpolation |
216
+ | `LinearKeyframe` | Straight-line interpolation |
217
+ | `SpringKeyframe` | Spring physics |
218
+ | `MoveKeyframe` | Instant jump (no interpolation) |
219
+
220
+ ### Multiple Synchronized Tracks
221
+
222
+ Tracks run **in parallel**, each animating one property.
223
+
224
+ ```swift
225
+ // GOOD - bell shake with synchronized rotation and scale
226
+ struct BellAnimation {
227
+ var rotation: Double = 0
228
+ var scale: CGFloat = 1.0
229
+ }
230
+
231
+ Image(systemName: "bell.fill")
232
+ .keyframeAnimator(
233
+ initialValue: BellAnimation(),
234
+ trigger: trigger
235
+ ) { content, value in
236
+ content
237
+ .rotationEffect(.degrees(value.rotation))
238
+ .scaleEffect(value.scale)
239
+ } keyframes: { _ in
240
+ KeyframeTrack(\.rotation) {
241
+ CubicKeyframe(15, duration: 0.1)
242
+ CubicKeyframe(-15, duration: 0.1)
243
+ CubicKeyframe(10, duration: 0.1)
244
+ CubicKeyframe(-10, duration: 0.1)
245
+ CubicKeyframe(0, duration: 0.1)
246
+ }
247
+ KeyframeTrack(\.scale) {
248
+ CubicKeyframe(1.1, duration: 0.25)
249
+ CubicKeyframe(1.0, duration: 0.25)
250
+ }
251
+ }
252
+
253
+ // BAD - manual timer-based animation
254
+ Image(systemName: "bell.fill")
255
+ .onTapGesture {
256
+ withAnimation(.easeOut(duration: 0.1)) { rotation = 15 }
257
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
258
+ withAnimation { rotation = -15 }
259
+ }
260
+ // ... more manual timing - error prone
261
+ }
262
+ ```
263
+
264
+ ### KeyframeTimeline (iOS 17+)
265
+
266
+ Query animation values directly for testing or non-SwiftUI use.
267
+
268
+ ```swift
269
+ let timeline = KeyframeTimeline(initialValue: AnimationValues()) {
270
+ KeyframeTrack(\.scale) {
271
+ CubicKeyframe(1.2, duration: 0.25)
272
+ CubicKeyframe(1.0, duration: 0.25)
273
+ }
274
+ }
275
+
276
+ let midpoint = timeline.value(time: 0.25)
277
+ print(midpoint.scale) // Value at 0.25 seconds
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Animation Completion Handlers (iOS 17+)
283
+
284
+ Execute code when animations finish.
285
+
286
+ ### With withAnimation
287
+
288
+ ```swift
289
+ // GOOD - completion with withAnimation
290
+ Button("Animate") {
291
+ withAnimation(.spring) {
292
+ isExpanded.toggle()
293
+ } completion: {
294
+ showNextStep = true
295
+ }
296
+ }
297
+ ```
298
+
299
+ ### With Transaction (For Reexecution)
300
+
301
+ ```swift
302
+ // GOOD - completion fires on every trigger change
303
+ Circle()
304
+ .scaleEffect(bounceCount % 2 == 0 ? 1.0 : 1.2)
305
+ .transaction(value: bounceCount) { transaction in
306
+ transaction.animation = .spring
307
+ transaction.addAnimationCompletion {
308
+ message = "Bounce \(bounceCount) complete"
309
+ }
310
+ }
311
+
312
+ // BAD - completion only fires ONCE (no value parameter)
313
+ Circle()
314
+ .scaleEffect(bounceCount % 2 == 0 ? 1.0 : 1.2)
315
+ .animation(.spring, value: bounceCount)
316
+ .transaction { transaction in // No value!
317
+ transaction.addAnimationCompletion {
318
+ completionCount += 1 // Only fires once, ever
319
+ }
320
+ }
321
+ ```
322
+
323
+ ---
324
+
325
+ ## Quick Reference
326
+
327
+ ### Transactions (All iOS versions)
328
+ - `withTransaction` is the explicit form of `withAnimation`
329
+ - Implicit animations override explicit (later in view tree wins)
330
+ - Use `disablesAnimations` to prevent override
331
+ - Use `.transaction { $0.animation = nil }` to remove animation
332
+
333
+ ### Custom Transaction Keys (iOS 17+)
334
+ - Pass metadata through animation system via `TransactionKey`
335
+
336
+ ### Phase Animations (iOS 17+)
337
+ - Use for multi-step sequences returning to start
338
+ - Prefer enum phases for clarity
339
+ - Each phase change is a separate animation
340
+ - Use `trigger` parameter for one-shot animations
341
+
342
+ ### Keyframe Animations (iOS 17+)
343
+ - Use for precise timing control
344
+ - Tracks run in parallel
345
+ - Use `KeyframeTimeline` for testing/advanced use
346
+ - Prefer over manual DispatchQueue timing
347
+
348
+ ### Completion Handlers (iOS 17+)
349
+ - Use `withAnimation(.animation) { } completion: { }` for one-shot completion handlers
350
+ - Use `.transaction(value:)` for handlers that should refire on every value change
351
+ - Without `value:` parameter, completion only fires once