@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.1

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 (284) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +285 -285
  5. package/package.json +3 -3
  6. package/pipeline/lib/redact.mjs +3 -2
  7. package/pipeline/scripts/_notices.mjs +1 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +13 -1
  9. package/pipeline/scripts/pre-commit-check.sh +4 -0
  10. package/pipeline/skills/.skill-manifest.json +69 -69
  11. package/pipeline/skills/shared/README.md +70 -70
  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/app-clips/SKILL.md +260 -160
  16. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  17. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  18. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  19. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  20. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  21. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  22. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  23. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +345 -277
  24. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  25. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +107 -121
  26. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +145 -165
  27. package/pipeline/skills/shared/external/app-store-review/SKILL.md +306 -326
  28. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  29. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  30. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  31. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +335 -360
  32. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  34. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  35. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  37. package/pipeline/skills/shared/external/authentication/SKILL.md +277 -381
  38. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  39. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +135 -178
  40. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  41. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  42. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  43. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  44. package/pipeline/skills/shared/external/background-processing/SKILL.md +274 -384
  45. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  46. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +173 -321
  47. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  48. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  49. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  50. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  51. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  52. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  53. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  54. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  55. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  56. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +228 -376
  57. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  58. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  59. package/pipeline/skills/shared/external/core-data/SKILL.md +302 -368
  60. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  61. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  62. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  63. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  64. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  65. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  66. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  67. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  68. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  69. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  70. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  71. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  72. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  73. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  74. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  75. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  76. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  77. package/pipeline/skills/shared/external/device-integrity/SKILL.md +236 -353
  78. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  79. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  80. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  81. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  82. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  83. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  84. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  85. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  86. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  87. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  88. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  89. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  90. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  91. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  92. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  93. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  94. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  95. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  96. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  97. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  98. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  99. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  100. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  101. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  102. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  103. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  104. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  105. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  106. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  107. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  108. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  109. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  111. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  112. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  113. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  114. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +3 -3
  115. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +1 -1
  116. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +8 -7
  117. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +2 -2
  118. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +5 -2
  119. package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +100 -0
  120. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +45 -26
  121. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
  122. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +12 -5
  123. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +2 -1
  124. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +6 -5
  125. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +44 -18
  126. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +5 -2
  127. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +10 -11
  128. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +4 -33
  129. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +12 -59
  130. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +297 -267
  131. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  132. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  133. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  134. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  135. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  136. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  137. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  138. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  139. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  140. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  141. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  142. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  143. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  144. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  145. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  146. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  147. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  148. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  149. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  150. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  151. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  152. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  153. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  154. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  155. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  156. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  157. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  158. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  159. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  160. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  161. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  162. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  163. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  164. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  165. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  166. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  167. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  168. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  169. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  170. package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
  171. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  172. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  173. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  174. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  175. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  176. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  177. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +302 -241
  178. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  179. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  180. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  181. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  182. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  183. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  184. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  185. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  186. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  187. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  188. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  189. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  190. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  191. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  192. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +304 -351
  193. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  194. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  195. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  196. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  197. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  198. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  199. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  200. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  201. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  202. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  203. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  204. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  205. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  206. package/pipeline/skills/shared/external/swift-security/SKILL.md +183 -162
  207. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  208. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  209. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +411 -476
  210. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  211. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  212. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  213. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  214. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  215. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  216. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +375 -491
  217. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  218. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  219. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  220. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +397 -457
  221. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  222. package/pipeline/skills/shared/external/swift-testing/SKILL.md +191 -175
  223. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  224. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +81 -84
  225. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  226. package/pipeline/skills/shared/external/swiftdata/SKILL.md +394 -256
  227. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  228. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  229. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  230. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  231. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  232. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  233. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  234. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  235. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  236. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  237. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  238. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  239. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  240. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  241. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  242. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  243. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  244. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  245. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  246. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  247. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  248. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  249. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  250. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  251. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +201 -168
  252. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  253. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +134 -133
  254. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  255. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +111 -138
  256. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  257. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  258. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  259. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  260. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  261. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  262. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  263. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  264. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  265. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  266. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  267. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  268. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  269. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  270. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  271. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  272. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  273. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  274. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  275. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  276. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  277. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  278. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  279. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  280. package/pipeline/skills/shared/external/weatherkit/SKILL.md +160 -315
  281. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  282. package/pipeline/skills/shared/external/widgetkit/SKILL.md +224 -288
  283. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +416 -719
  284. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,430 +1,438 @@
1
1
  ---
2
2
  name: swiftui-uikit-interop
3
- description: "Bridges UIKit and SwiftUI by wrapping UIKit views and view controllers in SwiftUI with UIViewRepresentable and UIViewControllerRepresentable, embedding SwiftUI in UIKit with UIHostingController, and coordinating delegate callbacks. Use when integrating camera previews, map views, mail compose, document scanners, PDF renderers, text views with attributed text, or other UIKit-only or third-party UIKit SDK surfaces into a SwiftUI app, or when migrating a UIKit app to SwiftUI incrementally."
3
+ description: "UIKit and SwiftUI interop: UIViewRepresentable, UIViewControllerRepresentable, UIHostingController, UIHostingConfiguration, Coordinator delegate bridging. Use when SwiftUI needs a camera preview, map view, mail compose, document scanner, PDF renderer, attributed text view or another UIKit-only or third-party UIKit SDK surface, or when migrating a UIKit app to SwiftUI step by step. Not for web content (swiftui-webkit)."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
- # SwiftUI-UIKit Interop
8
+ # SwiftUI and UIKit Interop
9
9
 
10
- Bridge UIKit and SwiftUI in both directions. Wrap UIKit views and view controllers for use in SwiftUI, embed SwiftUI views inside UIKit screens, and synchronize state across the boundary. Targets iOS 26+ with Swift 6.3 patterns; notes backward-compatible to iOS 16 unless stated otherwise.
10
+ SwiftUI and UIKit meet at three seams: a UIKit view or controller shown inside
11
+ SwiftUI (the representable protocols), SwiftUI shown inside UIKit (the hosting
12
+ controller and the hosting cell configuration), and the object that carries
13
+ UIKit callbacks back into SwiftUI state (the Coordinator). This skill covers all
14
+ three. Patterns target iOS 26 and Swift 6.3 and stay usable back to iOS 16
15
+ unless a section says otherwise.
11
16
 
12
- See [references/representable-recipes.md](references/representable-recipes.md) for complete wrapping recipes and [references/hosting-migration.md](references/hosting-migration.md) for UIKit-to-SwiftUI migration patterns.
17
+ Detailed material lives in two references:
13
18
 
14
- ## Contents
15
-
16
- - [UIViewRepresentable Protocol](#uiviewrepresentable-protocol)
17
- - [UIViewControllerRepresentable Protocol](#uiviewcontrollerrepresentable-protocol)
18
- - [The Coordinator Pattern](#the-coordinator-pattern)
19
- - [UIHostingController](#uihostingcontroller)
20
- - [Sizing and Layout](#sizing-and-layout)
21
- - [State Synchronization Patterns](#state-synchronization-patterns)
22
- - [Sendable Considerations](#sendable-considerations)
23
- - [Common Mistakes](#common-mistakes)
24
- - [Review Checklist](#review-checklist)
25
- - [References](#references)
19
+ - [wrapper recipes](references/representable-recipes.md):
20
+ nine complete wrappers (map, attributed text view, camera preview, photo
21
+ picker, mail, share sheet, search bar, PDF view, SMS) with usage and pitfalls.
22
+ - [migration guide](references/hosting-migration.md): moving a
23
+ UIKit app to SwiftUI one piece at a time, including navigation, shared data
24
+ and environment bridging.
26
25
 
27
- ## UIViewRepresentable Protocol
26
+ Embedded web content is out of scope: on iOS 26 use the native SwiftUI WebKit
27
+ types and the `swiftui-webkit` skill.
28
28
 
29
- Use `UIViewRepresentable` to wrap any `UIView` subclass for use in SwiftUI.
29
+ ## Contents
30
30
 
31
- ### Required Methods
31
+ 1. [Wrapping a view](#1-uiviewrepresentable)
32
+ 2. [Wrapping a controller](#2-uiviewcontrollerrepresentable)
33
+ 3. [Coordinators](#3-the-coordinator-pattern)
34
+ 4. [Hosting SwiftUI in a controller](#4-uihostingcontroller)
35
+ 5. [Hosting SwiftUI in cells](#5-uihostingconfiguration-ios-16)
36
+ 6. [Sizing](#6-sizing-and-layout)
37
+ 7. [Keeping state in sync](#7-state-synchronization)
38
+ 8. [Concurrency](#8-swift-concurrency-and-sendable)
39
+ 9. [Mistakes](#9-common-mistakes)
40
+ 10. [Checklist](#10-review-checklist)
41
+
42
+ ## 1. UIViewRepresentable
43
+
44
+ `UIViewRepresentable` lets SwiftUI display any `UIView` subclass. Two methods
45
+ are required:
46
+
47
+ - `makeUIView(context:)` builds and returns the UIKit view. SwiftUI calls it a
48
+ single time, when the representable is inserted. Put one-time setup here:
49
+ delegate assignment, fonts, static configuration.
50
+ - `updateUIView(_:context:)` pushes SwiftUI state into the view. It runs again
51
+ for every relevant state change, so compare before you assign. Writing a value
52
+ the view already holds can trigger a delegate callback that writes the binding
53
+ that calls update again.
32
54
 
33
55
  ```swift
34
- struct WrappedTextView: UIViewRepresentable {
35
- @Binding var text: String
56
+ import UIKit
57
+ import SwiftUI
58
+
59
+ struct PlainTextEditor: UIViewRepresentable {
60
+ @Binding var draft: String
61
+
62
+ func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
36
63
 
37
64
  func makeUIView(context: Context) -> UITextView {
38
- // Called ONCE when SwiftUI inserts this view into the hierarchy.
39
- // Create and return the UIKit view. One-time setup goes here.
40
- let textView = UITextView()
41
- textView.delegate = context.coordinator
42
- textView.font = .preferredFont(forTextStyle: .body)
43
- return textView
65
+ let editor = UITextView(frame: .zero)
66
+ editor.delegate = context.coordinator
67
+ editor.font = .preferredFont(forTextStyle: .body)
68
+ return editor
44
69
  }
45
70
 
46
- func updateUIView(_ uiView: UITextView, context: Context) {
47
- // Called on EVERY SwiftUI state change that affects this view.
48
- // Synchronize SwiftUI state into the UIKit view.
49
- // Guard against redundant updates to avoid loops.
50
- if uiView.text != text {
51
- uiView.text = text
71
+ func updateUIView(_ editor: UITextView, context: Context) {
72
+ context.coordinator.parent = self
73
+ guard editor.text != draft else { return }
74
+ editor.text = draft
75
+ }
76
+
77
+ @MainActor
78
+ final class Coordinator: NSObject, UITextViewDelegate {
79
+ init(parent: PlainTextEditor) { self.parent = parent }
80
+ var parent: PlainTextEditor
81
+
82
+ func textViewDidChange(_ editor: UITextView) {
83
+ parent.draft = editor.text
52
84
  }
53
85
  }
54
86
  }
55
87
  ```
56
88
 
57
- ### Lifecycle Timing
89
+ ### Lifecycle order
58
90
 
59
- | Method | When Called | Purpose |
60
- |--------|-----------|---------|
61
- | `makeCoordinator()` | Before `makeUIView`. Once per representable lifetime. | Create the delegate/datasource reference type. |
62
- | `makeUIView(context:)` | Once, when the representable enters the view tree. | Allocate and configure the UIKit view. |
63
- | `updateUIView(_:context:)` | Immediately after `makeUIView`, then on every relevant state change. | Push SwiftUI state into the UIKit view. |
64
- | `dismantleUIView(_:coordinator:)` | When the representable is removed from the view tree. | Clean up observers, timers, subscriptions. |
65
- | `sizeThatFits(_:uiView:context:)` | During layout, when SwiftUI needs the view's ideal size. iOS 16+. | Return a custom size proposal. |
91
+ | Step | Method | When it runs | Job |
92
+ |------|--------|--------------|-----|
93
+ | 1 | `makeCoordinator()` | Before `makeUIView`, once for the representable's lifetime | Create the reference object that acts as delegate or data source |
94
+ | 2 | `makeUIView(context:)` | Once, when the view joins the tree | Build the UIKit object and set it up |
95
+ | 3 | `updateUIView(_:context:)` | Immediately after make, then on each relevant change | Copy SwiftUI state into UIKit |
96
+ | 4 | `dismantleUIView(_:coordinator:)` | When the view leaves the tree | Tear down: observers removed, timers invalidated, subscriptions cancelled |
97
+ | - | `sizeThatFits(_:uiView:context:)` | While laying out, if SwiftUI wants an ideal size (iOS 16+) | Report a custom size |
66
98
 
67
- **Why `updateUIView` is the most important method:** SwiftUI calls it every time any `@Binding`, `@State`, `@Environment`, or `@Observable` property read by the representable changes. All state synchronization from SwiftUI to UIKit happens here. If you skip a property, the UIKit view will fall out of sync.
99
+ SwiftUI calls `updateUIView` whenever anything the representable reads changes:
100
+ a `@Binding`, `@State`, `@Environment` value or a property of an `@Observable`
101
+ model. A property you forget to apply in `updateUIView` silently goes stale.
68
102
 
69
103
  ### Optional: dismantleUIView
70
104
 
105
+ `dismantleUIView` is a `static` function. SwiftUI hands it both the UIKit view
106
+ and the Coordinator, so anything the coordinator stores can be released there.
107
+
108
+ For a coordinator that keeps a list of cancellable observation tokens:
109
+
71
110
  ```swift
72
- static func dismantleUIView(_ uiView: UITextView, coordinator: Coordinator) {
73
- // Remove observers, invalidate timers, cancel subscriptions.
74
- // The coordinator is passed in so you can access state stored on it.
75
- coordinator.cancellables.removeAll()
111
+ static func dismantleUIView(_ editor: UITextView, coordinator: Coordinator) {
112
+ coordinator.observationTokens.forEach { $0.cancel() }
113
+ coordinator.observationTokens.removeAll()
76
114
  }
77
115
  ```
78
116
 
79
117
  ### Optional: sizeThatFits (iOS 16+)
80
118
 
119
+ A `nil` result means SwiftUI falls back to the view's `intrinsicContentSize`. Return a
120
+ `CGSize` and SwiftUI uses that size instead of its own choice.
121
+
81
122
  ```swift
82
123
  @available(iOS 16.0, *)
83
- func sizeThatFits(
84
- _ proposal: ProposedViewSize,
85
- uiView: UITextView,
86
- context: Context
87
- ) -> CGSize? {
88
- // Return nil to fall back to UIKit's intrinsicContentSize.
89
- // Return a CGSize to override SwiftUI's sizing for this view.
90
- let width = proposal.width ?? UIView.layoutFittingExpandedSize.width
91
- let size = uiView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
92
- return size
124
+ func sizeThatFits(_ proposal: ProposedViewSize,
125
+ uiView editor: UITextView,
126
+ context: Context) -> CGSize? {
127
+ let targetWidth = proposal.width ?? UIView.layoutFittingExpandedSize.width
128
+ return editor.sizeThatFits(
129
+ CGSize(width: targetWidth, height: .greatestFiniteMagnitude)
130
+ )
93
131
  }
94
132
  ```
95
133
 
96
- ## UIViewControllerRepresentable Protocol
134
+ ## 2. UIViewControllerRepresentable
97
135
 
98
- Use `UIViewControllerRepresentable` to wrap a `UIViewController` subclass -- typically for system pickers, document scanners, mail compose, or any controller that presents modally.
136
+ Wrap a `UIViewController` subclass with `UIViewControllerRepresentable`. It is
137
+ the right choice for pickers the system provides, a scanner for documents, the
138
+ mail composer, and other controllers that are normally presented modally.
139
+ The methods mirror the view version: `makeUIViewController(context:)`, `updateUIViewController(_:context:)`
140
+ and `makeCoordinator()`. For a modal controller the update method usually
141
+ has nothing to push after presentation; it only refreshes the coordinator's
142
+ `parent`.
99
143
 
100
144
  ```swift
101
- struct DocumentScannerView: UIViewControllerRepresentable {
102
- @Binding var scannedImages: [UIImage]
103
- @Environment(\.dismiss) private var dismiss
145
+ import VisionKit
146
+ import SwiftUI
147
+
148
+ struct PageScannerView: UIViewControllerRepresentable {
149
+ @Environment(\.dismiss) var close
150
+ @Binding var pages: [UIImage]
151
+
152
+ func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
104
153
 
105
154
  func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
106
- let scanner = VNDocumentCameraViewController()
107
- scanner.delegate = context.coordinator
108
- return scanner
155
+ let camera = VNDocumentCameraViewController()
156
+ camera.delegate = context.coordinator
157
+ return camera
109
158
  }
110
159
 
111
- func updateUIViewController(_ uiViewController: VNDocumentCameraViewController, context: Context) {
112
- // Usually empty for modal controllers -- nothing to push from SwiftUI.
160
+ func updateUIViewController(_ camera: VNDocumentCameraViewController,
161
+ context: Context) {
162
+ context.coordinator.parent = self
113
163
  }
114
-
115
- func makeCoordinator() -> Coordinator { Coordinator(self) }
116
164
  }
117
165
  ```
118
166
 
119
- ### Handling Results from Presented Controllers
167
+ ### Returning results from a presented controller
120
168
 
121
- The coordinator captures delegate callbacks and routes results back to SwiftUI through the parent's `@Binding` or closures:
169
+ Delegate callbacks land in the Coordinator, which passes results on by writing
170
+ a `@Binding` or calling a closure stored on `parent`. Every exit path, success, cancel
171
+ and failure, ends by dismissing.
122
172
 
123
173
  ```swift
124
- extension DocumentScannerView {
125
- final class Coordinator: NSObject, VNDocumentCameraViewControllerDelegate {
126
- let parent: DocumentScannerView
127
-
128
- init(_ parent: DocumentScannerView) { self.parent = parent }
129
-
130
- func documentCameraViewController(
131
- _ controller: VNDocumentCameraViewController,
132
- didFinishWith scan: VNDocumentCameraScan
133
- ) {
134
- parent.scannedImages = (0..<scan.pageCount).map { scan.imageOfPage(at: $0) }
135
- parent.dismiss()
174
+ extension PageScannerView {
175
+ @MainActor
176
+ final class Coordinator: NSObject, @preconcurrency VNDocumentCameraViewControllerDelegate {
177
+ init(parent: PageScannerView) { self.parent = parent }
178
+ var parent: PageScannerView
179
+
180
+ func documentCameraViewController(_ camera: VNDocumentCameraViewController,
181
+ didFinishWith result: VNDocumentCameraScan) {
182
+ parent.pages = (0..<result.pageCount).map { index in result.imageOfPage(at: index) }
183
+ parent.close()
136
184
  }
137
185
 
138
- func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
139
- parent.dismiss()
186
+ func documentCameraViewControllerDidCancel(_ camera: VNDocumentCameraViewController) {
187
+ parent.close()
140
188
  }
141
189
 
142
- func documentCameraViewController(
143
- _ controller: VNDocumentCameraViewController,
144
- didFailWithError error: Error
145
- ) {
146
- parent.dismiss()
190
+ func documentCameraViewController(_ camera: VNDocumentCameraViewController,
191
+ didFailWithError failure: Error) {
192
+ parent.close()
147
193
  }
148
194
  }
149
195
  }
150
196
  ```
151
197
 
152
- ## The Coordinator Pattern
198
+ The coordinator is a `final class` that subclasses `NSObject`, adopts the
199
+ delegate protocol, stores `parent`, and is built from `makeCoordinator()` by
200
+ passing `self`.
201
+
202
+ ## 3. The Coordinator Pattern
153
203
 
154
- ### Why Coordinators Exist
204
+ ### Why it exists
155
205
 
156
- UIKit delegates, data sources, and target-action patterns require a reference type (`class`). SwiftUI representable structs are value types and cannot serve as delegates. The Coordinator is a `class` instance that SwiftUI creates and manages for you -- it lives as long as the representable view.
206
+ Delegates, data sources and target-action receivers in UIKit have to be class
207
+ instances. A representable is a struct, so it cannot play that role. The
208
+ Coordinator is the class that does. SwiftUI creates it, owns it, and keeps it
209
+ alive exactly as long as the representable.
157
210
 
158
- ### Structure
211
+ ### Shape
159
212
 
160
- Always nest the Coordinator inside the representable or in an extension. Store a reference to `parent` (the representable struct) so the coordinator can write back to `@Binding` properties.
213
+ Declare the Coordinator as a nested type of the representable, directly or
214
+ through an extension. Give it a `parent` property holding the representable
215
+ struct; that is how it writes to `@Binding` properties and calls closures.
161
216
 
162
217
  ```swift
163
- struct SearchBarView: UIViewRepresentable {
164
- @Binding var text: String
165
- var onSearch: (String) -> Void
218
+ struct LegacySearchField: UIViewRepresentable {
219
+ @Binding var query: String
220
+ var onSubmit: (String) -> Void
166
221
 
167
- func makeCoordinator() -> Coordinator { Coordinator(self) }
222
+ func makeCoordinator() -> Coordinator { .init(parent: self) }
168
223
 
169
224
  func makeUIView(context: Context) -> UISearchBar {
170
- let bar = UISearchBar()
171
- bar.delegate = context.coordinator // Set delegate HERE, not in updateUIView
225
+ let bar = UISearchBar(frame: .zero)
226
+ bar.delegate = context.coordinator
172
227
  return bar
173
228
  }
174
229
 
175
- func updateUIView(_ uiView: UISearchBar, context: Context) {
176
- if uiView.text != text {
177
- uiView.text = text
178
- }
230
+ func updateUIView(_ bar: UISearchBar, context: Context) {
231
+ context.coordinator.parent = self
232
+ if bar.text != query { bar.text = query }
179
233
  }
180
234
 
235
+ @MainActor
181
236
  final class Coordinator: NSObject, UISearchBarDelegate {
182
- var parent: SearchBarView
183
-
184
- init(_ parent: SearchBarView) { self.parent = parent }
237
+ init(parent: LegacySearchField) { self.parent = parent }
238
+ var parent: LegacySearchField
185
239
 
186
- func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
187
- parent.text = searchText
240
+ func searchBar(_ bar: UISearchBar, textDidChange newText: String) {
241
+ parent.query = newText
188
242
  }
189
243
 
190
- func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
191
- parent.onSearch(parent.text)
192
- searchBar.resignFirstResponder()
244
+ func searchBarSearchButtonClicked(_ bar: UISearchBar) {
245
+ parent.onSubmit(parent.query)
246
+ bar.resignFirstResponder()
193
247
  }
194
248
  }
195
249
  }
196
250
  ```
197
251
 
198
- ### Key Rules
252
+ ### Rules
199
253
 
200
- 1. **Set the delegate in `makeUIView`/`makeUIViewController`, never in `updateUIView`.** The update method runs on every state change -- setting the delegate there causes redundant assignment and can trigger unexpected side effects.
254
+ - Assign the coordinator as delegate in `makeUIView` or
255
+ `makeUIViewController`. Doing it in the update method repeats the assignment
256
+ on every state change and can cause side effects inside the UIKit object.
257
+ - SwiftUI does not refresh `coordinator.parent` for you. The coordinator is
258
+ created once with the first struct value, while SwiftUI builds a new struct
259
+ on every update. Start `updateUIView` (or `updateUIViewController`) with
260
+ `context.coordinator.parent = self`, so the coordinator always reads the
261
+ current bindings and closures before the next delegate callback arrives.
262
+ - When a UIKit object stores a closure that refers to the coordinator, capture
263
+ it as `[weak coordinator]` to avoid a retain cycle.
201
264
 
202
- 2. **The coordinator's `parent` property is updated automatically.** SwiftUI updates the coordinator's reference to the latest representable struct value before each call to `updateUIView`. This means the coordinator always sees current `@Binding` values through `parent`.
265
+ ## 4. UIHostingController
203
266
 
204
- 3. **Use `[weak coordinator]` in closures** to avoid retain cycles between the coordinator and UIKit objects that capture it.
267
+ ### Basic embedding
205
268
 
206
- ## UIHostingController
207
-
208
- Embed SwiftUI views inside UIKit view controllers using `UIHostingController`.
209
-
210
- ### Basic Embedding
269
+ `UIHostingController(rootView:)` puts a SwiftUI view inside a UIKit
270
+ controller. Containment has three steps and the order is fixed:
211
271
 
212
272
  ```swift
213
- final class ProfileViewController: UIViewController {
214
- private let hostingController = UIHostingController(rootView: ProfileView())
273
+ final class ProfileScreenController: UIViewController {
274
+ let summaryHost = UIHostingController(rootView: ProfileSummary(profile: .empty))
215
275
 
216
276
  override func viewDidLoad() {
217
277
  super.viewDidLoad()
278
+ addChild(summaryHost) // 1
218
279
 
219
- // 1. Add as child
220
- addChild(hostingController)
221
-
222
- // 2. Add and constrain the view
223
- hostingController.view.translatesAutoresizingMaskIntoConstraints = false
224
- view.addSubview(hostingController.view)
280
+ let hosted: UIView = summaryHost.view
281
+ hosted.translatesAutoresizingMaskIntoConstraints = false // 2
282
+ view.addSubview(hosted)
225
283
  NSLayoutConstraint.activate([
226
- hostingController.view.topAnchor.constraint(equalTo: view.topAnchor),
227
- hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
228
- hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
229
- hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
284
+ hosted.topAnchor.constraint(equalTo: view.topAnchor),
285
+ hosted.bottomAnchor.constraint(equalTo: view.bottomAnchor),
286
+ hosted.leadingAnchor.constraint(equalTo: view.leadingAnchor),
287
+ hosted.trailingAnchor.constraint(equalTo: view.trailingAnchor)
230
288
  ])
231
289
 
232
- // 3. Notify the child
233
- hostingController.didMove(toParent: self)
290
+ summaryHost.didMove(toParent: self) // 3
234
291
  }
235
292
  }
236
293
  ```
237
294
 
238
- The three-step sequence (addChild, add view, didMove) is mandatory. Skipping any step causes containment callbacks to misfire, which breaks appearance transitions and trait propagation.
295
+ Leave out any step and containment callbacks, appearance transitions and trait
296
+ propagation stop working correctly.
239
297
 
240
- ### Sizing Options (iOS 16+)
298
+ ### Sizing options (iOS 16+)
241
299
 
242
- ```swift
243
- @available(iOS 16.0, *)
244
- hostingController.sizingOptions = [.intrinsicContentSize]
245
- ```
300
+ `sizingOptions` is an option set on the hosting controller:
246
301
 
247
- | Option | Effect |
248
- |--------|--------|
249
- | `.intrinsicContentSize` | The hosting controller's view reports its SwiftUI content size as `intrinsicContentSize`. Use in Auto Layout when the hosted view should size itself. |
250
- | `.preferredContentSize` | Updates `preferredContentSize` to match SwiftUI content. Use when presenting as a popover or form sheet. |
302
+ | Option | Effect | Use for |
303
+ |--------|--------|---------|
304
+ | `.intrinsicContentSize` | The hosting view reports the SwiftUI content size as its `intrinsicContentSize` | Self-sizing under Auto Layout |
305
+ | `.preferredContentSize` | `preferredContentSize` follows the SwiftUI content size | Popovers and form sheets |
251
306
 
252
- ### Updating the Root View
307
+ ### Updating the root view
253
308
 
254
- When data changes in UIKit, push new state into the hosted SwiftUI view:
309
+ To push new data from UIKit, assign a new value: `summaryHost.rootView =
310
+ ProfileSummary(profile: updated)`. If the SwiftUI view instead receives an
311
+ `@Observable` model, SwiftUI tracks its changes and no reassignment is needed.
255
312
 
256
- ```swift
257
- func updateProfile(_ profile: Profile) {
258
- hostingController.rootView = ProfileView(profile: profile)
259
- }
260
- ```
313
+ ## 5. UIHostingConfiguration (iOS 16+)
261
314
 
262
- For observable models, pass an `@Observable` object and SwiftUI tracks changes automatically -- no need to reassign `rootView`.
263
-
264
- ### UIHostingConfiguration (iOS 16+)
265
-
266
- Render SwiftUI content directly inside `UICollectionViewCell` or `UITableViewCell` without managing a child hosting controller:
315
+ For cells, set `contentConfiguration` to a `UIHostingConfiguration`. SwiftUI
316
+ content renders directly in a `UICollectionViewCell` or `UITableViewCell`
317
+ without creating a child hosting controller yourself.
267
318
 
268
319
  ```swift
269
- @available(iOS 16.0, *)
270
- func collectionView(
271
- _ collectionView: UICollectionView,
272
- cellForItemAt indexPath: IndexPath
273
- ) -> UICollectionViewCell {
274
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
320
+ func collectionView(_ grid: UICollectionView,
321
+ cellForItemAt position: IndexPath) -> UICollectionViewCell {
322
+ let contact = contacts[position.item]
323
+ let cell = grid.dequeueReusableCell(withReuseIdentifier: "contact", for: position)
275
324
  cell.contentConfiguration = UIHostingConfiguration {
276
- ItemRow(item: items[indexPath.item])
325
+ ContactRow(contact: contact)
277
326
  }
278
327
  return cell
279
328
  }
280
329
  ```
281
330
 
282
- ## Sizing and Layout
283
-
284
- ### intrinsicContentSize Bridging
331
+ Margins, backgrounds, self-sizing and reuse rules are in
332
+ [the migration guide, pattern 5](references/hosting-migration.md#5-uihostingconfiguration-ios-16).
285
333
 
286
- UIKit views wrapped in `UIViewRepresentable` communicate their natural size to SwiftUI through `intrinsicContentSize`. SwiftUI respects this during layout unless overridden by `frame()` or `fixedSize()`.
334
+ ## 6. Sizing and Layout
287
335
 
288
- ### fixedSize() and frame() Interactions
336
+ A wrapped UIKit view tells SwiftUI its natural size through
337
+ `intrinsicContentSize`. SwiftUI respects it unless `frame()` or `fixedSize()`
338
+ says otherwise.
289
339
 
290
- | SwiftUI Modifier | Effect on Representable |
291
- |-----------------|------------------------|
292
- | No modifier | SwiftUI uses `intrinsicContentSize` as ideal size; the view is flexible. |
293
- | `.fixedSize()` | Forces the representable to its ideal (intrinsic) size in both axes. |
294
- | `.fixedSize(horizontal: true, vertical: false)` | Fixes width to intrinsic; height remains flexible. |
295
- | `.frame(width:height:)` | Overrides the proposed size; UIKit view receives this size. |
340
+ | Modifier on the representable | Result |
341
+ |-------------------------------|--------|
342
+ | none | Intrinsic size is the ideal size; the view can still grow or shrink |
343
+ | `.frame(width:height:)` | Replaces the proposed size; the UIKit view gets exactly that size |
344
+ | `.fixedSize()` | Locked to the intrinsic size in width and height |
345
+ | `.fixedSize(horizontal: true, vertical: false)` | Width locked to intrinsic, height flexible |
296
346
 
297
- ### Auto Layout with UIHostingController
347
+ When SwiftUI is the child inside UIKit, pin the hosting view with constraints
348
+ and set `sizingOptions = [.intrinsicContentSize]`; that is how Auto Layout
349
+ learns the natural size. Self-sizing cells and sections of variable height
350
+ depend on this.
298
351
 
299
- When embedding `UIHostingController` as a child, pin its view with constraints. Use `.sizingOptions = [.intrinsicContentSize]` so Auto Layout can query the SwiftUI content's natural size for self-sizing cells or variable-height sections.
352
+ ## 7. State Synchronization
300
353
 
301
- ## State Synchronization Patterns
354
+ **Two-way with `@Binding`.** The coordinator writes `parent.value` from a
355
+ delegate method; `updateUIView` reads the binding and applies it to the view.
356
+ The `PlainTextEditor` above does both halves (`textViewDidChange(_:)` writes,
357
+ the update method applies).
302
358
 
303
- ### `@Binding`: Two-Way Sync (SwiftUI <-> UIKit)
304
-
305
- Use `@Binding` when both sides read and write the same value. The coordinator writes to `parent.bindingProperty` in delegate callbacks; `updateUIView` reads the binding and pushes it into the UIKit view.
359
+ **One-way with closures.** For events that flow only from UIKit to SwiftUI
360
+ (tap, submit, scan finished), give the representable an optional closure
361
+ property such as `var onScanComplete: ((String) -> Void)?`. The coordinator
362
+ calls `parent.onScanComplete?(code)` when UIKit reports the event, and SwiftUI
363
+ supplies the closure at the call site:
306
364
 
307
365
  ```swift
308
- // SwiftUI -> UIKit: in updateUIView
309
- if uiView.text != text { uiView.text = text }
310
-
311
- // UIKit -> SwiftUI: in Coordinator delegate method
312
- func textViewDidChange(_ textView: UITextView) {
313
- parent.text = textView.text
314
- }
366
+ TicketBarcodeView(onScanComplete: { code in checkIn.validate(code) })
315
367
  ```
316
368
 
317
- ### Closures: One-Way Events (UIKit -> SwiftUI)
318
-
319
- For fire-and-forget events (button tapped, search submitted, scan completed), pass a closure instead of a binding:
369
+ **Environment.** Inside the representable methods read SwiftUI environment
370
+ values from `context.environment`:
320
371
 
321
372
  ```swift
322
- struct WebViewWrapper: UIViewRepresentable {
323
- let url: URL
324
- var onNavigationFinished: ((URL) -> Void)?
373
+ func updateUIView(_ editor: UITextView, context: Context) {
374
+ context.coordinator.parent = self
375
+ let environment = context.environment
376
+ editor.isEditable = environment.isEnabled
377
+ editor.backgroundColor = environment.colorScheme == .dark
378
+ ? .secondarySystemBackground : .systemBackground
325
379
  }
326
380
  ```
327
381
 
328
- ### Environment Values
329
-
330
- Access SwiftUI environment values inside representable methods via `context.environment`:
331
-
332
- ```swift
333
- func updateUIView(_ uiView: UITextView, context: Context) {
334
- let isEnabled = context.environment.isEnabled
335
- uiView.isEditable = isEnabled
336
-
337
- // Respond to color scheme changes
338
- let colorScheme = context.environment.colorScheme
339
- uiView.backgroundColor = colorScheme == .dark ? .systemGray6 : .white
340
- }
341
- ```
342
-
343
- ### Avoiding Update Loops
344
-
345
- `updateUIView` is called whenever SwiftUI state changes -- including changes triggered by the coordinator writing to a `@Binding`. Guard against redundant updates to prevent infinite loops:
346
-
347
- ```swift
348
- func updateUIView(_ uiView: UITextView, context: Context) {
349
- // GUARD: Only update if values actually differ
350
- if uiView.text != text {
351
- uiView.text = text
352
- }
353
- }
354
- ```
355
-
356
- Without the guard, setting `uiView.text` may trigger the delegate's `textViewDidChange`, which writes to `parent.text`, which triggers `updateUIView` again.
357
-
358
- ## Sendable Considerations
359
-
360
- UIKit delegate protocols are not `Sendable`. When the coordinator conforms to a UIKit delegate, it inherits main-actor isolation from UIKit. Mark coordinators `@MainActor` or use `nonisolated` only for methods that truly do not touch UIKit state. In Swift 6 strict concurrency:
361
-
362
- ```swift
363
- @MainActor
364
- final class Coordinator: NSObject, UISearchBarDelegate {
365
- var parent: SearchBarView
366
- init(_ parent: SearchBarView) { self.parent = parent }
367
- // Delegate methods are main-actor-isolated -- safe to access UIKit and @Binding.
368
- }
369
- ```
370
-
371
- If passing closures across isolation boundaries, ensure they are `@Sendable` or captured on the correct actor.
372
-
373
- ## Common Mistakes
374
-
375
- ### DO / DON'T
376
-
377
- **DON'T:** Create the UIKit view in `updateUIView`.
378
- **DO:** Create the view once in `makeUIView`; only configure/update it in `updateUIView`.
379
- *Why:* `updateUIView` runs on every state change. Creating a new view each time destroys all UIKit state (selection, scroll position, first responder) and leaks memory.
380
-
381
- **DON'T:** Set delegates in `updateUIView`.
382
- **DO:** Set delegates in `makeUIView`/`makeUIViewController` only.
383
- *Why:* Redundant delegate assignment on every update can reset internal delegate state in UIKit views like `WKWebView` or `MKMapView`.
384
-
385
- **DON'T:** Hold strong references to the Coordinator from closures.
386
- **DO:** Use `[weak coordinator]` in closures.
387
- *Why:* UIKit objects often store closures (completion handlers, action blocks). A strong reference to the coordinator that holds a reference to the UIKit view creates a retain cycle.
388
-
389
- **DON'T:** Forget to call `parent.dismiss()` or completion handlers.
390
- **DO:** Use the coordinator to track dismissal and invoke `parent.dismiss()` in all delegate exit paths.
391
- *Why:* Modal controllers presented by SwiftUI (via `.sheet`) need their dismiss binding toggled, or the sheet state becomes inconsistent.
392
-
393
- **DON'T:** Ignore `dismantleUIView` for views that hold observers or timers.
394
- **DO:** Clean up `NotificationCenter` observers, `Combine` subscriptions, and `Timer` instances in `dismantleUIView`.
395
- *Why:* Without cleanup, observers and timers continue firing after the view is removed, causing crashes or stale state updates.
396
-
397
- **DON'T:** Force `UIHostingController`'s view to fill the parent without proper constraints.
398
- **DO:** Use Auto Layout constraints or `sizingOptions` for proper embedding.
399
- *Why:* Setting `frame` manually breaks adaptive layout, trait propagation, and safe area handling.
400
-
401
- **DON'T:** Try to use `@State` in the Coordinator -- it is not a `View`.
402
- **DO:** Use regular stored properties on the Coordinator and communicate to SwiftUI via `parent`'s `@Binding` properties.
403
- *Why:* `@State` only works inside `View` conformances. Using it on a class has no effect.
404
-
405
- **DON'T:** Skip the `addChild`/`didMove(toParent:)` dance when embedding `UIHostingController`.
406
- **DO:** Always call `addChild(_:)`, add the view to the hierarchy, then call `didMove(toParent:)`.
407
- *Why:* Skipping containment causes viewWillAppear/viewDidAppear to never fire, breaks trait collection propagation, and causes visual glitches.
408
-
409
- ## Review Checklist
410
-
411
- - [ ] View/controller created in `make*`, not `update*`
412
- - [ ] Coordinator set as delegate in `make*`, not `update*`
413
- - [ ] `@Binding` used for two-way state sync
414
- - [ ] `updateUIView` handles all SwiftUI state changes with redundancy guards
415
- - [ ] `dismantleUIView` cleans up observers/timers if needed
416
- - [ ] No retain cycles between coordinator and closures (`[weak coordinator]`)
417
- - [ ] `UIHostingController` properly added as child (`addChild` + `didMove(toParent:)`)
418
- - [ ] Sizing strategy chosen (`intrinsicContentSize` vs fixed `frame` vs `sizeThatFits`)
419
- - [ ] Environment values read in `updateUIView` via `context.environment` where needed
420
- - [ ] Coordinator marked `@MainActor` for strict concurrency
421
- - [ ] Modal controllers dismiss in all delegate exit paths (success, cancel, error)
422
- - [ ] `UIHostingConfiguration` used for collection/table view cells instead of manual hosting (iOS 16+)
382
+ **Why the equality guard matters.** Assigning to the UIKit view may fire its
383
+ delegate, the delegate writes the binding, the binding change calls
384
+ `updateUIView`, which assigns again. Checking for a difference before assigning
385
+ is what ends that cycle.
386
+
387
+ ## 8. Swift Concurrency and Sendable
388
+
389
+ - Delegate protocols in UIKit are main-actor protocols, not `Sendable` ones. A
390
+ coordinator adopting one is isolated to the main actor through that
391
+ conformance.
392
+ - Some framework delegates carry no isolation at all, for example
393
+ `VNDocumentCameraViewControllerDelegate`, `MFMailComposeViewControllerDelegate`
394
+ and `MFMessageComposeViewControllerDelegate`. A `@MainActor` coordinator that
395
+ adopts one plainly fails in Swift 6 with "conformance ... crosses into main
396
+ actor-isolated code". Write `@preconcurrency` before the protocol name: the
397
+ conformance compiles, and the callbacks are checked at run time to arrive on
398
+ the main thread, which is where these controllers deliver them.
399
+ - Mark coordinators `@MainActor`. Use `nonisolated` only on methods that never
400
+ touch UIKit state.
401
+ - A closure that crosses an isolation boundary must be `@Sendable` or captured
402
+ on the actor it runs on.
403
+
404
+ ## 9. Common Mistakes
405
+
406
+ | Mistake | Consequence | Fix |
407
+ |---------|-------------|-----|
408
+ | Creating the UIKit view inside `updateUIView` | Selection, scroll offset and first responder are lost, and memory leaks | Create once in `makeUIView` |
409
+ | Setting delegates inside `updateUIView` | Objects such as `WKWebView` or `MKMapView` may reset their own delegate bookkeeping | Assign in `make*` |
410
+ | Strong capture of the coordinator in a closure a UIKit object keeps | Retain cycle coordinator to view and back | `[weak coordinator]` |
411
+ | Forgetting `parent.dismiss()` or the completion on one delegate path | A `.sheet` presenting the controller is left in an inconsistent state | Dismiss on every exit path |
412
+ | Leaving `NotificationCenter` observers, Combine subscriptions or `Timer`s alive | They keep firing after removal: crashes and stale updates | Tear down in `dismantleUIView` |
413
+ | Sizing the hosting view by setting `frame` by hand | Safe areas, trait propagation and adaptive layout stop working | Constraints or `sizingOptions` |
414
+ | Using `@State` in a Coordinator | Does nothing, the coordinator is not a `View` | Plain stored properties, talk to SwiftUI through `parent` bindings |
415
+ | Skipping `addChild` or `didMove(toParent:)` | `viewWillAppear` and `viewDidAppear` never run, traits do not propagate, visual glitches | Full containment sequence |
416
+
417
+ ## 10. Review Checklist
418
+
419
+ - [ ] View or controller created in `make*`, never in `update*`
420
+ - [ ] Delegate assignment to the coordinator happens in `make*`, never in `update*`
421
+ - [ ] `@Binding` used where data flows both ways
422
+ - [ ] `updateUIView` applies every piece of state it depends on, each behind a difference check
423
+ - [ ] `dismantleUIView` removes observers and timers where any exist
424
+ - [ ] No retain cycle between coordinator and stored closures (`[weak coordinator]`)
425
+ - [ ] Hosting controller added as a child with `addChild` and `didMove(toParent:)`
426
+ - [ ] Sizing chosen deliberately: `intrinsicContentSize`, fixed `frame`, or `sizeThatFits`
427
+ - [ ] Environment values read from `context.environment` in `updateUIView` where relevant
428
+ - [ ] Coordinators carry `@MainActor` so strict concurrency checking passes
429
+ - [ ] Modal controllers dismiss on success, cancel and error
430
+ - [ ] Cells use `UIHostingConfiguration` rather than hand-managed hosting controllers (iOS 16+)
423
431
 
424
432
  ## References
425
433
 
426
- - Wrapping recipes: [references/representable-recipes.md](references/representable-recipes.md)
427
- - Migration patterns: [references/hosting-migration.md](references/hosting-migration.md)
428
- - Apple docs: [UIViewRepresentable](https://sosumi.ai/documentation/swiftui/UIViewRepresentable)
429
- - Apple docs: [UIViewControllerRepresentable](https://sosumi.ai/documentation/swiftui/UIViewControllerRepresentable)
430
- - Apple docs: [UIHostingController](https://sosumi.ai/documentation/swiftui/UIHostingController)
434
+ - [wrapper recipes](references/representable-recipes.md): complete wrapper recipes
435
+ - [migration guide](references/hosting-migration.md): incremental UIKit to SwiftUI migration
436
+ - Apple: [UIViewRepresentable](https://developer.apple.com/documentation/swiftui/uiviewrepresentable),
437
+ [UIViewControllerRepresentable](https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable),
438
+ [UIHostingController](https://developer.apple.com/documentation/swiftui/uihostingcontroller)