@mmerterden/multi-agent-pipeline 16.18.0 → 16.19.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 (200) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +5 -5
  3. package/README.tr.md +2 -2
  4. package/docs/FIGMA_PIPELINE.md +1 -1
  5. package/docs/adr/0001-three-model-triage.md +4 -2
  6. package/docs/architecture.md +2 -2
  7. package/docs/ecosystem.md +13 -11
  8. package/docs/features.md +2 -2
  9. package/index.js +1 -1
  10. package/install/_common.mjs +25 -1
  11. package/install/_dev-only-files.mjs +2 -2
  12. package/install/_mcp-register.mjs +4 -3
  13. package/install/_plugin-skills.mjs +1 -3
  14. package/install/copilot.mjs +18 -9
  15. package/install/index.mjs +2 -4
  16. package/install/templates/copilot-instructions.md +7 -7
  17. package/package.json +1 -1
  18. package/pipeline/claude-md-template.md +2 -2
  19. package/pipeline/commands/multi-agent/SKILL.md +5 -5
  20. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +1 -1
  21. package/pipeline/commands/multi-agent/design-check/SKILL.md +1 -1
  22. package/pipeline/commands/multi-agent/help/SKILL.md +2 -2
  23. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
  24. package/pipeline/commands/multi-agent/review/SKILL.md +27 -14
  25. package/pipeline/commands/multi-agent/review-analysis/SKILL.md +1 -1
  26. package/pipeline/commands/multi-agent/store-ready/SKILL.md +2 -2
  27. package/pipeline/commands/multi-agent/sync/SKILL.md +2 -2
  28. package/pipeline/commands/sim-test.md +54 -15
  29. package/pipeline/lib/credential-inventory.sh +15 -2
  30. package/pipeline/lib/credential-store-resolver.sh +14 -4
  31. package/pipeline/lib/credential-store.sh +8 -2
  32. package/pipeline/lib/extract-conventions.sh +1 -14
  33. package/pipeline/lib/fetch-confluence.sh +12 -4
  34. package/pipeline/lib/fetch-crashlytics.sh +11 -8
  35. package/pipeline/lib/fetch-document.sh +1 -1
  36. package/pipeline/lib/fetch-figma-annotations.sh +7 -5
  37. package/pipeline/lib/fetch-fortify.sh +5 -3
  38. package/pipeline/lib/fetch-graylog.sh +5 -3
  39. package/pipeline/lib/figma-mcp-refresh.sh +1 -1
  40. package/pipeline/lib/figma-screenshot.sh +27 -24
  41. package/pipeline/lib/figma-token.sh +8 -4
  42. package/pipeline/lib/issue-fetcher.sh +0 -1
  43. package/pipeline/lib/jira-publish.sh +7 -5
  44. package/pipeline/lib/md2confluence-v3.py +13 -7
  45. package/pipeline/lib/multi-repo-pipeline.sh +18 -8
  46. package/pipeline/lib/plan-todos.sh +11 -0
  47. package/pipeline/lib/post-pr-review.sh +9 -2
  48. package/pipeline/lib/repo-cache.sh +18 -10
  49. package/pipeline/lib/review-watch.sh +60 -14
  50. package/pipeline/lib/shadow-git.sh +8 -4
  51. package/pipeline/lib/vercel-deploy.sh +2 -2
  52. package/pipeline/multi-agent-refs/_dev-context.md +5 -2
  53. package/pipeline/multi-agent-refs/analysis/locked.md +4 -4
  54. package/pipeline/multi-agent-refs/analysis/render.md +1 -1
  55. package/pipeline/multi-agent-refs/cross-cli-contract.md +1 -1
  56. package/pipeline/multi-agent-refs/outside-the-pipeline.md +6 -6
  57. package/pipeline/multi-agent-refs/phases/log-format.md +1 -1
  58. package/pipeline/multi-agent-refs/phases/modes.md +1 -1
  59. package/pipeline/multi-agent-refs/phases/phase-0-init.md +4 -2
  60. package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +3 -3
  61. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +5 -5
  62. package/pipeline/multi-agent-refs/phases/phase-4-review.md +21 -16
  63. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  64. package/pipeline/rules/figma-pipeline.md +18 -72
  65. package/pipeline/rules/outside-the-pipeline.md +4 -3
  66. package/pipeline/schemas/agent-state.schema.json +1 -1
  67. package/pipeline/schemas/token-budget.json +1 -1
  68. package/pipeline/scripts/_stack-routing.mjs +1 -1
  69. package/pipeline/scripts/agent-guard.py +102 -21
  70. package/pipeline/scripts/anonymize-findings.mjs +7 -6
  71. package/pipeline/scripts/build-skills-index.mjs +14 -3
  72. package/pipeline/scripts/cost-budget-check.mjs +5 -3
  73. package/pipeline/scripts/cost-lib.sh +0 -15
  74. package/pipeline/scripts/diff-explain.mjs +22 -12
  75. package/pipeline/scripts/gc-refs.sh +6 -2
  76. package/pipeline/scripts/gc-tmp.sh +1 -1
  77. package/pipeline/scripts/gc-worktrees.sh +1 -1
  78. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  79. package/pipeline/scripts/github-ssh-setup.sh +7 -2
  80. package/pipeline/scripts/graph-build.mjs +2 -2
  81. package/pipeline/scripts/jira-wiki-escape.mjs +2 -1
  82. package/pipeline/scripts/keychain.py +12 -11
  83. package/pipeline/scripts/learning-curve.mjs +1 -1
  84. package/pipeline/scripts/migrate-prefs.mjs +1 -1
  85. package/pipeline/scripts/output-quality-check.sh +3 -1
  86. package/pipeline/scripts/phase-tracker.sh +1 -1
  87. package/pipeline/scripts/phase0-exit-gate.mjs +2 -1
  88. package/pipeline/scripts/plan-coverage-gate.mjs +2 -1
  89. package/pipeline/scripts/pre-commit-check.sh +23 -13
  90. package/pipeline/scripts/prune-logs.sh +1 -1
  91. package/pipeline/scripts/render-agent-log-cost.sh +3 -1
  92. package/pipeline/scripts/render-cost-summary.sh +4 -2
  93. package/pipeline/scripts/render-work-summary.sh +5 -3
  94. package/pipeline/scripts/repo-map.mjs +3 -2
  95. package/pipeline/scripts/scan-skills.sh +6 -2
  96. package/pipeline/scripts/search-logs.sh +8 -6
  97. package/pipeline/scripts/sign-skills.sh +3 -1
  98. package/pipeline/scripts/smoke-cross-cli-behavior.sh +1 -1
  99. package/pipeline/scripts/triage-memory.mjs +25 -4
  100. package/pipeline/scripts/uninstall.mjs +20 -12
  101. package/pipeline/scripts/update-check.sh +2 -2
  102. package/pipeline/scripts/update-issue-progress.sh +6 -5
  103. package/pipeline/scripts/validate-analysis-doc.mjs +6 -6
  104. package/pipeline/scripts/verify-skills.sh +3 -1
  105. package/pipeline/scripts/worktree-finalize.sh +22 -10
  106. package/pipeline/skills/.skill-manifest.json +81 -57
  107. package/pipeline/skills/.skills-index.json +19 -19
  108. package/pipeline/skills/shared/README.md +8 -8
  109. package/pipeline/skills/shared/core/multi-agent/SKILL.md +7 -7
  110. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +1 -1
  111. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -2
  112. package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
  113. package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +2 -2
  114. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +23 -14
  115. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +2 -2
  116. package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +56 -0
  117. package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +0 -4
  118. package/pipeline/skills/shared/external/api-patterns/SKILL.md +12 -24
  119. package/pipeline/skills/shared/external/app-store-changelog/references/release-notes-guidelines.md +34 -0
  120. package/pipeline/skills/shared/external/app-store-changelog/scripts/collect_release_changes.sh +33 -0
  121. package/pipeline/skills/shared/external/architecture/SKILL.md +7 -9
  122. package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +0 -4
  123. package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +0 -1
  124. package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +0 -14
  125. package/pipeline/skills/shared/external/hig-components-content/SKILL.md +13 -13
  126. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +16 -16
  127. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +6 -6
  128. package/pipeline/skills/shared/external/hig-components-system/SKILL.md +13 -13
  129. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +23 -23
  130. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +18 -18
  131. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +30 -30
  132. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +11 -11
  133. package/pipeline/skills/shared/external/hig-technologies/SKILL.md +33 -33
  134. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +52 -52
  135. package/pipeline/skills/shared/external/ios-coding-standard/references/lint-local.sh +1 -1
  136. package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +11 -11
  137. package/pipeline/skills/shared/external/ios-developer/SKILL.md +0 -1
  138. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +7 -3
  139. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -15
  140. package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +0 -5
  141. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/Package.swift +17 -0
  142. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/Resources/.keep +0 -0
  143. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/main.swift +11 -0
  144. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/version.env +2 -0
  145. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/build_icon.sh +49 -0
  146. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/compile_and_run.sh +63 -0
  147. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/launch.sh +28 -0
  148. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/make_appcast.sh +82 -0
  149. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/package_app.sh +206 -0
  150. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +52 -0
  151. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +52 -0
  152. package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/version.env +2 -0
  153. package/pipeline/skills/shared/external/macos-spm-app-packaging/references/packaging.md +17 -0
  154. package/pipeline/skills/shared/external/macos-spm-app-packaging/references/release.md +32 -0
  155. package/pipeline/skills/shared/external/macos-spm-app-packaging/references/scaffold.md +79 -0
  156. package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +0 -1
  157. package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +0 -4
  158. package/pipeline/skills/shared/external/swift-concurrency-expert/references/approachable-concurrency.md +63 -0
  159. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +272 -0
  160. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swiftui-concurrency-tour-wwdc.md +33 -0
  161. package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +150 -0
  162. package/pipeline/skills/shared/external/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
  163. package/pipeline/skills/shared/external/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
  164. package/pipeline/skills/shared/external/swiftui-performance-audit/references/profiling-intake.md +44 -0
  165. package/pipeline/skills/shared/external/swiftui-performance-audit/references/report-template.md +47 -0
  166. package/pipeline/skills/shared/external/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
  167. package/pipeline/skills/shared/external/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
  168. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +201 -0
  169. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/async-state.md +96 -0
  170. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/components-index.md +46 -0
  171. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/controls.md +57 -0
  172. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/deeplinks.md +66 -0
  173. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/focus.md +90 -0
  174. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/form.md +97 -0
  175. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/grids.md +71 -0
  176. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/haptics.md +71 -0
  177. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  178. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  179. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/list.md +86 -0
  180. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  181. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/macos-settings.md +71 -0
  182. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  183. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/media.md +73 -0
  184. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +101 -0
  185. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +159 -0
  186. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/overlay.md +45 -0
  187. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/performance.md +62 -0
  188. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/previews.md +48 -0
  189. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  190. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scrollview.md +87 -0
  191. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/searchable.md +71 -0
  192. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +155 -0
  193. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/split-views.md +72 -0
  194. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +114 -0
  195. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/theming.md +71 -0
  196. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/title-menus.md +93 -0
  197. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/top-bar.md +49 -0
  198. package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +161 -0
  199. package/pipeline/skills/skills-index.md +8 -8
  200. package/pipeline/skills/shared/external/help-skills/SKILL.md +0 -166
@@ -0,0 +1,32 @@
1
+ # Release and notarization notes
2
+
3
+ ## Notarization requirements
4
+ - Install Xcode Command Line Tools (for `xcrun` and `notarytool`).
5
+ - Provide App Store Connect API credentials:
6
+ - `APP_STORE_CONNECT_API_KEY_P8`
7
+ - `APP_STORE_CONNECT_KEY_ID`
8
+ - `APP_STORE_CONNECT_ISSUER_ID`
9
+ - Provide a Developer ID Application identity in `APP_IDENTITY`.
10
+
11
+ ## Sparkle appcast (optional)
12
+ - Install Sparkle tools so `generate_appcast` is on PATH.
13
+ - Provide `SPARKLE_PRIVATE_KEY_FILE` (ed25519 key).
14
+ - The appcast script uses your zip artifact to create an updated `appcast.xml`.
15
+ - Sparkle compares `sparkle:version` (derived from `CFBundleVersion`), so bump `BUILD_NUMBER` for every release.
16
+
17
+ ## Tag and GitHub release (optional)
18
+ Use a versioned git tag and publish a GitHub release with the notarized zip (and appcast if you host it on GitHub Releases).
19
+
20
+ Example flow:
21
+ ```
22
+ git tag v<version>
23
+ git push origin v<version>
24
+
25
+ gh release create v<version> CodexBar-<version>.zip appcast.xml \
26
+ --title "AppName <version>" \
27
+ --notes-file CHANGELOG.md
28
+ ```
29
+
30
+ Notes:
31
+ - If you serve appcast from GitHub Releases or raw URLs, ensure the release is published and assets are accessible (no 404s).
32
+ - Prefer using a curated release notes file rather than dumping the full changelog.
@@ -0,0 +1,79 @@
1
+ # Scaffold a SwiftPM macOS app (no Xcode)
2
+
3
+ ## Steps
4
+ 1) Create a repo and initialize SwiftPM:
5
+ ```
6
+ mkdir MyApp
7
+ cd MyApp
8
+ swift package init --type executable
9
+ ```
10
+
11
+ 2) Update `Package.swift` to target macOS and define an executable target for the app.
12
+
13
+ 3) Create the app entry point under `Sources/MyApp/`.
14
+ - Use SwiftUI if you want a windowed app with minimal AppKit glue.
15
+ - Use AppKit if you want a menu bar or accessory-style app.
16
+
17
+ 4) If you need app resources, add:
18
+ ```
19
+ resources: [.process("Resources")]
20
+ ```
21
+ and create `Sources/MyApp/Resources/`.
22
+
23
+ 5) Add a `version.env` file (used by packaging templates):
24
+ ```
25
+ MARKETING_VERSION=0.1.0
26
+ BUILD_NUMBER=1
27
+ ```
28
+
29
+ 6) Copy script templates from `assets/templates/` into your repo (for example, `Scripts/`).
30
+
31
+ ## Minimal Package.swift (example)
32
+ ```
33
+ // swift-tools-version: 6.2
34
+ import PackageDescription
35
+
36
+ let package = Package(
37
+ name: "MyApp",
38
+ platforms: [.macOS(.v14)],
39
+ targets: [
40
+ .executableTarget(
41
+ name: "MyApp",
42
+ path: "Sources/MyApp",
43
+ resources: [
44
+ .process("Resources")
45
+ ])
46
+ ]
47
+ )
48
+ ```
49
+
50
+ ## Minimal SwiftUI entry point (example)
51
+ ```
52
+ import SwiftUI
53
+
54
+ @main
55
+ struct MyApp: App {
56
+ var body: some Scene {
57
+ WindowGroup {
58
+ Text("Hello")
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Minimal AppKit entry point (example)
65
+ ```
66
+ import AppKit
67
+
68
+ final class AppDelegate: NSObject, NSApplicationDelegate {
69
+ func applicationDidFinishLaunching(_ notification: Notification) {
70
+ // Initialize app state here.
71
+ }
72
+ }
73
+
74
+ let app = NSApplication.shared
75
+ let delegate = AppDelegate()
76
+ app.delegate = delegate
77
+ app.setActivationPolicy(.regular)
78
+ app.run()
79
+ ```
@@ -20,7 +20,6 @@ Expert in monorepo architecture, build systems, and dependency management at sca
20
20
  - Clarify goals, constraints, and required inputs.
21
21
  - Apply relevant best practices and validate outcomes.
22
22
  - Provide actionable steps and verification.
23
- - If detailed examples are required, open `resources/implementation-playbook.md`.
24
23
 
25
24
  ## Capabilities
26
25
 
@@ -31,8 +31,4 @@ Comprehensive guidance for building scalable, maintainable, and production-ready
31
31
  - Clarify goals, constraints, and required inputs.
32
32
  - Apply relevant best practices and validate outcomes.
33
33
  - Provide actionable steps and verification.
34
- - If detailed examples are required, open `resources/implementation-playbook.md`.
35
34
 
36
- ## Resources
37
-
38
- - `resources/implementation-playbook.md` for detailed patterns and examples.
@@ -0,0 +1,63 @@
1
+ ## Approachable Concurrency (Swift 6.2) - project mode quick guide
2
+
3
+ Use this reference when the project has opted into the Swift 6.2 approachable
4
+ concurrency settings (default actor isolation / main-actor-by-default).
5
+
6
+ ## Detect the mode
7
+
8
+ Check Xcode build settings under "Swift Compiler - Concurrency":
9
+ - Swift language version (must be 6.2+).
10
+ - Default actor isolation / Main Actor by default.
11
+ - Strict concurrency checking level (Complete/Targeted/Minimal).
12
+
13
+ For SwiftPM, inspect Package.swift swiftSettings for the same flags.
14
+
15
+ ## Behavior changes to expect
16
+
17
+ - Async functions stay on the caller's actor by default; they don't hop to a
18
+ global concurrent executor unless the implementation chooses to.
19
+ - Main-actor-by-default reduces data race errors for UI-bound code and global
20
+ state, because mutable state is implicitly protected.
21
+ - Protocol conformances can be isolated (e.g., `extension Foo: @MainActor Bar`).
22
+
23
+ ## How to apply fixes in this mode
24
+
25
+ - Prefer minimal annotations; let main-actor-by-default do the work when the
26
+ code is UI-bound.
27
+ - Use isolated conformances instead of forcing `nonisolated` workarounds.
28
+ - Keep global or shared mutable state on the main actor unless there is a clear
29
+ performance need to offload it.
30
+
31
+ ## When to opt out or offload work
32
+
33
+ - Use `@concurrent` on async functions that must run on the concurrent pool.
34
+ - Make types or members `nonisolated` only when they are truly thread-safe and
35
+ used off the main actor.
36
+ - Continue to respect Sendable boundaries when values cross actors or tasks.
37
+
38
+ ## Common pitfalls
39
+
40
+ - `Task.detached` ignores inherited actor context; avoid it unless you truly
41
+ need to break isolation.
42
+ - Main-actor-by-default can hide performance issues if CPU-heavy work stays on
43
+ the main actor; move that work into `@concurrent` async functions.
44
+
45
+ ## Keywords (from source cheat sheet)
46
+
47
+ | Keyword | What it does |
48
+ | --- | --- |
49
+ | `async` | Function can pause |
50
+ | `await` | Pause here until done |
51
+ | `Task { }` | Start async work, inherits context |
52
+ | `Task.detached { }` | Start async work, no inherited context |
53
+ | `@MainActor` | Runs on main thread |
54
+ | `actor` | Type with isolated mutable state |
55
+ | `nonisolated` | Opts out of actor isolation |
56
+ | `Sendable` | Safe to pass between isolation domains |
57
+ | `@concurrent` | Always run on background (Swift 6.2+) |
58
+ | `async let` | Start parallel work |
59
+ | `TaskGroup` | Dynamic parallel work |
60
+
61
+ ## Source
62
+
63
+ https://fuckingapproachableswiftconcurrency.com/en/
@@ -0,0 +1,272 @@
1
+ ## Concurrent programming updates in Swift 6.2
2
+
3
+ Concurrent programming is hard because sharing memory between multiple tasks is prone to mistakes that lead to unpredictable behavior.
4
+
5
+ ## Data-race safety
6
+
7
+ Data-race safety in Swift 6 prevents these mistakes at compile time, so you can write concurrent code without fear of introducing hard-to-debug runtime bugs. But in many cases, the most natural code to write is prone to data races, leading to compiler errors that you have to address. A class with mutable state, like this `PhotoProcessor` class, is safe as long as you don't access it concurrently.
8
+
9
+ ```swift
10
+ class PhotoProcessor {
11
+ func extractSticker(data: Data, with id: String?) async -> Sticker? { }
12
+ }
13
+
14
+ @MainActor
15
+ final class StickerModel {
16
+ let photoProcessor = PhotoProcessor()
17
+
18
+ func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
19
+ guard let data = try await item.loadTransferable(type: Data.self) else {
20
+ return nil
21
+ }
22
+
23
+ // Error: Sending 'self.photoProcessor' risks causing data races
24
+ // Sending main actor-isolated 'self.photoProcessor' to nonisolated instance method 'extractSticker(data:with:)'
25
+ // risks causing data races between nonisolated and main actor-isolated uses
26
+ return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
27
+ }
28
+ }
29
+ ```
30
+
31
+ It has an async method to extract a `Sticker` by computing the subject of the given image data. But if you try to call `extractSticker` from UI code on the main actor, you'll get an error that the call risks causing data races. This is because there are several places in the language that offload work to the background implicitly, even if you never needed code to run in parallel.
32
+
33
+ Swift 6.2 changes this philosophy to stay single threaded by default until you choose to introduce concurrency.
34
+
35
+ ```swift
36
+ class PhotoProcessor {
37
+ func extractSticker(data: Data, with id: String?) async -> Sticker? { }
38
+ }
39
+
40
+ @MainActor
41
+ final class StickerModel {
42
+ let photoProcessor = PhotoProcessor()
43
+
44
+ func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
45
+ guard let data = try await item.loadTransferable(type: Data.self) else {
46
+ return nil
47
+ }
48
+
49
+ // No longer a data race error in Swift 6.2 because of Approachable Concurrency and default actor isolation
50
+ return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
51
+ }
52
+ }
53
+ ```
54
+
55
+ The language changes in Swift 6.2 make the most natural code to write data race free by default. This provides a more approachable path to introducing concurrency in a project.
56
+
57
+ When you choose to introduce concurrency because you want to run code in parallel, data-race safety will protect you.
58
+
59
+ First, we've made it easier to call async functions on types with mutable state. Instead of eagerly offloading async functions that aren't tied to a specific actor, the function will continue to run on the actor it was called from. This eliminates data races because the values passed into the async function are never sent outside the actor. Async functions can still offload work in their implementation, but clients don't have to worry about their mutable state.
60
+
61
+ Next, we've made it easier to implement conformances on main actor types. Here I have a protocol called `Exportable`, and I'm trying to implement a conformance for my main actor `StickerModel` class. The export requirement doesn't have actor isolation, so the language assumed that it could be called from off the main actor, and prevented `StickerModel` from using main actor state in its implementation.
62
+
63
+ ```swift
64
+ protocol Exportable {
65
+ func export()
66
+ }
67
+
68
+ extension StickerModel: Exportable { // error: Conformance of 'StickerModel' to protocol 'Exportable' crosses into main actor-isolated code and can cause data races
69
+ func export() {
70
+ photoProcessor.exportAsPNG()
71
+ }
72
+ }
73
+ ```
74
+
75
+ Swift 6.2 supports these conformances. A conformance that needs main actor state is called an *isolated* conformance. This is safe because the compiler ensures a main actor conformance is only used on the main actor.
76
+
77
+ ```swift
78
+ // Isolated conformances
79
+
80
+ protocol Exportable {
81
+ func export()
82
+ }
83
+
84
+ extension StickerModel: @MainActor Exportable {
85
+ func export() {
86
+ photoProcessor.exportAsPNG()
87
+ }
88
+ }
89
+ ```
90
+
91
+ I can create an `ImageExporter` type that adds a `StickerModel` to an array of any `Exportable` items as long as it stays on the main actor.
92
+
93
+ ```swift
94
+ // Isolated conformances
95
+
96
+ @MainActor
97
+ struct ImageExporter {
98
+ var items: [any Exportable]
99
+
100
+ mutating func add(_ item: StickerModel) {
101
+ items.append(item)
102
+ }
103
+
104
+ func exportAll() {
105
+ for item in items {
106
+ item.export()
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ But if I allow `ImageExporter` to be used from anywhere, the compiler prevents adding `StickerModel` to the array because it isn't safe to call export on `StickerModel` from outside the main actor.
113
+
114
+ ```swift
115
+ // Isolated conformances
116
+
117
+ nonisolated
118
+ struct ImageExporter {
119
+ var items: [any Exportable]
120
+
121
+ mutating func add(_ item: StickerModel) {
122
+ items.append(item) // error: Main actor-isolated conformance of 'StickerModel' to 'Exportable' cannot be used in nonisolated context
123
+ }
124
+
125
+ func exportAll() {
126
+ for item in items {
127
+ item.export()
128
+ }
129
+ }
130
+ }
131
+ ```
132
+
133
+ With isolated conformances, you only have to solve data race safety issues when the code indicates that it uses the conformance concurrently.
134
+
135
+ ## Global State
136
+
137
+ Global and static variables are prone to data races because they allow mutable state to be accessed from anywhere.
138
+
139
+ ```swift
140
+ final class StickerLibrary {
141
+ static let shared: StickerLibrary = .init() // error: Static property 'shared' is not concurrency-safe because non-'Sendable' type 'StickerLibrary' may have shared mutable state
142
+ }
143
+ ```
144
+
145
+ The most common way to protect global state is with the main actor.
146
+
147
+ ```swift
148
+ final class StickerLibrary {
149
+ @MainActor
150
+ static let shared: StickerLibrary = .init()
151
+ }
152
+ ```
153
+
154
+ And it's common to annotate an entire class with the main actor to protect all of its mutable state, especially in a project that doesn't have a lot of concurrent tasks.
155
+
156
+ ```swift
157
+ @MainActor
158
+ final class StickerLibrary {
159
+ static let shared: StickerLibrary = .init()
160
+ }
161
+ ```
162
+
163
+ You can model a program that's entirely single-threaded by writing `@MainActor` on everything in your project.
164
+
165
+ ```swift
166
+ @MainActor
167
+ final class StickerLibrary {
168
+ static let shared: StickerLibrary = .init()
169
+ }
170
+
171
+ @MainActor
172
+ final class StickerModel {
173
+ let photoProcessor: PhotoProcessor
174
+
175
+ var selection: [PhotosPickerItem]
176
+ }
177
+
178
+ extension StickerModel: @MainActor Exportable {
179
+ func export() {
180
+ photoProcessor.exportAsPNG()
181
+ }
182
+ }
183
+ ```
184
+
185
+ To make it easier to model single-threaded code, we've introduced a mode to infer main actor by default.
186
+
187
+ ```swift
188
+ // Mode to infer main actor by default in Swift 6.2
189
+
190
+ final class StickerLibrary {
191
+ static let shared: StickerLibrary = .init()
192
+ }
193
+
194
+ final class StickerModel {
195
+ let photoProcessor: PhotoProcessor
196
+
197
+ var selection: [PhotosPickerItem]
198
+ }
199
+
200
+ extension StickerModel: Exportable {
201
+ func export() {
202
+ photoProcessor.exportAsPNG()
203
+ }
204
+ }
205
+ ```
206
+
207
+ This eliminates data-race safety errors about unsafe global and static variables, calls to other main actor functions like ones from the SDK, and more, because the main actor protects all mutable state by default. It also reduces concurrency annotations in code that's mostly single-threaded. This mode is great for projects that do most of the work on the main actor, and concurrent code is encapsulated within specific types or files. It's opt-in and it's recommended for apps, scripts, and other executable targets.
208
+
209
+ ## Offloading work to the background
210
+
211
+ Offloading work to the background is still important for performance, such as keeping apps responsive when performing CPU-intensive tasks.
212
+
213
+ Let's look at the implementation of the `extractSticker` method on `PhotoProcessor`.
214
+
215
+ ```swift
216
+ // Explicitly offloading async work
217
+
218
+ class PhotoProcessor {
219
+ var cachedStickers: [String: Sticker]
220
+
221
+ func extractSticker(data: Data, with id: String) async -> Sticker {
222
+ if let sticker = cachedStickers[id] {
223
+ return sticker
224
+ }
225
+
226
+ let sticker = await Self.extractSubject(from: data)
227
+ cachedStickers[id] = sticker
228
+ return sticker
229
+ }
230
+
231
+ // Offload expensive image processing using the @concurrent attribute.
232
+ @concurrent
233
+ static func extractSubject(from data: Data) async -> Sticker { }
234
+ }
235
+ ```
236
+
237
+ It first checks whether it already extracted a sticker for an image, so it can return the cached sticker immediately. If the sticker hasn't been cached, it extracts the subject from the image data and creates a new sticker. The `extractSubject` method performs expensive image processing that I don't want to block the main actor or any other actor.
238
+
239
+ I can offload this work using the `@concurrent` attribute. `@concurrent` ensures that a function always runs on the concurrent thread pool, freeing up the actor to run other tasks at the same time.
240
+
241
+ ### An example
242
+
243
+ Say you have a function called `process` that you would like to run on a background thread. To call that function on a background thread you need to:
244
+
245
+ - make sure the structure or class is `nonisolated`
246
+ - add the `@concurrent` attribute to the function you want to run in the background
247
+ - add the keyword `async` to the function if it is not already asynchronous
248
+ - and then add the keyword `await` to any callers
249
+
250
+ Like this:
251
+
252
+ ```swift
253
+ nonisolated struct PhotoProcessor {
254
+
255
+ @concurrent
256
+ func process(data: Data) async -> ProcessedPhoto? { ... }
257
+ }
258
+
259
+ // Callers with the added await
260
+ processedPhotos[item.id] = await PhotoProcessor().process(data: data)
261
+ ```
262
+
263
+
264
+ ## Summary
265
+
266
+ These language changes work together to make concurrency more approachable.
267
+
268
+ You start by writing code that runs on the main actor by default, where there's no risk of data races. When you start to use async functions, those functions run wherever they're called from. There's still no risk of data races because all of your code still runs on the main actor. When you're ready to embrace concurrency to improve performance, it's easy to offload specific code to the background to run in parallel.
269
+
270
+ Some of these language changes are opt-in because they require changes in your project to adopt. You can find and enable all of the approachable concurrency language changes under the Swift Compiler - Concurrency section of Xcode build settings. You can also enable these features in a Swift package manifest file using the SwiftSettings API.
271
+
272
+ Swift 6.2 includes migration tooling to help you make the necessary code changes automatically. You can learn more about migration tooling at swift.org/migration.
@@ -0,0 +1,33 @@
1
+ # SwiftUI Concurrency Tour (Summary)
2
+
3
+ Context: SwiftUI-focused concurrency overview covering actor isolation, Sendable closures, and how SwiftUI runs work off the main thread.
4
+
5
+ ## Main-actor default in SwiftUI
6
+
7
+ - `View` is `@MainActor` isolated by default; members and `body` inherit isolation.
8
+ - Swift 6.2 can infer `@MainActor` for all types in a module (new language mode).
9
+ - This default simplifies UI code and aligns with UIKit/AppKit `@MainActor` APIs.
10
+
11
+ ## Where SwiftUI runs code off the main thread
12
+
13
+ - SwiftUI may evaluate some view logic on background threads for performance.
14
+ - Examples: `Shape` path generation, `Layout` methods, `visualEffect` closures, and `onGeometryChange` closures.
15
+ - These APIs often require `Sendable` closures to reflect their runtime semantics.
16
+
17
+ ## Sendable closures and data-race safety
18
+
19
+ - Accessing `@MainActor` state from a `Sendable` closure is unsafe and flagged by the compiler.
20
+ - Prefer capturing value copies in the closure capture list (e.g., copy a `Bool`).
21
+ - Avoid sending `self` into a sendable closure just to read a single property.
22
+
23
+ ## Structuring async work with SwiftUI
24
+
25
+ - SwiftUI action callbacks are synchronous so UI updates (like loading states) can be immediate.
26
+ - Use `Task` to bridge into async contexts; keep async bodies minimal.
27
+ - Use state as the boundary: async work updates model/state; UI reacts synchronously.
28
+
29
+ ## Performance-driven concurrency
30
+
31
+ - Offload expensive work from the main actor to avoid hitches.
32
+ - Keep time-sensitive UI logic (animations, gesture responses) synchronous.
33
+ - Separate UI code from long-running async work to improve responsiveness and testability.
@@ -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
@@ -0,0 +1,46 @@
1
+ # Demystify SwiftUI Performance (WWDC23) (Summary)
2
+
3
+ Context: WWDC23 session on building a mental model for SwiftUI performance and triaging hangs/hitches.
4
+
5
+ ## Performance loop
6
+
7
+ - Measure -> Identify -> Optimize -> Re-measure.
8
+ - Focus on concrete symptoms (slow navigation, broken animations, spinning cursor).
9
+
10
+ ## Dependencies and updates
11
+
12
+ - Views form a dependency graph; dynamic properties are a frequent source of updates.
13
+ - Use `Self._printChanges()` in debug only to inspect extra dependencies.
14
+ - Eliminate unnecessary dependencies by extracting views or narrowing state.
15
+ - Consider `@Observable` for more granular property tracking.
16
+
17
+ ## Common causes of slow updates
18
+
19
+ - Expensive view bodies (string interpolation, filtering, formatting).
20
+ - Dynamic property instantiation and state initialization in `body`.
21
+ - Slow identity resolution in lists/tables.
22
+ - Hidden work: bundle lookups, heap allocations, repeated string construction.
23
+
24
+ ## Avoid slow initialization in view bodies
25
+
26
+ - Don't create heavy models synchronously in view bodies.
27
+ - Use `.task` to fetch async data and keep `init` lightweight.
28
+
29
+ ## Lists and tables identity rules
30
+
31
+ - Stable identity is critical for performance and animation.
32
+ - Ensure a constant number of views per element in `ForEach`.
33
+ - Avoid inline filtering in `ForEach`; pre-filter and cache collections.
34
+ - Avoid `AnyView` in list rows; it hides identity and increases cost.
35
+ - Flatten nested `ForEach` when possible to reduce overhead.
36
+
37
+ ## Table specifics
38
+
39
+ - `TableRow` resolves to a single row; row count must be constant.
40
+ - Prefer the streamlined `Table` initializer to enforce constant rows.
41
+ - Use explicit IDs for back deployment when needed.
42
+
43
+ ## Debugging aids
44
+
45
+ - Use Instruments for hangs and hitches.
46
+ - Use `_printChanges` to validate dependency assumptions during debug.