@mmerterden/multi-agent-pipeline 20.6.0 → 20.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +328 -329
  5. package/package.json +2 -2
  6. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  7. package/pipeline/scripts/_notices.mjs +12 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  9. package/pipeline/skills/.skill-manifest.json +106 -106
  10. package/pipeline/skills/shared/README.md +71 -71
  11. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  16. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  17. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  19. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  20. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  21. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  22. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  23. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  24. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  25. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  26. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  27. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  28. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  29. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  30. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  31. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  32. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  33. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  34. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  35. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  37. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  38. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  39. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  40. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  41. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  42. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  43. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  44. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  45. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  46. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  47. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  48. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  49. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  50. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  51. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  53. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  54. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  55. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  56. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  57. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  58. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  59. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  60. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  61. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  62. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  63. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  64. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  65. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  66. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  67. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  68. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  69. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  70. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  71. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  72. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  73. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  74. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  75. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  76. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  77. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  78. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  79. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  81. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  82. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  83. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  84. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  85. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  86. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  87. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  88. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  89. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  90. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  91. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  92. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  93. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  94. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  95. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  96. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  97. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  98. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  99. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  100. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  101. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  102. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  103. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  104. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  105. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  106. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  107. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  108. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  109. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  110. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  111. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  112. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  113. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  114. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  115. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  116. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  117. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  118. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  119. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  120. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  121. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  122. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  123. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  124. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  125. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  126. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  127. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  128. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  129. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  130. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  131. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  132. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  133. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  134. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  135. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  136. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  137. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  138. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  139. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  140. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  141. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  142. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  143. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  144. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  145. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  146. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  147. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  148. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  149. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  150. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  151. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  152. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  153. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  154. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  155. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  156. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  157. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  158. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  159. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  160. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  161. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  162. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  163. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  164. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  165. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  166. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  167. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  168. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  169. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  170. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  171. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  172. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  173. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  174. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  175. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  176. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  177. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  178. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  179. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  180. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  181. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  183. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  184. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  185. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  186. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  187. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  188. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  189. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  190. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  191. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  192. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  193. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  194. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  195. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  196. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  197. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  198. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  199. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  200. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  201. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  202. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  203. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  204. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  205. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  206. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  207. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  208. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  209. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  210. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  211. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  212. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  213. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  214. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  219. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  220. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  221. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  222. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  223. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  224. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  225. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  226. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  227. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  228. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  229. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  230. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  231. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  232. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  233. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  234. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  235. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  236. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  237. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  238. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  239. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  240. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  241. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  242. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  243. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  244. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  245. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  246. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  247. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  248. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  249. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  250. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  251. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  252. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  254. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  255. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  256. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  257. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  258. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  259. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  261. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  262. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  263. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  264. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  265. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  268. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  269. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  270. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  271. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  272. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  273. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  274. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  275. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  276. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  277. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  278. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  279. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  280. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  281. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  284. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  286. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  291. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  294. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  295. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  296. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  297. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  298. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  299. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  300. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  301. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  302. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  303. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  305. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  306. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  309. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  310. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  311. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  312. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  313. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  314. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  315. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  316. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  317. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  318. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  319. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  320. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  321. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  322. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  323. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
  324. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  325. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  326. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,463 +1,310 @@
1
- # UIKit-to-SwiftUI Migration Patterns
1
+ # Hosting and Incremental Migration
2
2
 
3
- Patterns for incrementally migrating a UIKit app to SwiftUI. Each pattern is self-contained with rationale, implementation, and gotchas.
4
-
5
- ---
3
+ A UIKit app does not have to switch to SwiftUI in one release. These six
4
+ patterns move it over piece by piece while both frameworks share the same
5
+ navigation stack, the same models and the same traits.
6
6
 
7
7
  ## Contents
8
8
 
9
- - [1. Screen-by-Screen Migration](#1-screen-by-screen-migration)
10
- - [2. UIHostingController as Child](#2-uihostingcontroller-as-child)
11
- - [3. Navigation Bridging](#3-navigation-bridging)
12
- - [4. Data Sharing Between UIKit and SwiftUI](#4-data-sharing-between-uikit-and-swiftui)
13
- - [5. UIHostingConfiguration (iOS 16+)](#5-uihostingconfiguration-ios-16)
14
- - [6. Environment Bridging](#6-environment-bridging)
9
+ 1. [Replace whole screens](#1-screen-by-screen-migration)
10
+ 2. [Embed a SwiftUI region](#2-uihostingcontroller-as-child)
11
+ 3. [Share one navigation stack](#3-navigation-bridging)
12
+ 4. [Share models](#4-data-sharing-between-uikit-and-swiftui)
13
+ 5. [SwiftUI cells](#5-uihostingconfiguration-ios-16)
14
+ 6. [Pass environment and traits](#6-environment-bridging)
15
15
 
16
16
  ## 1. Screen-by-Screen Migration
17
17
 
18
- Replace one `UIViewController` at a time with a `UIHostingController` wrapping a SwiftUI view. This is the safest migration path -- each screen is an isolated unit.
19
-
20
- ### Strategy
21
-
22
- 1. Pick a leaf screen (one that does not contain child view controllers).
23
- 2. Rewrite its UI in SwiftUI.
24
- 3. Replace the UIKit view controller with `UIHostingController` wherever it was instantiated.
25
- 4. Wire navigation from the parent UIKit code into the hosting controller.
26
-
27
- ### Implementation
28
-
29
- ```swift
30
- // BEFORE: UIKit screen pushed onto a navigation stack
31
- let detailVC = ItemDetailViewController(item: item)
32
- navigationController?.pushViewController(detailVC, animated: true)
33
-
34
- // AFTER: SwiftUI screen wrapped in UIHostingController
35
- let detailView = ItemDetailView(item: item)
36
- let hostingVC = UIHostingController(rootView: detailView)
37
- navigationController?.pushViewController(hostingVC, animated: true)
38
- ```
18
+ The lowest-risk route is to replace whole view controllers, one at a time, with
19
+ a `UIHostingController` that wraps the new SwiftUI screen.
39
20
 
40
- ### Passing Dismiss/Navigation Callbacks
21
+ Order of work:
41
22
 
42
- When the SwiftUI screen needs to pop itself or trigger navigation in the UIKit stack:
23
+ 1. Choose a leaf screen, one that embeds no child view controllers.
24
+ 2. Rebuild it in SwiftUI.
25
+ 3. Replace the old controller wherever it was instantiated.
26
+ 4. Connect the surrounding UIKit navigation to the hosting controller.
43
27
 
44
28
  ```swift
45
- struct ItemDetailView: View {
46
- let item: Item
47
- var onDelete: (() -> Void)?
48
-
49
- @Environment(\.dismiss) private var dismiss
50
-
51
- var body: some View {
52
- VStack {
53
- Text(item.title)
54
- Button("Delete", role: .destructive) {
55
- onDelete?()
56
- dismiss()
57
- }
58
- }
59
- }
60
- }
61
-
62
- // In UIKit:
63
- let detailView = ItemDetailView(item: item) {
64
- self.dataSource.delete(item)
65
- self.navigationController?.popViewController(animated: true)
66
- }
67
- let hostingVC = UIHostingController(rootView: detailView)
29
+ // Before: navigationController?.pushViewController(InvoiceDetailViewController(invoice: invoice), animated: true)
30
+ let detail = InvoiceDetailView(invoice: invoice, onVoid: { [weak self] in
31
+ self?.ledger.void(invoice)
32
+ self?.navigationController?.popViewController(animated: true)
33
+ })
34
+ let host = UIHostingController(rootView: detail)
35
+ navigationController?.pushViewController(host, animated: true)
68
36
  ```
69
37
 
70
- ### Gotchas
38
+ The SwiftUI view takes a callback such as `onVoid`; the UIKit side supplies the
39
+ closure that changes data and pops the screen.
71
40
 
72
- - **Navigation bar.** `UIHostingController` inherits navigation bar visibility from its parent `UINavigationController`. Use `.navigationTitle()` and `.toolbar()` in the SwiftUI view -- they propagate to the UIKit navigation bar automatically.
73
- - **Large titles.** Set `hostingVC.navigationItem.largeTitleDisplayMode` in UIKit code if the SwiftUI `.navigationBarTitleDisplayMode()` modifier does not apply correctly.
74
- - **Tab bar insets.** `UIHostingController` respects `additionalSafeAreaInsets`. If the content overlaps the tab bar, verify safe area propagation.
41
+ Navigation bar behaviour:
75
42
 
76
- ---
43
+ - `.navigationTitle()` and `.toolbar()` inside the hosted view show up in the
44
+ UIKit navigation bar. Whether the bar is visible is decided by the parent
45
+ `UINavigationController`.
46
+ - If `.navigationBarTitleDisplayMode()` does not take effect, set
47
+ `host.navigationItem.largeTitleDisplayMode` from UIKit instead.
48
+ - `UIHostingController` respects `additionalSafeAreaInsets`. If content slides
49
+ under the tab bar, check how the safe area reaches the hosted view.
77
50
 
78
51
  ## 2. UIHostingController as Child
79
52
 
80
- Embed SwiftUI sections within an existing UIKit screen. Use when migrating part of a screen (a header, a card, a section) before rewriting the entire controller.
81
-
82
- ### Implementation
53
+ Use a child hosting controller to migrate a region of a screen, such as a
54
+ header, a card or a section, before rewriting the rest.
83
55
 
84
56
  ```swift
85
- final class DashboardViewController: UIViewController {
86
- private var statsHostingController: UIHostingController<StatsCardView>?
57
+ final class AccountViewController: UIViewController {
58
+ private var balanceHost: UIHostingController<BalanceCard>?
59
+ private let cardContainer = UIView()
87
60
 
88
61
  override func viewDidLoad() {
89
62
  super.viewDidLoad()
90
-
91
- let statsView = StatsCardView(stats: currentStats)
92
- let hostingVC = UIHostingController(rootView: statsView)
93
-
94
- // Enable intrinsic sizing so Auto Layout can size the hosted view
63
+ let host = UIHostingController(rootView: BalanceCard(amount: 0))
95
64
  if #available(iOS 16.0, *) {
96
- hostingVC.sizingOptions = [.intrinsicContentSize]
65
+ host.sizingOptions = [.intrinsicContentSize]
97
66
  }
67
+ host.view.backgroundColor = .clear
98
68
 
99
- addChild(hostingVC)
100
- hostingVC.view.translatesAutoresizingMaskIntoConstraints = false
101
- containerView.addSubview(hostingVC.view)
102
-
69
+ addChild(host)
70
+ host.view.translatesAutoresizingMaskIntoConstraints = false
71
+ cardContainer.addSubview(host.view)
103
72
  NSLayoutConstraint.activate([
104
- hostingVC.view.topAnchor.constraint(equalTo: containerView.topAnchor),
105
- hostingVC.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
106
- hostingVC.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
107
- hostingVC.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
73
+ host.view.topAnchor.constraint(equalTo: cardContainer.topAnchor),
74
+ host.view.bottomAnchor.constraint(equalTo: cardContainer.bottomAnchor),
75
+ host.view.leadingAnchor.constraint(equalTo: cardContainer.leadingAnchor),
76
+ host.view.trailingAnchor.constraint(equalTo: cardContainer.trailingAnchor)
108
77
  ])
109
-
110
- hostingVC.didMove(toParent: self)
111
- statsHostingController = hostingVC
78
+ host.didMove(toParent: self)
79
+ balanceHost = host
112
80
  }
113
81
 
114
- func updateStats(_ stats: Stats) {
115
- statsHostingController?.rootView = StatsCardView(stats: stats)
116
- }
117
- }
118
- ```
119
-
120
- ### With `@Observable` Model
121
-
122
- Pass an `@Observable` model to avoid reassigning `rootView` manually. SwiftUI tracks changes automatically:
123
-
124
- ```swift
125
- @Observable
126
- final class DashboardModel {
127
- var stats: Stats = .empty
128
- var isLoading = false
129
- }
130
-
131
- struct StatsCardView: View {
132
- let model: DashboardModel
133
-
134
- var body: some View {
135
- // Automatically re-renders when model.stats changes
136
- if model.isLoading {
137
- ProgressView()
138
- } else {
139
- StatsGrid(stats: model.stats)
140
- }
82
+ func show(amount: Decimal) {
83
+ balanceHost?.rootView = BalanceCard(amount: amount)
141
84
  }
142
85
  }
143
-
144
- // In UIKit:
145
- let model = DashboardModel()
146
- let hostingVC = UIHostingController(rootView: StatsCardView(model: model))
147
-
148
- // Later -- just mutate the model, no rootView reassignment needed
149
- model.stats = newStats
150
86
  ```
151
87
 
152
- ### Gotchas
88
+ With an `@Observable` model passed into the card instead, mutate the model and
89
+ leave `rootView` alone; SwiftUI picks up the change.
153
90
 
154
- - **Background color.** `UIHostingController`'s view has an opaque system background by default. Set `hostingVC.view.backgroundColor = .clear` if embedding over existing content.
155
- - **sizingOptions on iOS 16+.** Without `.intrinsicContentSize`, the hosted view may report zero size in Auto Layout, causing the container to collapse.
156
- - **Memory.** Store the hosting controller in a property. If it is only held as a child, removing it from the parent deallocates it and the SwiftUI view disappears.
91
+ Pitfalls:
157
92
 
158
- ---
93
+ - By default the hosting view paints the opaque system background. Set
94
+ `view.backgroundColor = .clear` when it sits over existing UIKit content.
95
+ - Without `.intrinsicContentSize` the hosted view can report a zero size and
96
+ the container collapses.
97
+ - Keep a stored reference to the hosting controller. Removing the child would
98
+ otherwise free it and the view vanishes.
159
99
 
160
100
  ## 3. Navigation Bridging
161
101
 
162
- Mix UIKit and SwiftUI screens in the same `UINavigationController` stack.
102
+ UIKit screens and SwiftUI screens can live on the same `UINavigationController`
103
+ stack.
163
104
 
164
- ### UIKit Pushing SwiftUI
105
+ **UIKit pushes SwiftUI.** Wrap the view, give the host a `title`, push it:
165
106
 
166
107
  ```swift
167
- // From a UIKit view controller, push a SwiftUI screen
168
- func showProfile(for user: User) {
169
- let profileView = ProfileView(user: user)
170
- let hostingVC = UIHostingController(rootView: profileView)
171
- hostingVC.title = user.name
172
- navigationController?.pushViewController(hostingVC, animated: true)
173
- }
108
+ let host = UIHostingController(rootView: ReceiptsView())
109
+ host.title = "Receipts"
110
+ navigationController?.pushViewController(host, animated: true)
174
111
  ```
175
112
 
176
- ### SwiftUI Pushing UIKit
177
-
178
- Use a coordinator or `UIViewControllerRepresentable` bridge:
113
+ **SwiftUI presents UIKit.** Present a representable from `.sheet` that returns
114
+ a `UINavigationController(rootViewController:)` around the legacy controller:
179
115
 
180
116
  ```swift
181
- struct ProfileView: View {
182
- let user: User
183
- @State private var showLegacyEditor = false
184
-
185
- var body: some View {
186
- List {
187
- // ... profile content
188
- Button("Edit (Legacy)") { showLegacyEditor = true }
189
- }
190
- .sheet(isPresented: $showLegacyEditor) {
191
- LegacyEditorWrapper(user: user)
192
- }
193
- }
194
- }
195
-
196
- struct LegacyEditorWrapper: UIViewControllerRepresentable {
197
- let user: User
198
-
117
+ struct LegacyExportFlow: UIViewControllerRepresentable {
199
118
  func makeUIViewController(context: Context) -> UINavigationController {
200
- let editor = ProfileEditorViewController(user: user)
201
- return UINavigationController(rootViewController: editor)
119
+ UINavigationController(rootViewController: ExportOptionsViewController())
202
120
  }
203
-
204
- func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
121
+ func updateUIViewController(_ nav: UINavigationController, context: Context) {}
205
122
  }
206
- ```
207
123
 
208
- ### Passing NavigationController Reference
124
+ // .sheet(isPresented: $exporting) { LegacyExportFlow() }
125
+ ```
209
126
 
210
- For deep integration where SwiftUI needs to push onto the UIKit navigation stack:
127
+ **Deep integration.** Give SwiftUI views a small bridge to the UIKit stack
128
+ through the environment:
211
129
 
212
130
  ```swift
213
- struct NavigationBridge {
131
+ struct UIKitNavigator {
214
132
  weak var navigationController: UINavigationController?
215
133
 
216
- func push(_ viewController: UIViewController, animated: Bool = true) {
217
- navigationController?.pushViewController(viewController, animated: animated)
134
+ @MainActor
135
+ func push(_ controller: UIViewController, animated: Bool = true) {
136
+ navigationController?.pushViewController(controller, animated: animated)
218
137
  }
219
138
 
220
- func push<V: View>(_ view: V, title: String? = nil, animated: Bool = true) {
221
- let hostingVC = UIHostingController(rootView: view)
222
- hostingVC.title = title
223
- navigationController?.pushViewController(hostingVC, animated: animated)
139
+ @MainActor
140
+ func push<Content: View>(_ view: Content, title: String, animated: Bool = true) {
141
+ let host = UIHostingController(rootView: view)
142
+ host.title = title
143
+ navigationController?.pushViewController(host, animated: animated)
224
144
  }
225
145
  }
226
146
 
227
- // Inject via environment
228
- private struct NavigationBridgeKey: EnvironmentKey {
229
- static let defaultValue = NavigationBridge()
147
+ private struct UIKitNavigatorKey: EnvironmentKey {
148
+ static var defaultValue: UIKitNavigator { UIKitNavigator() }
230
149
  }
231
150
 
232
151
  extension EnvironmentValues {
233
- var navigationBridge: NavigationBridge {
234
- get { self[NavigationBridgeKey.self] }
235
- set { self[NavigationBridgeKey.self] = newValue }
152
+ var uikitNavigator: UIKitNavigator {
153
+ get { self[UIKitNavigatorKey.self] }
154
+ set { self[UIKitNavigatorKey.self] = newValue }
236
155
  }
237
156
  }
238
157
  ```
239
158
 
240
- ### Gotchas
159
+ Set it on the root view before creating the host:
160
+ `ReceiptsView().environment(\.uikitNavigator, UIKitNavigator(navigationController: navigationController))`.
241
161
 
242
- - **Back button.** When pushing `UIHostingController` onto a `UINavigationController`, the back button works automatically. Do not add a manual back button in the SwiftUI view.
243
- - **Double navigation bars.** If the SwiftUI view uses `NavigationStack`, it creates its own navigation bar inside the UIKit one. Remove `NavigationStack` from SwiftUI views presented inside `UINavigationController`.
244
- - **Toolbar items.** SwiftUI `.toolbar` items propagate to the UIKit navigation bar when hosted in `UIHostingController`. This works reliably on iOS 16+.
162
+ Notes:
245
163
 
246
- ---
164
+ - A pushed hosting controller gets the standard back button automatically. Do
165
+ not add your own.
166
+ - Do not put a `NavigationStack` inside a hosting controller that is already on
167
+ a `UINavigationController`; you get two navigation bars. Remove the
168
+ `NavigationStack`.
169
+ - On iOS 16 and later, SwiftUI `.toolbar` items reliably appear in the UIKit
170
+ navigation bar.
247
171
 
248
172
  ## 4. Data Sharing Between UIKit and SwiftUI
249
173
 
250
- ### Using `@Observable` (iOS 17+)
251
-
252
- The cleanest approach. Create an `@Observable` model, pass it to both UIKit and SwiftUI code:
174
+ **Preferred (iOS 17+): one `@Observable` model for both sides.** SwiftUI tracks
175
+ it automatically; UIKit reads its properties directly.
253
176
 
254
177
  ```swift
255
178
  @Observable
256
- final class AppState {
257
- var currentUser: User?
258
- var unreadCount: Int = 0
259
- var theme: AppTheme = .system
260
- }
261
-
262
- // UIKit side -- read properties directly
263
- let state = AppState()
264
- func viewDidLoad() {
265
- titleLabel.text = state.currentUser?.name
266
- }
267
-
268
- // SwiftUI side -- observation is automatic
269
- struct HeaderView: View {
270
- let state: AppState
271
-
272
- var body: some View {
273
- HStack {
274
- Text(state.currentUser?.name ?? "Guest")
275
- if state.unreadCount > 0 {
276
- Badge(count: state.unreadCount)
277
- }
278
- }
279
- }
179
+ @MainActor
180
+ final class CartModel {
181
+ var itemCount = 0
280
182
  }
281
183
  ```
282
184
 
283
- ### Reactive Updates in UIKit with Combine
284
-
285
- If UIKit code needs to react to `@Observable` changes, bridge with a `withObservationTracking` loop or use `Combine`:
185
+ `@Observable` does not push changes into UIKit by itself. A UIKit controller
186
+ has to observe explicitly, for example with `withObservationTracking` inside a
187
+ long-lived task:
286
188
 
287
189
  ```swift
288
- import Combine
289
- import Observation
190
+ final class CartBadgeViewController: UIViewController {
191
+ private let cart: CartModel
192
+ private var observation: Task<Void, Never>?
290
193
 
291
- final class DashboardViewController: UIViewController {
292
- let state: AppState
293
- private var observationTask: Task<Void, Never>?
194
+ init(cart: CartModel) {
195
+ self.cart = cart
196
+ super.init(nibName: nil, bundle: .main)
197
+ }
198
+ required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
294
199
 
295
200
  override func viewDidLoad() {
296
201
  super.viewDidLoad()
297
- startObserving()
298
- }
299
-
300
- private func startObserving() {
301
- observationTask = Task { @MainActor [weak self] in
202
+ observation = Task { @MainActor [weak self, cart] in
302
203
  while !Task.isCancelled {
303
- guard let self else { return }
304
- withObservationTracking {
305
- self.updateUI(unreadCount: self.state.unreadCount)
306
- } onChange: {
307
- // Triggers next iteration
204
+ await withCheckedContinuation { continuation in
205
+ withObservationTracking {
206
+ self?.title = "\(cart.itemCount) items"
207
+ } onChange: {
208
+ continuation.resume()
209
+ }
308
210
  }
309
- try? await Task.sleep(for: .zero) // Yield to allow onChange to fire
310
211
  }
311
212
  }
312
213
  }
313
214
 
314
- private func updateUI(unreadCount: Int) {
315
- badgeLabel.text = "\(unreadCount)"
316
- }
317
-
318
- deinit { observationTask?.cancel() }
215
+ deinit { observation?.cancel() }
319
216
  }
320
217
  ```
321
218
 
322
- ### Legacy: ObservableObject with Combine
219
+ Each pass applies the current value, registers tracking, and suspends until
220
+ the next change. A variant that loops and yields with `Task.sleep(for: .zero)`
221
+ also works, but it wakes constantly; waiting on the `onChange` signal does not.
222
+ The task holds `self` weakly, and `deinit` cancels it; a cancelled task leaves the loop at the next change.
323
223
 
324
- For iOS 15-16 or existing `ObservableObject` models, subscribe to `objectWillChange`:
224
+ **Legacy (iOS 15 and 16, or an `ObservableObject`).** Subscribe to
225
+ `objectWillChange` with Combine and keep the cancellable:
325
226
 
326
227
  ```swift
327
- final class SettingsViewController: UIViewController {
328
- let settings: SettingsModel // ObservableObject
329
- private var cancellable: AnyCancellable?
330
-
331
- override func viewDidLoad() {
332
- super.viewDidLoad()
333
- cancellable = settings.objectWillChange
334
- .receive(on: RunLoop.main)
335
- .sink { [weak self] _ in
336
- self?.updateUI()
337
- }
228
+ cancellable = store.objectWillChange
229
+ .receive(on: RunLoop.main)
230
+ .sink { [weak self] _ in
231
+ self?.refreshBadge()
338
232
  }
339
- }
340
233
  ```
341
234
 
342
- ### Gotchas
343
-
344
- - **`@Observable` does not trigger UIKit updates automatically.** Unlike SwiftUI views, UIKit code must manually observe changes via `withObservationTracking` or `Combine`.
345
- - **Thread safety.** Mutate `@Observable` properties on `@MainActor` when they drive UI in both UIKit and SwiftUI.
346
- - **Retain cycles.** Use `[weak self]` in Combine sinks and task closures. Store cancellables and tasks, then cancel in `deinit`.
235
+ Rules:
347
236
 
348
- ---
237
+ - Change UI-driving properties of an `@Observable` model on the `@MainActor`.
238
+ - Capture `[weak self]` in sinks and task closures. Keep cancellables and tasks
239
+ in stored properties and cancel them in `deinit`.
349
240
 
350
241
  ## 5. UIHostingConfiguration (iOS 16+)
351
242
 
352
- Render SwiftUI content inside `UICollectionViewCell` and `UITableViewCell` without managing a child `UIHostingController`. This is the preferred approach for cells in a UIKit collection or table view.
353
-
354
- ### UICollectionView with SwiftUI Cells
243
+ This is the preferred way to put SwiftUI content into UIKit collection and
244
+ table view cells.
355
245
 
356
246
  ```swift
357
- @available(iOS 16.0, *)
358
- func collectionView(
359
- _ collectionView: UICollectionView,
360
- cellForItemAt indexPath: IndexPath
361
- ) -> UICollectionViewCell {
362
- let cell = collectionView.dequeueReusableCell(
363
- withReuseIdentifier: "cell",
364
- for: indexPath
365
- )
366
- let item = dataSource[indexPath.item]
367
-
368
- cell.contentConfiguration = UIHostingConfiguration {
369
- HStack {
370
- AsyncImage(url: item.imageURL) { image in
371
- image.resizable().scaledToFill()
372
- } placeholder: {
373
- ProgressView()
374
- }
375
- .frame(width: 60, height: 60)
376
- .clipShape(.rect(cornerRadius: 8))
377
-
378
- VStack(alignment: .leading) {
379
- Text(item.title).font(.headline)
380
- Text(item.subtitle).font(.subheadline).foregroundStyle(.secondary)
381
- }
247
+ cell.contentConfiguration = UIHostingConfiguration {
248
+ HStack(spacing: 12) {
249
+ AsyncImage(url: product.thumbnailURL) { image in
250
+ image.resizable().scaledToFill()
251
+ } placeholder: {
252
+ ProgressView()
382
253
  }
254
+ .frame(width: 56, height: 56)
255
+ .clipShape(.rect(cornerRadius: 8))
256
+ Text(product.name)
383
257
  }
384
- .margins(.all, 12)
385
-
386
- return cell
387
258
  }
259
+ .margins(.all, 12)
388
260
  ```
389
261
 
390
- ### UITableView with SwiftUI Cells
391
-
392
- ```swift
393
- @available(iOS 16.0, *)
394
- func tableView(
395
- _ tableView: UITableView,
396
- cellForRowAt indexPath: IndexPath
397
- ) -> UITableViewCell {
398
- let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
399
- let item = items[indexPath.row]
400
-
401
- cell.contentConfiguration = UIHostingConfiguration {
402
- ItemRowView(item: item)
403
- }
404
-
405
- return cell
406
- }
407
- ```
408
-
409
- ### Self-Sizing
410
-
411
- `UIHostingConfiguration` cells self-size automatically. Ensure:
412
- - The table/collection view uses `UICollectionViewCompositionalLayout` with estimated dimensions, or `tableView.rowHeight = UITableView.automaticDimension`.
413
- - The SwiftUI content has defined height (via content or explicit `.frame`).
414
-
415
- ### Background Customization
262
+ Table views work the same way inside `tableView(_:cellForRowAt:)`:
416
263
 
417
264
  ```swift
265
+ let cell = tableView.dequeueReusableCell(withIdentifier: "order", for: indexPath)
418
266
  cell.contentConfiguration = UIHostingConfiguration {
419
- ItemRowView(item: item)
267
+ OrderRowView(order: orders[indexPath.row])
420
268
  }
421
- .background {
422
- RoundedRectangle(cornerRadius: 12)
423
- .fill(.background)
424
- }
425
- .margins(.horizontal, 16)
426
- .minSize(height: 60)
269
+ return cell
427
270
  ```
428
271
 
429
- ### Gotchas
272
+ Configuration modifiers include `.background { }`, `.margins(.horizontal, 16)`
273
+ and `.minSize(height: 60)`.
430
274
 
431
- - **Performance.** Each `UIHostingConfiguration` creates a lightweight hosting controller. For very large lists (10,000+ items), profile with Instruments to ensure smooth scrolling.
432
- - **State management.** The SwiftUI content inside `UIHostingConfiguration` is recreated on each cell reuse. Do not store `@State` that needs to persist across reuse -- use the data model instead.
433
- - **Swipe actions.** Configure swipe actions in UIKit (`leadingSwipeActionsConfigurationForRowAt`), not inside the SwiftUI content.
434
- - **No `@Environment` propagation by default.** Environment values from the UIKit context are not automatically available. Inject them explicitly in the `UIHostingConfiguration` closure.
275
+ Notes:
435
276
 
436
- ---
277
+ - Self-sizing needs a `UICollectionViewCompositionalLayout` with estimated
278
+ dimensions (for tables, `rowHeight` set to `UITableView.automaticDimension`), plus
279
+ SwiftUI content that has a definite height.
280
+ - Behind every configuration sits a small hosting controller. For huge
281
+ lists (10,000 items and up) measure with Instruments.
282
+ - The content is rebuilt when a cell is reused. `@State` inside it does not
283
+ survive reuse; keep such state in the data model.
284
+ - Swipe actions belong to UIKit (`leadingSwipeActionsConfigurationForRowAt`),
285
+ not to the SwiftUI content.
286
+ - Environment values from the surrounding UIKit context are not passed in
287
+ automatically. Inject what you need inside the configuration closure.
437
288
 
438
289
  ## 6. Environment Bridging
439
290
 
440
- Pass SwiftUI environment values into hosted SwiftUI views from UIKit, and access UIKit traits from SwiftUI.
441
-
442
- ### Injecting Environment into UIHostingController
291
+ Environment has to be attached before the hosting controller exists. Apply
292
+ `.environment(model)` (iOS 17, for an `@Observable` model),
293
+ `.environment(\.locale, ...)` and similar modifiers to the root view, then
294
+ create the host. Adding environment afterwards means
295
+ assigning a new `rootView`.
443
296
 
444
297
  ```swift
445
- let model = AppState()
446
- let settingsView = SettingsView()
447
- .environment(model)
448
- .environment(\.locale, Locale(identifier: "en_US"))
449
-
450
- let hostingVC = UIHostingController(rootView: settingsView)
298
+ let root = SettingsView()
299
+ .environment(session)
300
+ .environment(\.locale, Locale(identifier: "fr_FR"))
301
+ let host = UIHostingController(rootView: root)
451
302
  ```
452
303
 
453
- Apply environment modifiers to the root view before passing it to the hosting controller. The hosting controller does not support adding environment values after creation (you would need to reassign `rootView`).
304
+ Traits that bridge on their own:
454
305
 
455
- ### Trait Collection to SwiftUI Environment
456
-
457
- `UIHostingController` automatically bridges these UIKit trait collections to SwiftUI environment values:
458
-
459
- | UIKit Trait | SwiftUI Environment |
460
- |------------|-------------------|
306
+ | UIKit trait | SwiftUI environment |
307
+ |-------------|---------------------|
461
308
  | `userInterfaceStyle` | `\.colorScheme` |
462
309
  | `horizontalSizeClass` | `\.horizontalSizeClass` |
463
310
  | `verticalSizeClass` | `\.verticalSizeClass` |
@@ -465,70 +312,40 @@ Apply environment modifiers to the root view before passing it to the hosting co
465
312
  | `layoutDirection` | `\.layoutDirection` |
466
313
  | `legibilityWeight` | `\.legibilityWeight` |
467
314
 
468
- These update automatically when the UIKit trait environment changes (device rotation, split view resize, accessibility settings change).
315
+ These stay current as the device rotates, a split view resizes, or an
316
+ accessibility setting changes. `host.overrideUserInterfaceStyle` forces light
317
+ or dark and flows through to `\.colorScheme`.
469
318
 
470
- ### Custom Environment Values Across the Bridge
319
+ **Custom values.** Declare an `EnvironmentKey` with a `defaultValue`, extend
320
+ `EnvironmentValues`, set it from UIKit with `.environment(\.yourKey, value)`
321
+ and read it in SwiftUI with `@Environment(\.yourKey)`. The navigator in pattern
322
+ 3 is an example.
471
323
 
472
- Define a custom environment key and set it from UIKit:
324
+ **Changing environment after creation (iOS 17+).** Wrap the content in a
325
+ container view that reads an `@Observable` state object and applies the environment. Mutating
326
+ that object then updates the environment without touching `rootView`.
473
327
 
474
328
  ```swift
475
- private struct UserRoleKey: EnvironmentKey {
476
- static let defaultValue: UserRole = .guest
477
- }
478
-
479
- extension EnvironmentValues {
480
- var userRole: UserRole {
481
- get { self[UserRoleKey.self] }
482
- set { self[UserRoleKey.self] = newValue }
483
- }
484
- }
485
-
486
- // UIKit side:
487
- let role = authManager.currentRole
488
- let profileView = ProfileView().environment(\.userRole, role)
489
- let hostingVC = UIHostingController(rootView: profileView)
490
-
491
- // SwiftUI side:
492
- struct ProfileView: View {
493
- @Environment(\.userRole) private var role
494
-
495
- var body: some View {
496
- if role == .admin {
497
- AdminDashboard()
498
- } else {
499
- UserDashboard()
500
- }
501
- }
329
+ @Observable
330
+ @MainActor
331
+ final class HostedEnvironment {
332
+ var locale = Locale.current
502
333
  }
503
- ```
504
-
505
- ### Updating Environment After Creation
506
-
507
- To change environment values after the hosting controller is created, wrap the root view in a container that takes a binding or observable:
508
334
 
509
- ```swift
510
335
  struct EnvironmentBridge<Content: View>: View {
511
- let state: AppState // @Observable
512
- let content: Content
336
+ let state: HostedEnvironment
337
+ @ViewBuilder var content: Content
513
338
 
514
339
  var body: some View {
515
- content
516
- .environment(state)
517
- .environment(\.userRole, state.currentRole)
340
+ content.environment(\.locale, state.locale)
518
341
  }
519
342
  }
520
-
521
- // UIKit:
522
- let state = AppState()
523
- let bridge = EnvironmentBridge(state: state, content: SettingsView())
524
- let hostingVC = UIHostingController(rootView: bridge)
525
-
526
- // Later: mutating state.currentRole updates the environment automatically
527
- state.currentRole = .admin
528
343
  ```
529
344
 
530
- ### Gotchas
345
+ Pitfalls:
531
346
 
532
- - **`@Environment(\.dismiss)` in hosted views.** This works when the `UIHostingController` is presented modally (via `present(_:animated:)`). It does NOT work when the hosting controller is pushed onto a `UINavigationController` -- use the navigation controller's `popViewController` instead.
533
- - **Missing environment.** If a SwiftUI view expects an `@Environment` object and it is not provided, the app crashes at runtime. Always set required environment values before creating the hosting controller.
534
- - **Overriding traits.** Use `hostingVC.overrideUserInterfaceStyle` to force light/dark mode for a hosted SwiftUI view. This propagates to `\.colorScheme` automatically.
347
+ - `@Environment(\.dismiss)` inside a hosted view works when the host was shown
348
+ with `present(_:animated:)`. It does nothing when the host was pushed onto a
349
+ `UINavigationController`; call `popViewController(animated:)` there instead.
350
+ - A required environment object that is missing crashes at runtime. Set every
351
+ one before creating the hosting controller.