@mmerterden/multi-agent-pipeline 20.7.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 (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. 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)