@mmerterden/multi-agent-pipeline 20.17.1 → 20.18.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/docs/facts.json +6 -6
  3. package/install/_mcp-register.mjs +1 -1
  4. package/install/_xcode-skills.mjs +263 -0
  5. package/install/catalog-history.json +1 -1
  6. package/install/claude.mjs +23 -0
  7. package/install/codex.mjs +11 -0
  8. package/install/copilot.mjs +9 -0
  9. package/manifest.json +85 -45
  10. package/package.json +1 -1
  11. package/pipeline/commands/multi-agent/stack/SKILL.md +2 -0
  12. package/pipeline/lib/xcode-plugin.mjs +131 -0
  13. package/pipeline/multi-agent-refs/features/doctor.md +10 -0
  14. package/pipeline/scripts/doctor.mjs +38 -0
  15. package/pipeline/scripts/uninstall.mjs +57 -1
  16. package/pipeline/skills/.skill-manifest.json +33 -13
  17. package/pipeline/skills/.skills-index.json +66 -11
  18. package/pipeline/skills/shared/README.md +18 -13
  19. package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +3 -1
  20. package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +2 -1
  21. package/pipeline/skills/shared/external/hig-app-icons/SKILL.md +159 -0
  22. package/pipeline/skills/shared/external/hig-app-icons/evals/evals.json +48 -0
  23. package/pipeline/skills/shared/external/hig-app-icons/evals/trigger-eval.json +22 -0
  24. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +115 -0
  25. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +32 -4
  26. package/pipeline/skills/shared/external/hig-components-layout/evals/evals.json +47 -0
  27. package/pipeline/skills/shared/external/hig-components-layout/evals/trigger-eval.json +22 -0
  28. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +231 -0
  29. package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +5 -30
  30. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
  31. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +23 -4
  32. package/pipeline/skills/shared/external/hig-foundations/evals/evals.json +47 -0
  33. package/pipeline/skills/shared/external/hig-foundations/evals/trigger-eval.json +21 -0
  34. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +34 -26
  35. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +219 -0
  36. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +5 -4
  37. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +19 -3
  38. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +188 -0
  39. package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +5 -11
  40. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +17 -5
  41. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +218 -0
  42. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +4 -1
  43. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +135 -0
  44. package/pipeline/skills/shared/external/ios-design-review/SKILL.md +146 -0
  45. package/pipeline/skills/shared/external/ios-design-review/evals/evals.json +48 -0
  46. package/pipeline/skills/shared/external/ios-design-review/evals/trigger-eval.json +22 -0
  47. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +135 -0
  48. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +193 -0
  49. package/pipeline/skills/shared/external/ios-haptics/SKILL.md +191 -0
  50. package/pipeline/skills/shared/external/ios-haptics/evals/evals.json +49 -0
  51. package/pipeline/skills/shared/external/ios-haptics/evals/trigger-eval.json +22 -0
  52. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +215 -0
  53. package/pipeline/skills/shared/external/ios-iphone-duo/NOTICE.md +53 -0
  54. package/pipeline/skills/shared/external/ios-iphone-duo/SKILL.md +179 -0
  55. package/pipeline/skills/shared/external/ios-iphone-duo/evals/evals.json +65 -0
  56. package/pipeline/skills/shared/external/ios-iphone-duo/evals/trigger-eval.json +22 -0
  57. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +212 -0
  58. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +202 -0
  59. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +122 -0
  60. package/pipeline/skills/shared/external/ios-sf-symbols/SKILL.md +177 -0
  61. package/pipeline/skills/shared/external/ios-sf-symbols/evals/evals.json +49 -0
  62. package/pipeline/skills/shared/external/ios-sf-symbols/evals/trigger-eval.json +22 -0
  63. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +135 -0
  64. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +38 -50
  65. package/pipeline/skills/shared/external/swiftui-animation/evals/evals.json +47 -0
  66. package/pipeline/skills/shared/external/swiftui-animation/evals/trigger-eval.json +22 -0
  67. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +7 -73
  68. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +185 -0
  69. package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +6 -6
  70. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +3 -253
  71. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +4 -47
  72. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +3 -111
  73. package/pipeline/skills/shared/external/swiftui-expert-skill/references/liquid-glass.md +11 -381
  74. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +184 -224
  75. package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/evals.json +47 -0
  76. package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/trigger-eval.json +22 -0
  77. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +246 -33
  78. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +227 -0
  79. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +15 -18
  80. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/components-index.md +1 -1
  81. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/haptics.md +6 -68
  82. package/pipeline/skills/skills-index.md +16 -11
  83. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +0 -786
@@ -1,8 +1,8 @@
1
1
  # Advanced SwiftUI Animation
2
2
 
3
3
  Depth behind the main skill: custom curves, the full `Spring` API, unit
4
- curves, richer phase and keyframe patterns, transactions, every transition and
5
- symbol effect, Reduce Motion handling and performance.
4
+ curves, richer phase and keyframe patterns, transactions, every transition, Reduce
5
+ Motion handling and performance. Symbol effects are `ios-sf-symbols`.
6
6
 
7
7
  ## Contents
8
8
 
@@ -14,7 +14,7 @@ symbol effect, Reduce Motion handling and performance.
14
14
  - [Transaction and TransactionKey](#transaction-and-transactionkey)
15
15
  - [Choosing an implicit scope](#choosing-an-implicit-scope)
16
16
  - [Every transition (iOS 17+)](#every-transition-ios-17)
17
- - [Every symbol effect](#every-symbol-effect)
17
+ - [Symbol effects](#symbol-effects)
18
18
  - [Reduce Motion](#reduce-motion)
19
19
  - [Performance](#performance)
20
20
 
@@ -445,77 +445,11 @@ Where an API still wants the older type-erased value, wrap it:
445
445
  )
446
446
  ```
447
447
 
448
- ## Every symbol effect
448
+ ## Symbol effects
449
449
 
450
- ### Discrete, triggered by `value:`
451
-
452
- - `.bounce`: scopes `.byLayer` and `.wholeSymbol`; no direction.
453
- - `.wiggle` (iOS 18): scopes, plus directions `.up`, `.down`, `.left`,
454
- `.right`, `.forward`, `.backward`, `.clockwise`, `.counterClockwise`,
455
- `.custom(angle:)`.
456
-
457
- ```swift
458
- Image(systemName: "tray.and.arrow.down.fill").symbolEffect(.bounce.byLayer, value: downloads)
459
- Image(systemName: "hand.wave.fill").symbolEffect(.wiggle.left, value: greetings)
460
- ```
461
-
462
- ### Indefinite, controlled by `isActive:`
463
-
464
- - `.pulse`
465
- - `.variableColor`: chain `.cumulative` or `.iterative`, `.reversing` or
466
- `.nonReversing`, `.dimInactiveLayers` or `.hideInactiveLayers`.
467
- - `.scale`: `.up` or `.down`.
468
- - `.breathe` (iOS 18)
469
- - `.rotate` (iOS 18): `.clockwise` or `.counterClockwise`.
470
-
471
- All of them take `.byLayer` or `.wholeSymbol`.
472
-
473
- ```swift
474
- Image(systemName: "antenna.radiowaves.left.and.right").symbolEffect(.pulse.byLayer, isActive: isLive)
475
- Image(systemName: "gearshape.fill").symbolEffect(.rotate.clockwise, isActive: isSyncing)
476
- Image(systemName: "chart.bar.fill")
477
- .symbolEffect(.variableColor.cumulative.nonReversing.dimInactiveLayers,
478
- options: .repeating, isActive: isLoading)
479
- Image(systemName: "plus.circle").symbolEffect(.scale.up, isActive: isHighlighted)
480
- Image(systemName: "moon.zzz.fill").symbolEffect(.breathe, isActive: isSleeping)
481
- ```
482
-
483
- ### Appear and disappear
484
-
485
- ```swift
486
- Image(systemName: "checkmark.seal.fill").symbolEffect(.appear, isActive: isVerified)
487
- Image(systemName: "exclamationmark.triangle").symbolEffect(.disappear, isActive: isResolved)
488
- ```
489
-
490
- ### Replace
491
-
492
- Replacement runs as a content transition:
493
-
494
- ```swift
495
- Image(systemName: isLocked ? "lock.fill" : "lock.open.fill")
496
- .contentTransition(.symbolEffect(.replace.downUp))
497
-
498
- Image(systemName: isRecording ? "stop.circle.fill" : "record.circle")
499
- .contentTransition(.symbolEffect(.replace.magic(fallback: .offUp))) // iOS 18
500
- ```
501
-
502
- Directions: `.downUp`, `.offUp`, `.upUp`. `.magic` morphs shared parts of the
503
- two symbols and falls back to the given direction when it cannot.
504
-
505
- ### Options
506
-
507
- ```swift
508
- .symbolEffect(.bounce, options: .default, value: n)
509
- .symbolEffect(.bounce, options: .repeating, value: n)
510
- .symbolEffect(.bounce, options: .nonRepeating, value: n)
511
- .symbolEffect(.bounce, options: .repeat(2), value: n)
512
- .symbolEffect(.bounce, options: .speed(2.0), value: n)
513
- .symbolEffect(.breathe, options: .repeat(.periodic(4, delay: 1.0)), isActive: on) // iOS 18
514
- .symbolEffect(.breathe, options: .repeat(.continuous), isActive: on) // iOS 18
515
- ```
516
-
517
- `.symbolEffectsRemoved(_:)` strips symbol effects from a subtree, for example
518
- `.symbolEffectsRemoved(reduceMotion)`.
450
+ Symbol effects, their options and `.symbolEffectsRemoved(_:)` are in
451
+ `ios-sf-symbols`. A symbol swap in place is a content transition:
452
+ `.contentTransition(.symbolEffect(.replace))`.
519
453
 
520
454
  ## Reduce Motion
521
455
 
@@ -0,0 +1,185 @@
1
+ # Scroll-driven effects: scrollTransition and visualEffect
2
+
3
+ Companion to `../SKILL.md` for the `swiftui-animation` skill. Both APIs ship
4
+ in iOS 17 (macOS 14, tvOS 17, watchOS 10, visionOS 1).
5
+
6
+ ## Contents
7
+
8
+ 1. [Which one](#1-which-one)
9
+ 2. [scrollTransition](#2-scrolltransition)
10
+ 3. [Configurations and thresholds](#3-configurations-and-thresholds)
11
+ 4. [visualEffect](#4-visualeffect)
12
+ 5. [What a VisualEffect can do](#5-what-a-visualeffect-can-do)
13
+ 6. [Concurrency: the closures are Sendable](#6-concurrency-the-closures-are-sendable)
14
+ 7. [Reduce Motion](#7-reduce-motion)
15
+ 8. [Mistakes](#8-mistakes)
16
+
17
+ ## 1. Which one
18
+
19
+ | Need | API |
20
+ |---|---|
21
+ | Items fade, scale or tilt as they enter and leave the visible area of a scroll view | `scrollTransition(_:axis:transition:)` |
22
+ | An effect that is a continuous function of the view's position or size (parallax, a header that shrinks, a stretch at the top) | `visualEffect(_:)` with the `GeometryProxy` |
23
+ | React to scroll position in state (show a button after 300 pt) | `onScrollGeometryChange(for:of:action:)` (iOS 18), not an effect |
24
+
25
+ Both apply effects that do not change layout, so they are cheap to run every
26
+ frame and safe inside lazy stacks.
27
+
28
+ ## 2. scrollTransition
29
+
30
+ ```swift
31
+ ScrollView {
32
+ LazyVStack(spacing: 16) {
33
+ ForEach(destinations) { destination in
34
+ DestinationCard(destination: destination)
35
+ .scrollTransition { content, phase in
36
+ content
37
+ .opacity(phase.isIdentity ? 1 : 0.4)
38
+ .scaleEffect(phase.isIdentity ? 1 : 0.92)
39
+ }
40
+ }
41
+ }
42
+ .padding(.horizontal)
43
+ }
44
+ ```
45
+
46
+ `ScrollTransitionPhase` has three cases:
47
+
48
+ - `.topLeading`: the view is entering or leaving at the top (vertical) or
49
+ leading (horizontal) edge.
50
+ - `.identity`: the view is in the visible region.
51
+ - `.bottomTrailing`: entering or leaving at the bottom or trailing edge.
52
+
53
+ `phase.value` is -1 at `.topLeading`, 0 at `.identity` and 1 at
54
+ `.bottomTrailing`, interpolated in between, so effects can scale with it:
55
+
56
+ ```swift
57
+ .scrollTransition(axis: .horizontal) { content, phase in
58
+ content
59
+ .rotation3DEffect(.degrees(phase.value * -25), axis: (x: 0, y: 1, z: 0))
60
+ .offset(x: phase.value * -20)
61
+ }
62
+ ```
63
+
64
+ In the `.identity` phase the transition should leave the view unchanged,
65
+ because whatever it applies there stays on for as long as the view is visible.
66
+
67
+ Different behaviour per edge:
68
+
69
+ ```swift
70
+ .scrollTransition(topLeading: .interactive, bottomTrailing: .animated(.smooth)) { content, phase in
71
+ content.opacity(phase.isIdentity ? 1 : 0)
72
+ }
73
+ ```
74
+
75
+ ## 3. Configurations and thresholds
76
+
77
+ | `ScrollTransitionConfiguration` | Behaviour |
78
+ |---|---|
79
+ | `.interactive` (default) | Tracks the scroll position directly |
80
+ | `.interactive(timingCurve:)` | Same, with a `UnitCurve` shaping the progress |
81
+ | `.animated`, `.animated(_:)` | Animates to the new phase when the threshold is crossed |
82
+ | `.identity` | No transition |
83
+
84
+ `.threshold(_:)` decides when a view counts as visible:
85
+ `.visible` (any part visible), `.visible(0.9)` (90% visible), `.centered`,
86
+ `.hidden`, and `.inset(by:)` or `.interpolated(towards:amount:)` to adjust.
87
+
88
+ ```swift
89
+ .scrollTransition(.interactive.threshold(.visible(0.9))) { content, phase in
90
+ content.blur(radius: phase.isIdentity ? 0 : 6)
91
+ }
92
+ ```
93
+
94
+ ## 4. visualEffect
95
+
96
+ `visualEffect(_:)` hands the closure an empty effect and the view's
97
+ `GeometryProxy`. Read position in the scroll view's coordinate space
98
+ (`.scrollView`, iOS 17) and return effects:
99
+
100
+ ```swift
101
+ struct ParallaxHeader: View {
102
+ let image: Image
103
+
104
+ var body: some View {
105
+ image
106
+ .resizable()
107
+ .scaledToFill()
108
+ .frame(height: 280)
109
+ .clipped()
110
+ .visualEffect { content, proxy in
111
+ let minY = proxy.frame(in: .scrollView).minY
112
+ let stretch = minY > 0 ? 1 + minY / 600 : 1
113
+ return content
114
+ .offset(y: minY > 0 ? -minY / 2 : 0)
115
+ .scaleEffect(x: stretch, y: stretch, anchor: .bottom)
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ The same pattern fades a title as it reaches the top bar, or dims rows that
122
+ sit under a floating control.
123
+
124
+ ## 5. What a VisualEffect can do
125
+
126
+ Effects returned from either closure: `offset`, `scaleEffect`,
127
+ `rotationEffect`, `rotation3DEffect`, `opacity`, `blur(radius:opaque:)`,
128
+ `brightness`, `contrast`, `saturation`, `grayscale`, `hueRotation`,
129
+ `blendMode`, and the shader effects `colorEffect`, `distortionEffect` and
130
+ `layerEffect`. Frames, padding and other layout modifiers are not available,
131
+ by design.
132
+
133
+ ## 6. Concurrency: the closures are Sendable
134
+
135
+ Both closures are `@Sendable`. Do not read main-actor state or non-Sendable
136
+ properties inside them; capture plain values first:
137
+
138
+ ```swift
139
+ let strength = isEmphasized ? 1.0 : 0.5
140
+ Card()
141
+ .scrollTransition { content, phase in
142
+ content.scaleEffect(1 - abs(phase.value) * 0.1 * strength)
143
+ }
144
+ ```
145
+
146
+ ## 7. Reduce Motion
147
+
148
+ Scroll-linked motion is still motion. With
149
+ `@Environment(\.accessibilityReduceMotion)` set, keep opacity changes and drop
150
+ movement, rotation and parallax, or pass `.identity`:
151
+
152
+ ```swift
153
+ @Environment(\.accessibilityReduceMotion) private var reduceMotion
154
+
155
+ var body: some View {
156
+ let configuration: ScrollTransitionConfiguration = reduceMotion ? .identity : .interactive
157
+ PosterCard(poster: poster)
158
+ .scrollTransition(configuration) { content, phase in
159
+ content
160
+ .opacity(phase.isIdentity ? 1 : 0.5)
161
+ .scaleEffect(phase.isIdentity ? 1 : 0.9)
162
+ }
163
+ }
164
+ ```
165
+
166
+ For `visualEffect` parallax, return `content` unchanged when the setting is on;
167
+ capture `reduceMotion` in a local constant before the closure (see section 6).
168
+
169
+ ## 8. Mistakes
170
+
171
+ 1. **Layout work in the closure.** It runs every frame; compute constants
172
+ outside.
173
+ 2. **A visible change in the `.identity` phase.** It stays on permanently.
174
+ 3. **Reaching for `GeometryReader` plus `@State` offsets** to build parallax.
175
+ `visualEffect` does it without invalidating the view on every scroll tick.
176
+ 4. **Effects that hide content or make text unreadable** at rest; transitions
177
+ belong to the edges.
178
+ 5. **Ignoring Reduce Motion** for 3D rotation and parallax.
179
+
180
+ Sources:
181
+ [scrollTransition(_:axis:transition:)](https://developer.apple.com/documentation/swiftui/view/scrolltransition(_:axis:transition:)),
182
+ [ScrollTransitionPhase](https://developer.apple.com/documentation/swiftui/scrolltransitionphase),
183
+ [ScrollTransitionConfiguration](https://developer.apple.com/documentation/swiftui/scrolltransitionconfiguration),
184
+ [visualEffect(_:)](https://developer.apple.com/documentation/swiftui/view/visualeffect(_:)),
185
+ [VisualEffect](https://developer.apple.com/documentation/swiftui/visualeffect).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: swiftui-expert-skill
3
- description: "Write, review, or improve SwiftUI code following best practices for state management, view composition, performance, macOS-specific APIs, and iOS 26+ Liquid Glass adoption. Use when building new SwiftUI features, refactoring existing views, reviewing code quality, or adopting modern SwiftUI patterns; for a findings-only review use swiftui-pro."
3
+ description: "Write, review, or improve SwiftUI code following best practices for state, view composition, performance, macOS APIs, and iOS 26+ Liquid Glass adoption. Use when building SwiftUI features, refactoring views, reviewing code, or adopting modern patterns; for a findings-only review use swiftui-pro; for Liquid Glass APIs use swiftui-liquid-glass."
4
4
  metadata:
5
5
  source: "AvdLee/SwiftUI-Agent-Skill (MIT)"
6
6
  ---
@@ -14,7 +14,7 @@ metadata:
14
14
  - Focus on correctness and performance; do not enforce specific architectures (MVVM, VIPER, etc.)
15
15
  - Encourage separating business logic from views for testability without mandating how
16
16
  - Follow Apple's Human Interface Guidelines and API design patterns
17
- - Only adopt Liquid Glass when explicitly requested by the user (see `references/liquid-glass.md`)
17
+ - Only adopt Liquid Glass when explicitly requested by the user (see `references/liquid-glass.md`; the API itself is in the `swiftui-liquid-glass` skill)
18
18
  - Present performance optimizations as suggestions, not requirements
19
19
  - Use `#available` gating with sensible fallbacks for version-specific APIs
20
20
 
@@ -60,7 +60,7 @@ Consult the reference file for each topic relevant to the current task:
60
60
  | Swift Charts | `references/charts.md` |
61
61
  | Charts accessibility | `references/charts-accessibility.md` |
62
62
  | Image optimization | `references/image-optimization.md` |
63
- | Liquid Glass (iOS 26+) | `references/liquid-glass.md` |
63
+ | Liquid Glass (iOS 26+) | `references/liquid-glass.md`, then the `swiftui-liquid-glass` skill |
64
64
  | macOS scenes | `references/macos-scenes.md` |
65
65
  | macOS window styling | `references/macos-window-styling.md` |
66
66
  | macOS views | `references/macos-views.md` |
@@ -91,14 +91,14 @@ These are hard rules -- violations are always bugs:
91
91
  - `references/layout-best-practices.md` -- Layout patterns, GeometryReader alternatives
92
92
  - `references/accessibility-patterns.md` -- VoiceOver, Dynamic Type, grouping, traits
93
93
  - `references/animation-basics.md` -- Implicit/explicit animations, timing, performance
94
- - `references/animation-transitions.md` -- View transitions, `matchedGeometryEffect`, `Animatable`
95
- - `references/animation-advanced.md` -- Phase/keyframe animations (iOS 17+), `@Animatable` macro (iOS 26+)
94
+ - `references/animation-transitions.md` -- Transition animation context, identity, `Animatable` (transition catalog: `swiftui-animation`)
95
+ - `references/animation-advanced.md` -- Transactions, completion handlers (phase, keyframe and `@Animatable`: `swiftui-animation`)
96
96
  - `references/charts.md` -- Swift Charts marks, axes, selection, styling, Chart3D (iOS 26+)
97
97
  - `references/charts-accessibility.md` -- Charts VoiceOver, Audio Graph, fallback strategies
98
98
  - `references/sheet-navigation-patterns.md` -- Sheets, NavigationSplitView, Inspector
99
99
  - `references/scroll-patterns.md` -- ScrollViewReader, programmatic scrolling
100
100
  - `references/image-optimization.md` -- AsyncImage, downsampling, caching
101
- - `references/liquid-glass.md` -- iOS 26+ Liquid Glass effects and fallback patterns
101
+ - `references/liquid-glass.md` -- When to adopt Liquid Glass; the API, fallbacks and checklist live in `swiftui-liquid-glass`
102
102
  - `references/macos-scenes.md` -- Settings, MenuBarExtra, WindowGroup, multi-window
103
103
  - `references/macos-window-styling.md` -- Toolbar styles, window sizing, Commands
104
104
  - `references/macos-views.md` -- HSplitView, Table, PasteButton, AppKit interop
@@ -1,13 +1,12 @@
1
1
  # SwiftUI Advanced Animations
2
2
 
3
- Transactions, phase animations (iOS 17+), keyframe animations (iOS 17+), completion handlers (iOS 17+), and `@Animatable` macro (iOS 26+).
3
+ Transactions and completion handlers (iOS 17+).
4
+
5
+ > Local trim: phase animations, keyframe animations and the `@Animatable` macro are maintained in the `swiftui-animation` skill, which owns SwiftUI motion. This file keeps transactions and completion handlers.
4
6
 
5
7
  ## Table of Contents
6
8
  - [Transactions](#transactions)
7
- - [Phase Animations (iOS 17+)](#phase-animations-ios-17)
8
- - [Keyframe Animations (iOS 17+)](#keyframe-animations-ios-17)
9
9
  - [Animation Completion Handlers (iOS 17+)](#animation-completion-handlers-ios-17)
10
- - [@Animatable Macro (iOS 26+)](#animatable-macro-ios-26)
11
10
 
12
11
  ---
13
12
 
@@ -94,192 +93,6 @@ withTransaction(transaction) { flag.toggle() }
94
93
 
95
94
  ---
96
95
 
97
- ## Phase Animations (iOS 17+)
98
-
99
- Cycle through discrete phases automatically. Each phase change is a separate animation.
100
-
101
- ### Basic Usage
102
-
103
- ```swift
104
- // GOOD - triggered phase animation
105
- Button("Shake") { trigger += 1 }
106
- .phaseAnimator(
107
- [0.0, -10.0, 10.0, -5.0, 5.0, 0.0],
108
- trigger: trigger
109
- ) { content, offset in
110
- content.offset(x: offset)
111
- }
112
-
113
- // Infinite loop (no trigger)
114
- Circle()
115
- .phaseAnimator([1.0, 1.2, 1.0]) { content, scale in
116
- content.scaleEffect(scale)
117
- }
118
- ```
119
-
120
- ### Enum Phases (Recommended for Clarity)
121
-
122
- ```swift
123
- // GOOD - enum phases are self-documenting
124
- enum BouncePhase: CaseIterable {
125
- case initial, up, down, settle
126
-
127
- var scale: CGFloat {
128
- switch self {
129
- case .initial: 1.0
130
- case .up: 1.2
131
- case .down: 0.9
132
- case .settle: 1.0
133
- }
134
- }
135
- }
136
-
137
- Circle()
138
- .phaseAnimator(BouncePhase.allCases, trigger: trigger) { content, phase in
139
- content.scaleEffect(phase.scale)
140
- }
141
- ```
142
-
143
- ### Custom Timing Per Phase
144
-
145
- ```swift
146
- .phaseAnimator([0, -20, 20], trigger: trigger) { content, offset in
147
- content.offset(x: offset)
148
- } animation: { phase in
149
- switch phase {
150
- case -20: .bouncy
151
- case 20: .linear
152
- default: .smooth
153
- }
154
- }
155
- ```
156
-
157
- ### Good vs Bad
158
-
159
- ```swift
160
- // GOOD - use phaseAnimator for multi-step sequences
161
- .phaseAnimator([0, -10, 10, 0], trigger: trigger) { content, offset in
162
- content.offset(x: offset)
163
- }
164
-
165
- // BAD - manual DispatchQueue sequencing
166
- Button("Animate") {
167
- withAnimation(.easeOut(duration: 0.1)) { offset = -10 }
168
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
169
- withAnimation { offset = 10 }
170
- }
171
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
172
- withAnimation { offset = 0 }
173
- }
174
- }
175
- ```
176
-
177
- ---
178
-
179
- ## Keyframe Animations (iOS 17+)
180
-
181
- Precise timing control with exact values at specific times.
182
-
183
- ### Basic Usage
184
-
185
- ```swift
186
- Button("Bounce") { trigger += 1 }
187
- .keyframeAnimator(
188
- initialValue: AnimationValues(),
189
- trigger: trigger
190
- ) { content, value in
191
- content
192
- .scaleEffect(value.scale)
193
- .offset(y: value.verticalOffset)
194
- } keyframes: { _ in
195
- KeyframeTrack(\.scale) {
196
- SpringKeyframe(1.2, duration: 0.15)
197
- SpringKeyframe(0.9, duration: 0.1)
198
- SpringKeyframe(1.0, duration: 0.15)
199
- }
200
- KeyframeTrack(\.verticalOffset) {
201
- LinearKeyframe(-20, duration: 0.15)
202
- LinearKeyframe(0, duration: 0.25)
203
- }
204
- }
205
-
206
- struct AnimationValues {
207
- var scale: CGFloat = 1.0
208
- var verticalOffset: CGFloat = 0
209
- }
210
- ```
211
-
212
- ### Keyframe Types
213
-
214
- | Type | Behavior |
215
- |------|----------|
216
- | `CubicKeyframe` | Smooth interpolation |
217
- | `LinearKeyframe` | Straight-line interpolation |
218
- | `SpringKeyframe` | Spring physics |
219
- | `MoveKeyframe` | Instant jump (no interpolation) |
220
-
221
- ### Multiple Synchronized Tracks
222
-
223
- Tracks run **in parallel**, each animating one property.
224
-
225
- ```swift
226
- // GOOD - bell shake with synchronized rotation and scale
227
- struct BellAnimation {
228
- var rotation: Double = 0
229
- var scale: CGFloat = 1.0
230
- }
231
-
232
- Image(systemName: "bell.fill")
233
- .keyframeAnimator(
234
- initialValue: BellAnimation(),
235
- trigger: trigger
236
- ) { content, value in
237
- content
238
- .rotationEffect(.degrees(value.rotation))
239
- .scaleEffect(value.scale)
240
- } keyframes: { _ in
241
- KeyframeTrack(\.rotation) {
242
- CubicKeyframe(15, duration: 0.1)
243
- CubicKeyframe(-15, duration: 0.1)
244
- CubicKeyframe(10, duration: 0.1)
245
- CubicKeyframe(-10, duration: 0.1)
246
- CubicKeyframe(0, duration: 0.1)
247
- }
248
- KeyframeTrack(\.scale) {
249
- CubicKeyframe(1.1, duration: 0.25)
250
- CubicKeyframe(1.0, duration: 0.25)
251
- }
252
- }
253
-
254
- // BAD - manual timer-based animation
255
- Image(systemName: "bell.fill")
256
- .onTapGesture {
257
- withAnimation(.easeOut(duration: 0.1)) { rotation = 15 }
258
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
259
- withAnimation { rotation = -15 }
260
- }
261
- // ... more manual timing - error prone
262
- }
263
- ```
264
-
265
- ### KeyframeTimeline (iOS 17+)
266
-
267
- Query animation values directly for testing or non-SwiftUI use.
268
-
269
- ```swift
270
- let timeline = KeyframeTimeline(initialValue: AnimationValues()) {
271
- KeyframeTrack(\.scale) {
272
- CubicKeyframe(1.2, duration: 0.25)
273
- CubicKeyframe(1.0, duration: 0.25)
274
- }
275
- }
276
-
277
- let midpoint = timeline.value(time: 0.25)
278
- print(midpoint.scale) // Value at 0.25 seconds
279
- ```
280
-
281
- ---
282
-
283
96
  ## Animation Completion Handlers (iOS 17+)
284
97
 
285
98
  Execute code when animations finish.
@@ -323,52 +136,6 @@ Circle()
323
136
 
324
137
  ---
325
138
 
326
- ## @Animatable Macro (iOS 26+)
327
-
328
- The `@Animatable` macro auto-synthesizes `animatableData` from all animatable stored properties, eliminating verbose manual conformance. Use `@AnimatableIgnored` to exclude properties that should not animate.
329
-
330
- ### Before (Manual)
331
-
332
- ```swift
333
- struct Wedge: Shape {
334
- var startAngle: Angle
335
- var endAngle: Angle
336
- var drawClockwise: Bool
337
-
338
- var animatableData: AnimatablePair<Double, Double> {
339
- get { AnimatablePair(startAngle.radians, endAngle.radians) }
340
- set {
341
- startAngle = .radians(newValue.first)
342
- endAngle = .radians(newValue.second)
343
- }
344
- }
345
-
346
- func path(in rect: CGRect) -> Path { /* ... */ }
347
- }
348
- ```
349
-
350
- ### After (@Animatable)
351
-
352
- ```swift
353
- @Animatable
354
- struct Wedge: Shape {
355
- var startAngle: Angle
356
- var endAngle: Angle
357
- @AnimatableIgnored var drawClockwise: Bool
358
-
359
- func path(in rect: CGRect) -> Path { /* ... */ }
360
- }
361
- ```
362
-
363
- ### When to Use
364
- - **Prefer `@Animatable`** for any custom `Shape`, `AnimatableModifier`, or type conforming to `Animatable` with multiple properties
365
- - **Use `@AnimatableIgnored`** for properties that control behavior but should not interpolate (e.g., directions, flags, identifiers)
366
- - The macro works with any type conforming to `Animatable`, not just `Shape`
367
-
368
- > Source: "What's new in SwiftUI" (WWDC25, session 256)
369
-
370
- ---
371
-
372
139
  ## Quick Reference
373
140
 
374
141
  ### Transactions (All iOS versions)
@@ -380,24 +147,7 @@ struct Wedge: Shape {
380
147
  ### Custom Transaction Keys (iOS 17+)
381
148
  - Pass metadata through animation system via `TransactionKey`
382
149
 
383
- ### Phase Animations (iOS 17+)
384
- - Use for multi-step sequences returning to start
385
- - Prefer enum phases for clarity
386
- - Each phase change is a separate animation
387
- - Use `trigger` parameter for one-shot animations
388
-
389
- ### Keyframe Animations (iOS 17+)
390
- - Use for precise timing control
391
- - Tracks run in parallel
392
- - Use `KeyframeTimeline` for testing/advanced use
393
- - Prefer over manual DispatchQueue timing
394
-
395
150
  ### Completion Handlers (iOS 17+)
396
151
  - Use `withAnimation(.animation) { } completion: { }` for one-shot completion handlers
397
152
  - Use `.transaction(value:)` for handlers that should refire on every value change
398
153
  - Without `value:` parameter, completion only fires once
399
-
400
- ### @Animatable Macro (iOS 26+)
401
- - Use `@Animatable` to auto-synthesize `animatableData` from stored properties
402
- - Use `@AnimatableIgnored` to exclude non-animatable properties
403
- - Replaces verbose manual `animatableData` getters/setters
@@ -1,6 +1,8 @@
1
1
  # SwiftUI Animation Basics
2
2
 
3
- Core animation concepts, implicit vs explicit animations, timing curves, and performance patterns.
3
+ Core animation concepts, implicit vs explicit animations, and performance patterns.
4
+
5
+ > Local trim: Timing curves and springs are maintained in the `swiftui-animation` skill, which owns SwiftUI motion. This file keeps placement, selective animation, performance and debugging.
4
6
 
5
7
  ## Table of Contents
6
8
  - [Core Concepts](#core-concepts)
@@ -8,7 +10,6 @@ Core animation concepts, implicit vs explicit animations, timing curves, and per
8
10
  - [Explicit Animations](#explicit-animations)
9
11
  - [Animation Placement](#animation-placement)
10
12
  - [Selective Animation](#selective-animation)
11
- - [Timing Curves](#timing-curves)
12
13
  - [Animation Performance](#animation-performance)
13
14
  - [Disabling Animations](#disabling-animations)
14
15
  - [Debugging](#debugging)
@@ -115,50 +116,6 @@ Rectangle()
115
116
 
116
117
  ---
117
118
 
118
- ## Timing Curves
119
-
120
- ### Built-in Curves
121
-
122
- | Curve | Use Case |
123
- |-------|----------|
124
- | `.spring` | Interactive elements, most UI |
125
- | `.easeInOut` | Appearance changes |
126
- | `.bouncy` | Playful feedback (iOS 17+) |
127
- | `.linear` | Progress indicators only |
128
-
129
- ### Modifiers
130
-
131
- ```swift
132
- .animation(.default.speed(2.0), value: flag) // 2x faster
133
- .animation(.default.delay(0.5), value: flag) // Delayed start
134
- .animation(.default.repeatCount(3, autoreverses: true), value: flag)
135
- ```
136
-
137
- ### Good vs Bad Timing
138
-
139
- ```swift
140
- // GOOD - appropriate timing for interaction type
141
- Button("Tap") {
142
- withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
143
- isActive.toggle()
144
- }
145
- }
146
- .scaleEffect(isActive ? 0.95 : 1.0)
147
-
148
- // BAD - too slow for button feedback
149
- Button("Tap") {
150
- withAnimation(.easeInOut(duration: 1.0)) { // Way too slow!
151
- isActive.toggle()
152
- }
153
- }
154
-
155
- // BAD - linear feels robotic
156
- Rectangle()
157
- .animation(.linear(duration: 0.5), value: isActive) // Mechanical
158
- ```
159
-
160
- ---
161
-
162
119
  ## Animation Performance
163
120
 
164
121
  ### Prefer Transforms Over Layout
@@ -274,7 +231,7 @@ struct AnimationDebugModifier: ViewModifier, Animatable {
274
231
  - Use `withAnimation` for event-driven animations
275
232
  - Prefer transforms over layout changes
276
233
  - Scope animations narrowly
277
- - Choose appropriate timing curves
234
+ - Choose appropriate timing curves (see `swiftui-animation`)
278
235
 
279
236
  ### Don't
280
237
  - Use deprecated `.animation(_:)` without value