@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,536 +1,358 @@
1
- # VisionKit Scanner Patterns
1
+ # VisionKit scanners
2
2
 
3
- Complete implementation patterns for DataScannerViewController and
4
- VNDocumentCameraViewController covering availability checking, configuration,
5
- SwiftUI integration, delegate handling, custom overlays, and camera permissions.
6
- All patterns target iOS 26+ with Swift 6.3 unless noted.
3
+ Camera UI that VisionKit provides on top of Vision: the live
4
+ `DataScannerViewController` and the paper-oriented
5
+ `VNDocumentCameraViewController`. Summary in `../SKILL.md`.
7
6
 
8
7
  ## Contents
9
- - Camera Permission Setup
10
- - DataScannerViewController
11
- - Delegate Methods
12
- - SwiftUI Integration
13
- - Custom Overlay UI
14
- - VNDocumentCameraViewController
15
8
 
16
- ## Camera Permission Setup
9
+ - [Camera permission](#camera-permission)
10
+ - [DataScannerViewController](#datascannerviewcontroller)
11
+ - [Delegate callbacks](#delegate-callbacks)
12
+ - [SwiftUI](#swiftui)
13
+ - [Custom overlay](#custom-overlay)
14
+ - [VNDocumentCameraViewController](#vndocumentcameraviewcontroller)
15
+ - [Performance](#performance)
17
16
 
18
- Add the camera usage description to Info.plist before using any scanner:
17
+ ## Camera permission
19
18
 
20
- ```xml
21
- <key>NSCameraUsageDescription</key>
22
- <string>Camera access is needed to scan text and barcodes.</string>
23
- ```
24
-
25
- Request permission before presenting the scanner:
19
+ Both scanners use the camera, so Info.plist needs `NSCameraUsageDescription`
20
+ before either is shown. Ask for access explicitly when you want to control the
21
+ moment of the prompt:
26
22
 
27
23
  ```swift
28
24
  import AVFoundation
29
25
 
30
- func requestCameraAccess() async -> Bool {
26
+ func cameraAllowed() async -> Bool {
31
27
  let status = AVCaptureDevice.authorizationStatus(for: .video)
32
- switch status {
33
- case .authorized:
28
+ if status == .authorized {
34
29
  return true
35
- case .notDetermined:
36
- return await AVCaptureDevice.requestAccess(for: .video)
37
- case .denied, .restricted:
38
- return false
39
- @unknown default:
40
- return false
41
30
  }
31
+ if status == .notDetermined {
32
+ let granted = await AVCaptureDevice.requestAccess(for: .video)
33
+ return granted
34
+ }
35
+ return false
42
36
  }
43
37
  ```
44
38
 
45
- ## DataScannerViewController
39
+ `.denied`, `.restricted` and any status added in a future release all fall
40
+ through to `false`.
46
41
 
47
- `DataScannerViewController` provides a full-screen live camera scanner for text
48
- and barcodes with built-in highlighting and interaction. Available on devices
49
- with an A12 chip or later (iOS 16+).
42
+ ## DataScannerViewController
50
43
 
51
- ### Availability Checking
44
+ A full-screen live scanner with built-in highlighting and tap handling.
45
+ Available from iOS 16 on devices with an A12 Bionic chip or newer.
52
46
 
53
- Always check both hardware support and runtime availability before presenting.
47
+ Two class properties gate it, and both must be true before presenting:
54
48
 
55
- ```swift
56
- import VisionKit
49
+ - `isSupported`: the hardware can run it.
50
+ - `isAvailable`: supported, plus camera access is granted and not blocked by
51
+ restrictions such as device management.
57
52
 
58
- func canUseDataScanner() -> Bool {
59
- // Hardware check: requires A12 Bionic or later
60
- guard DataScannerViewController.isSupported else {
61
- return false
62
- }
63
- // Runtime check: camera authorized and not restricted
64
- guard DataScannerViewController.isAvailable else {
65
- return false
66
- }
67
- return true
68
- }
69
- ```
53
+ ### Initializer options
70
54
 
71
- `isSupported` checks hardware capability (A12+). `isAvailable` checks that the
72
- camera is authorized and not restricted by device management. Both must be true.
55
+ | Parameter | Default | Meaning |
56
+ |---|---|---|
57
+ | `recognizedDataTypes` | required | Set of text and barcode types to find |
58
+ | `qualityLevel` | `.balanced` | Speed versus detail |
59
+ | `recognizesMultipleItems` | `false` | Track many items or only the most prominent |
60
+ | `isHighFrameRateTrackingEnabled` | `true` | Smoother highlight tracking, more power |
61
+ | `isPinchToZoomEnabled` | `true` | Pinch gesture zooms the camera |
62
+ | `isGuidanceEnabled` | `true` | On-screen hints such as "slow down" |
63
+ | `isHighlightingEnabled` | `false` | System highlight boxes around found items |
73
64
 
74
- ### Configuration and Initialization
65
+ Three configurations for different jobs:
75
66
 
76
67
  ```swift
77
68
  import VisionKit
78
69
 
79
- func createTextScanner() -> DataScannerViewController {
80
- DataScannerViewController(
81
- recognizedDataTypes: [
82
- .text(languages: ["en"]),
83
- ],
84
- qualityLevel: .balanced,
85
- recognizesMultipleItems: true,
86
- isHighFrameRateTrackingEnabled: true,
87
- isPinchToZoomEnabled: true,
88
- isGuidanceEnabled: true,
89
- isHighlightingEnabled: true
90
- )
91
- }
70
+ @MainActor
71
+ enum ScannerPresets {
72
+ static func signReader() -> DataScannerViewController {
73
+ DataScannerViewController(
74
+ recognizedDataTypes: [.text()],
75
+ qualityLevel: .accurate,
76
+ recognizesMultipleItems: true,
77
+ isHighFrameRateTrackingEnabled: false,
78
+ isPinchToZoomEnabled: true,
79
+ isGuidanceEnabled: false,
80
+ isHighlightingEnabled: true
81
+ )
82
+ }
92
83
 
93
- func createBarcodeScanner() -> DataScannerViewController {
94
- DataScannerViewController(
95
- recognizedDataTypes: [
96
- .barcode(symbologies: [.qr, .ean13, .code128]),
97
- ],
98
- qualityLevel: .fast,
99
- recognizesMultipleItems: false,
100
- isHighFrameRateTrackingEnabled: false,
101
- isPinchToZoomEnabled: false,
102
- isGuidanceEnabled: true,
103
- isHighlightingEnabled: true
104
- )
105
- }
84
+ static func boardingPassReader() -> DataScannerViewController {
85
+ DataScannerViewController(
86
+ recognizedDataTypes: [.barcode(symbologies: [.pdf417, .aztec, .qr])],
87
+ qualityLevel: .fast,
88
+ isHighFrameRateTrackingEnabled: false,
89
+ isPinchToZoomEnabled: false
90
+ )
91
+ }
106
92
 
107
- func createMixedScanner() -> DataScannerViewController {
108
- DataScannerViewController(
109
- recognizedDataTypes: [
110
- .text(languages: ["en"]),
111
- .barcode(symbologies: [.qr, .ean13]),
112
- ],
113
- qualityLevel: .balanced,
114
- recognizesMultipleItems: true,
115
- isHighFrameRateTrackingEnabled: true,
116
- isPinchToZoomEnabled: true,
117
- isGuidanceEnabled: true,
118
- isHighlightingEnabled: true
119
- )
93
+ static func parcelReader() -> DataScannerViewController {
94
+ DataScannerViewController(
95
+ recognizedDataTypes: [
96
+ .text(textContentType: .shipmentTrackingNumber),
97
+ .barcode(symbologies: [.code128, .qr])
98
+ ],
99
+ qualityLevel: .balanced,
100
+ isHighlightingEnabled: true
101
+ )
102
+ }
120
103
  }
121
104
  ```
122
105
 
123
- ### Recognized Data Types
106
+ ### Data types
124
107
 
125
- ```swift
126
- // Text with language hints
127
- let textType: DataScannerViewController.RecognizedDataType =
128
- .text(languages: ["en", "fr", "de"])
129
-
130
- // Text filtered by content type
131
- let emailType: DataScannerViewController.RecognizedDataType =
132
- .text(textContentType: .emailAddress)
133
- let urlType: DataScannerViewController.RecognizedDataType =
134
- .text(textContentType: .URL)
135
- let phoneType: DataScannerViewController.RecognizedDataType =
136
- .text(textContentType: .telephoneNumber)
137
- let addressType: DataScannerViewController.RecognizedDataType =
138
- .text(textContentType: .fullAddress)
139
- let flightType: DataScannerViewController.RecognizedDataType =
140
- .text(textContentType: .flightNumber)
141
- let trackingType: DataScannerViewController.RecognizedDataType =
142
- .text(textContentType: .shipmentTrackingNumber)
143
-
144
- // Barcode with specific symbologies
145
- let qrOnly: DataScannerViewController.RecognizedDataType =
146
- .barcode(symbologies: [.qr])
147
- let retailBarcodes: DataScannerViewController.RecognizedDataType =
148
- .barcode(symbologies: [.ean8, .ean13, .upce, .code128])
149
- ```
108
+ `DataScannerViewController.RecognizedDataType` values:
150
109
 
151
- ### Quality Levels
110
+ - `.text(languages:textContentType:)`: both arguments are optional, so
111
+ `.text()`, `.text(languages: ["fr"])` and
112
+ `.text(textContentType: .flightNumber)` all work.
113
+ - `.barcode(symbologies:)`: takes `VNBarcodeSymbology` values.
152
114
 
153
- | Level | Use Case | Notes |
154
- |---|---|---|
155
- | `.fast` | Barcode scanning, quick text grab | Lowest latency |
156
- | `.balanced` | General purpose text + barcode | Default choice |
157
- | `.accurate` | Detailed OCR, small text | Higher latency |
115
+ Text content types narrow recognition to one kind of string: `.URL`,
116
+ `.emailAddress`, `.telephoneNumber`, `.fullStreetAddress`, `.flightNumber`,
117
+ `.shipmentTrackingNumber`, `.dateTimeDuration`, and `.currency` (iOS 17).
158
118
 
159
- ### Starting and Stopping
119
+ A retail product scanner usually needs `.ean13`, `.ean8`, `.upce` and
120
+ `.code128`.
160
121
 
161
- ```swift
162
- func presentScanner(_ scanner: DataScannerViewController,
163
- from presenter: UIViewController) {
164
- scanner.delegate = presenter as? DataScannerViewControllerDelegate
165
- presenter.present(scanner, animated: true) {
166
- try? scanner.startScanning()
167
- }
168
- }
122
+ ### Quality levels
169
123
 
170
- func dismissScanner(_ scanner: DataScannerViewController) {
171
- scanner.stopScanning()
172
- scanner.dismiss(animated: true)
173
- }
174
- ```
124
+ - `.fast`: lowest latency; barcodes and large, quick text.
125
+ - `.balanced`: the sensible default for most scanners.
126
+ - `.accurate`: small or dense text; slower to respond.
175
127
 
176
- ## Delegate Methods
128
+ ### Start and stop
177
129
 
178
- Implement `DataScannerViewControllerDelegate` to handle recognized items and
179
- scanner lifecycle events.
130
+ Scanning can only start once the controller is on screen. Set the delegate,
131
+ present, and start in the presentation completion. To finish, call
132
+ `stopScanning()` and then dismiss.
180
133
 
181
134
  ```swift
135
+ import UIKit
182
136
  import VisionKit
183
137
 
184
- final class ScannerCoordinator: NSObject, DataScannerViewControllerDelegate {
185
-
186
- var onTextRecognized: ((String) -> Void)?
187
- var onBarcodeRecognized: ((String, VNBarcodeSymbology) -> Void)?
188
-
189
- // Called when the user taps on a recognized item
190
- func dataScanner(
191
- _ scanner: DataScannerViewController,
192
- didTapOn item: RecognizedItem
193
- ) {
194
- switch item {
195
- case .text(let text):
196
- onTextRecognized?(text.transcript)
197
- case .barcode(let barcode):
198
- if let payload = barcode.payloadStringValue {
199
- onBarcodeRecognized?(payload, barcode.observation.symbology)
200
- }
201
- @unknown default:
202
- break
203
- }
204
- }
205
-
206
- // Called when new items appear in the camera view
207
- func dataScanner(
208
- _ scanner: DataScannerViewController,
209
- didAdd addedItems: [RecognizedItem],
210
- allItems: [RecognizedItem]
211
- ) {
212
- for item in addedItems {
213
- switch item {
214
- case .text(let text):
215
- print("New text: \(text.transcript)")
216
- case .barcode(let barcode):
217
- print("New barcode: \(barcode.payloadStringValue ?? "nil")")
218
- @unknown default:
219
- break
220
- }
138
+ @MainActor
139
+ final class ScanCoordinator {
140
+ private var live: DataScannerViewController?
141
+
142
+ func open(from host: UIViewController, delegate: DataScannerViewControllerDelegate) {
143
+ let passReader = ScannerPresets.boardingPassReader()
144
+ passReader.delegate = delegate
145
+ live = passReader
146
+ host.present(passReader, animated: true) {
147
+ try? passReader.startScanning()
221
148
  }
222
149
  }
223
150
 
224
- // Called when items are updated (position or content changes)
225
- func dataScanner(
226
- _ scanner: DataScannerViewController,
227
- didUpdate updatedItems: [RecognizedItem],
228
- allItems: [RecognizedItem]
229
- ) {
230
- // Handle position or content updates
231
- }
232
-
233
- // Called when items leave the camera view
234
- func dataScanner(
235
- _ scanner: DataScannerViewController,
236
- didRemove removedItems: [RecognizedItem],
237
- allItems: [RecognizedItem]
238
- ) {
239
- // Clean up UI for removed items
240
- }
241
-
242
- // Called when the scanner becomes unavailable (e.g., camera revoked)
243
- func dataScannerDidChangeUnavailabilityReasons(
244
- _ scanner: DataScannerViewController
245
- ) {
246
- // Handle unavailability -- dismiss or show fallback
151
+ func close() {
152
+ guard let live else { return }
153
+ live.stopScanning()
154
+ live.dismiss(animated: true)
155
+ self.live = nil
247
156
  }
248
157
  }
249
158
  ```
250
159
 
251
- ### Async Sequence for Recognized Items
160
+ ## Delegate callbacks
161
+
162
+ `DataScannerViewControllerDelegate` methods, all main-actor isolated:
163
+
164
+ | Method | When it fires |
165
+ |---|---|
166
+ | `dataScanner(_:didTapOn:)` | The user tapped a recognized item |
167
+ | `dataScanner(_:didAdd:allItems:)` | New items appeared |
168
+ | `dataScanner(_:didUpdate:allItems:)` | Items moved or their content changed |
169
+ | `dataScanner(_:didRemove:allItems:)` | Items left the frame |
170
+ | `dataScanner(_:becameUnavailableWithError:)` | Scanning stopped being possible, for example camera access was revoked; dismiss or fall back |
171
+ | `dataScannerDidZoom(_:)` | Zoom factor changed |
252
172
 
253
- Use `recognizedItems` for a reactive stream of all currently visible items:
173
+ `RecognizedItem` is an enum. Handle both cases and keep an `@unknown default`:
254
174
 
255
175
  ```swift
256
- func observeRecognizedItems(_ scanner: DataScannerViewController) async {
257
- for await items in scanner.recognizedItems {
258
- let texts = items.compactMap { item -> String? in
259
- guard case .text(let text) = item else { return nil }
260
- return text.transcript
261
- }
262
- let barcodes = items.compactMap { item -> String? in
263
- guard case .barcode(let barcode) = item else { return nil }
264
- return barcode.payloadStringValue
265
- }
266
- await MainActor.run {
267
- // Update UI with current texts and barcodes
268
- }
176
+ import VisionKit
177
+
178
+ func describe(_ item: RecognizedItem) -> String {
179
+ switch item {
180
+ case .text(let recognized):
181
+ return recognized.transcript
182
+ case .barcode(let code):
183
+ return "\(code.observation.symbology.rawValue): \(code.payloadStringValue ?? "?")"
184
+ @unknown default:
185
+ return ""
269
186
  }
270
187
  }
271
188
  ```
272
189
 
273
- ### Capturing a Photo
190
+ ### Async stream of items
274
191
 
275
- Capture a still image from the scanner for further processing:
192
+ `recognizedItems` is an `AsyncStream<[RecognizedItem]>`: every element is the
193
+ full set of items currently in view.
276
194
 
277
195
  ```swift
278
- func captureAndProcess(_ scanner: DataScannerViewController) async throws {
279
- let photo = try await scanner.capturePhoto()
280
- // photo is a UIImage -- process with Vision or save
196
+ import VisionKit
197
+
198
+ @MainActor
199
+ func watch(_ scanner: DataScannerViewController,
200
+ onText: ([String]) -> Void,
201
+ onCodes: ([String]) -> Void) async {
202
+ for await visible in scanner.recognizedItems {
203
+ var lines: [String] = []
204
+ var codes: [String] = []
205
+ for item in visible {
206
+ switch item {
207
+ case .text(let recognized): lines.append(recognized.transcript)
208
+ case .barcode(let code): codes.append(code.payloadStringValue ?? "")
209
+ @unknown default: break
210
+ }
211
+ }
212
+ onText(lines)
213
+ onCodes(codes)
214
+ }
281
215
  }
282
216
  ```
283
217
 
284
- ## SwiftUI Integration
218
+ ### Still photo
285
219
 
286
- Wrap `DataScannerViewController` in `UIViewControllerRepresentable` for use
287
- in SwiftUI views.
220
+ `try await scanner.capturePhoto()` returns a `UIImage` of the current camera
221
+ frame, useful for a high-resolution OCR pass or for attaching to a record.
288
222
 
289
- ### Full DataScanner Representable
223
+ ## SwiftUI
224
+
225
+ A representable that reports tapped items through bindings:
290
226
 
291
227
  ```swift
292
- import SwiftUI
293
228
  import VisionKit
229
+ import SwiftUI
294
230
 
295
- struct DataScannerRepresentable: UIViewControllerRepresentable {
296
- let recognizedDataTypes: Set<DataScannerViewController.RecognizedDataType>
297
- let qualityLevel: DataScannerViewController.QualityLevel
298
- let recognizesMultipleItems: Bool
299
- @Binding var recognizedText: [String]
300
- @Binding var recognizedBarcodes: [String]
231
+ struct LiveScanner: UIViewControllerRepresentable {
232
+ let kinds: Set<DataScannerViewController.RecognizedDataType>
233
+ var quality: DataScannerViewController.QualityLevel = .balanced
234
+ var many = false
235
+ @Binding var lines: [String]
236
+ @Binding var payloads: [String]
301
237
 
302
238
  func makeUIViewController(context: Context) -> DataScannerViewController {
303
- let scanner = DataScannerViewController(
304
- recognizedDataTypes: recognizedDataTypes,
305
- qualityLevel: qualityLevel,
306
- recognizesMultipleItems: recognizesMultipleItems,
307
- isHighFrameRateTrackingEnabled: true,
308
- isPinchToZoomEnabled: true,
309
- isGuidanceEnabled: true,
239
+ let liveView = DataScannerViewController(
240
+ recognizedDataTypes: kinds,
241
+ qualityLevel: quality,
242
+ recognizesMultipleItems: many,
310
243
  isHighlightingEnabled: true
311
244
  )
312
- scanner.delegate = context.coordinator
313
- return scanner
245
+ liveView.delegate = context.coordinator
246
+ Task { @MainActor in
247
+ try? liveView.startScanning()
248
+ }
249
+ return liveView
314
250
  }
315
251
 
316
- func updateUIViewController(
317
- _ controller: DataScannerViewController,
318
- context: Context
319
- ) {
320
- // No dynamic updates needed
321
- }
252
+ func updateUIViewController(_ scannerController: DataScannerViewController, context: Context) {}
322
253
 
323
- func makeCoordinator() -> Coordinator {
324
- Coordinator(parent: self)
254
+ func makeCoordinator() -> Tapper {
255
+ Tapper(owner: self)
325
256
  }
326
257
 
327
- static func dismantleUIViewController(
328
- _ controller: DataScannerViewController,
329
- coordinator: Coordinator
330
- ) {
331
- controller.stopScanning()
258
+ static func dismantleUIViewController(_ scannerController: DataScannerViewController, coordinator: Tapper) {
259
+ scannerController.stopScanning()
332
260
  }
333
261
 
334
262
  @MainActor
335
- final class Coordinator: NSObject, DataScannerViewControllerDelegate {
336
- let parent: DataScannerRepresentable
263
+ final class Tapper: NSObject, DataScannerViewControllerDelegate {
264
+ private let owner: LiveScanner
337
265
 
338
- init(parent: DataScannerRepresentable) {
339
- self.parent = parent
266
+ init(owner: LiveScanner) {
267
+ self.owner = owner
340
268
  }
341
269
 
342
- func dataScanner(
343
- _ scanner: DataScannerViewController,
344
- didTapOn item: RecognizedItem
345
- ) {
270
+ func dataScanner(_ dataScanner: DataScannerViewController, didTapOn item: RecognizedItem) {
346
271
  switch item {
347
- case .text(let text):
348
- parent.recognizedText.append(text.transcript)
349
- case .barcode(let barcode):
350
- if let payload = barcode.payloadStringValue {
351
- parent.recognizedBarcodes.append(payload)
272
+ case .text(let recognized):
273
+ owner.lines.append(recognized.transcript)
274
+ case .barcode(let code):
275
+ if let value = code.payloadStringValue {
276
+ owner.payloads.append(value)
352
277
  }
353
278
  @unknown default:
354
279
  break
355
280
  }
356
281
  }
357
-
358
- func dataScanner(
359
- _ scanner: DataScannerViewController,
360
- didAdd addedItems: [RecognizedItem],
361
- allItems: [RecognizedItem]
362
- ) {
363
- // Handle newly recognized items
364
- }
365
-
366
- func dataScanner(
367
- _ scanner: DataScannerViewController,
368
- didUpdate updatedItems: [RecognizedItem],
369
- allItems: [RecognizedItem]
370
- ) {
371
- // Handle item updates
372
- }
373
-
374
- func dataScanner(
375
- _ scanner: DataScannerViewController,
376
- didRemove removedItems: [RecognizedItem],
377
- allItems: [RecognizedItem]
378
- ) {
379
- // Handle removed items
380
- }
381
282
  }
382
283
  }
383
284
  ```
384
285
 
385
- ### SwiftUI Scanner View
286
+ `startScanning()` must wait until the controller is presented. The deferred
287
+ `Task` above runs after `makeUIViewController` returns; stopping belongs in
288
+ `dismantleUIViewController`.
289
+
290
+ A screen that offers the scanner only where it can run:
386
291
 
387
292
  ```swift
388
- import SwiftUI
389
293
  import VisionKit
294
+ import SwiftUI
390
295
 
391
- struct ScannerView: View {
392
- @State private var recognizedText: [String] = []
393
- @State private var recognizedBarcodes: [String] = []
394
- @State private var isShowingScanner = false
296
+ struct ShelfScanScreen: View {
297
+ @State private var scanning = false
298
+ @State private var lines: [String] = []
299
+ @State private var payloads: [String] = []
395
300
 
396
301
  var body: some View {
397
- VStack {
302
+ Form {
398
303
  if DataScannerViewController.isSupported {
399
- Button("Scan") {
400
- isShowingScanner = true
401
- }
402
- .fullScreenCover(isPresented: $isShowingScanner) {
403
- NavigationStack {
404
- DataScannerRepresentable(
405
- recognizedDataTypes: [
406
- .text(languages: ["en"]),
407
- .barcode(symbologies: [.qr]),
408
- ],
409
- qualityLevel: .balanced,
410
- recognizesMultipleItems: true,
411
- recognizedText: $recognizedText,
412
- recognizedBarcodes: $recognizedBarcodes
413
- )
414
- .ignoresSafeArea()
415
- .toolbar {
416
- ToolbarItem(placement: .cancellationAction) {
417
- Button("Done") {
418
- isShowingScanner = false
419
- }
420
- }
421
- }
422
- }
423
- }
304
+ Button("Scan labels") { scanning = true }
424
305
  } else {
425
- ContentUnavailableView(
426
- "Scanner Not Available",
427
- systemImage: "camera.fill",
428
- description: Text("This device does not support scanning.")
429
- )
306
+ ContentUnavailableView("Scanning unavailable",
307
+ systemImage: "camera.fill",
308
+ description: Text("Live scanning needs a newer iPhone or iPad."))
430
309
  }
431
-
432
- List {
433
- Section("Text") {
434
- ForEach(recognizedText, id: \.self) { text in
435
- Text(text)
436
- }
437
- }
438
- Section("Barcodes") {
439
- ForEach(recognizedBarcodes, id: \.self) { barcode in
440
- Text(barcode)
310
+ Section("Text") { ForEach(lines, id: \.self, content: Text.init) }
311
+ Section("Codes") { ForEach(payloads, id: \.self, content: Text.init) }
312
+ }
313
+ .fullScreenCover(isPresented: $scanning) {
314
+ NavigationStack {
315
+ LiveScanner(kinds: [.text(), .barcode()], lines: $lines, payloads: $payloads)
316
+ .ignoresSafeArea()
317
+ .toolbar {
318
+ ToolbarItem(placement: .cancellationAction) {
319
+ Button("Close") { scanning = false }
320
+ }
441
321
  }
442
- }
443
322
  }
444
323
  }
445
324
  }
446
325
  }
447
326
  ```
448
327
 
449
- ### Starting the Scanner After Presentation
328
+ ## Custom overlay
450
329
 
451
- The scanner must be started after the view controller is fully presented.
452
- Use `onAppear` with a coordinator flag or start in the completion handler:
330
+ Layer your own guidance on top of the scanner with a `ZStack`:
453
331
 
454
332
  ```swift
455
- struct AutoStartScannerRepresentable: UIViewControllerRepresentable {
456
- func makeUIViewController(context: Context) -> DataScannerViewController {
457
- let scanner = DataScannerViewController(
458
- recognizedDataTypes: [.text(languages: ["en"])],
459
- qualityLevel: .balanced,
460
- recognizesMultipleItems: false,
461
- isHighFrameRateTrackingEnabled: true,
462
- isHighlightingEnabled: true
463
- )
464
- scanner.delegate = context.coordinator
465
- // Start scanning after a brief delay to ensure presentation is complete
466
- Task { @MainActor in
467
- try? scanner.startScanning()
468
- }
469
- return scanner
470
- }
471
-
472
- func updateUIViewController(
473
- _ controller: DataScannerViewController,
474
- context: Context
475
- ) {}
476
-
477
- func makeCoordinator() -> ScannerCoordinator {
478
- ScannerCoordinator()
479
- }
480
-
481
- static func dismantleUIViewController(
482
- _ controller: DataScannerViewController,
483
- coordinator: ScannerCoordinator
484
- ) {
485
- controller.stopScanning()
486
- }
487
- }
488
- ```
489
-
490
- ## Custom Overlay UI
491
-
492
- Add custom views on top of the scanner for region-of-interest indicators,
493
- instructions, or result display.
494
-
495
- ### Overlay with Region of Interest
333
+ import VisionKit
334
+ import SwiftUI
496
335
 
497
- ```swift
498
- struct ScannerWithOverlay: View {
499
- @State private var isShowingScanner = false
500
- @State private var lastScannedText = ""
336
+ struct FramedScanner: View {
337
+ @State private var lines: [String] = []
338
+ @State private var payloads: [String] = []
501
339
 
502
340
  var body: some View {
503
341
  ZStack {
504
- AutoStartScannerRepresentable()
342
+ LiveScanner(kinds: [.barcode()], lines: $lines, payloads: $payloads)
505
343
  .ignoresSafeArea()
506
-
507
- VStack {
508
- // Top instruction bar
509
- Text("Point camera at text or barcode")
510
- .font(.subheadline)
511
- .padding(.horizontal)
512
- .padding(.vertical)
344
+ VStack(spacing: 24) {
345
+ Text("Fit the code inside the frame")
346
+ .padding(.horizontal, 16)
347
+ .padding(.vertical, 8)
513
348
  .background(.ultraThinMaterial, in: Capsule())
514
- .padding(.top)
515
-
516
- Spacer()
517
-
518
- // Scan region indicator
519
- RoundedRectangle(cornerRadius: 12)
520
- .strokeBorder(.white.opacity(0.6), lineWidth: 2)
521
- .frame(width: 280, height: 180)
522
-
523
- Spacer()
524
-
525
- // Result display
526
- if !lastScannedText.isEmpty {
527
- Text(lastScannedText)
528
- .font(.body)
529
- .padding()
530
- .frame(maxWidth: .infinity)
531
- .background(.ultraThinMaterial)
532
- .clipShape(.rect(cornerRadius: 12))
349
+ RoundedRectangle(cornerRadius: 16)
350
+ .stroke(.white.opacity(0.7), lineWidth: 3)
351
+ .frame(width: 260, height: 260)
352
+ if let latest = payloads.last {
353
+ Text(latest)
533
354
  .padding()
355
+ .background(.thinMaterial, in: .rect(cornerRadius: 12))
534
356
  }
535
357
  }
536
358
  }
@@ -538,201 +360,203 @@ struct ScannerWithOverlay: View {
538
360
  }
539
361
  ```
540
362
 
363
+ Sizes in these examples are placeholders; take them from the design system.
364
+
541
365
  ## VNDocumentCameraViewController
542
366
 
543
- `VNDocumentCameraViewController` provides a full-screen document camera with
544
- auto-capture, perspective correction, and multi-page scanning. Available on
545
- all devices running iOS 13+.
367
+ Presents a camera that fills the screen, snaps pages on its own, straightens
368
+ them, and collects several pages per session. It works on every device that runs iOS 13 or later.
546
369
 
547
- ### UIKit Presentation
370
+ A small helper used by both versions below:
548
371
 
549
372
  ```swift
550
373
  import VisionKit
551
374
 
552
- final class DocumentScannerPresenter: NSObject,
553
- VNDocumentCameraViewControllerDelegate
554
- {
555
- weak var presenter: UIViewController?
375
+ extension VNDocumentCameraScan {
376
+ var allPages: [UIImage] {
377
+ var collected: [UIImage] = []
378
+ for index in 0..<pageCount {
379
+ collected.append(imageOfPage(at: index))
380
+ }
381
+ return collected
382
+ }
383
+ }
384
+ ```
385
+
386
+ UIKit:
387
+
388
+ ```swift
389
+ import UIKit
390
+ import VisionKit
556
391
 
557
- func showDocumentScanner() {
558
- let scanner = VNDocumentCameraViewController()
559
- scanner.delegate = self
560
- presenter?.present(scanner, animated: true)
392
+ @MainActor
393
+ final class PaperScanFlow: NSObject, @MainActor VNDocumentCameraViewControllerDelegate {
394
+ private weak var host: UIViewController?
395
+ var onPages: ([UIImage]) -> Void = { _ in }
396
+ var onFailure: (Error) -> Void = { _ in }
397
+
398
+ func start(from host: UIViewController) {
399
+ self.host = host
400
+ let camera = VNDocumentCameraViewController()
401
+ camera.delegate = self
402
+ host.present(camera, animated: true)
561
403
  }
562
404
 
563
- func documentCameraViewController(
564
- _ controller: VNDocumentCameraViewController,
565
- didFinishWith scan: VNDocumentCameraScan
566
- ) {
567
- controller.dismiss(animated: true)
568
- for pageIndex in 0..<scan.pageCount {
569
- let pageImage = scan.imageOfPage(at: pageIndex)
570
- // Process each scanned page image
571
- }
405
+ func documentCameraViewController(_ camera: VNDocumentCameraViewController,
406
+ didFinishWith scan: VNDocumentCameraScan) {
407
+ camera.dismiss(animated: true)
408
+ onPages(scan.allPages)
572
409
  }
573
410
 
574
- func documentCameraViewControllerDidCancel(
575
- _ controller: VNDocumentCameraViewController
576
- ) {
577
- controller.dismiss(animated: true)
411
+ func documentCameraViewControllerDidCancel(_ camera: VNDocumentCameraViewController) {
412
+ camera.dismiss(animated: true)
578
413
  }
579
414
 
580
- func documentCameraViewController(
581
- _ controller: VNDocumentCameraViewController,
582
- didFailWithError error: Error
583
- ) {
584
- controller.dismiss(animated: true)
585
- // Handle scanning error
415
+ func documentCameraViewController(_ camera: VNDocumentCameraViewController,
416
+ didFailWithError error: Error) {
417
+ camera.dismiss(animated: true)
418
+ onFailure(error)
586
419
  }
587
420
  }
588
421
  ```
589
422
 
590
- ### SwiftUI Document Scanner
423
+ `VNDocumentCameraViewControllerDelegate` carries no actor annotation, so the
424
+ conformance is written as `@MainActor VNDocumentCameraViewControllerDelegate`
425
+ (an isolated conformance, Swift 6.2 and later). The presenter is held `weak`
426
+ so the flow object never keeps a dismissed screen alive.
427
+
428
+ SwiftUI:
591
429
 
592
430
  ```swift
593
- import SwiftUI
594
431
  import VisionKit
432
+ import SwiftUI
595
433
 
596
- struct DocumentScannerRepresentable: UIViewControllerRepresentable {
597
- @Binding var scannedImages: [UIImage]
598
- @Environment(\.dismiss) private var dismiss
434
+ struct PaperCamera: UIViewControllerRepresentable {
435
+ @Binding var pages: [UIImage]
436
+ @Environment(\.dismiss) private var close
599
437
 
600
438
  func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
601
- let scanner = VNDocumentCameraViewController()
602
- scanner.delegate = context.coordinator
603
- return scanner
439
+ let camera = VNDocumentCameraViewController()
440
+ camera.delegate = context.coordinator
441
+ return camera
604
442
  }
605
443
 
606
- func updateUIViewController(
607
- _ controller: VNDocumentCameraViewController,
608
- context: Context
609
- ) {}
444
+ func updateUIViewController(_ camera: VNDocumentCameraViewController, context: Context) {}
610
445
 
611
- func makeCoordinator() -> Coordinator {
612
- Coordinator(parent: self)
446
+ func makeCoordinator() -> Receiver {
447
+ Receiver(owner: self)
613
448
  }
614
449
 
615
450
  @MainActor
616
- final class Coordinator: NSObject, VNDocumentCameraViewControllerDelegate {
617
- let parent: DocumentScannerRepresentable
451
+ final class Receiver: NSObject, @MainActor VNDocumentCameraViewControllerDelegate {
452
+ private let owner: PaperCamera
618
453
 
619
- init(parent: DocumentScannerRepresentable) {
620
- self.parent = parent
454
+ init(owner: PaperCamera) {
455
+ self.owner = owner
621
456
  }
622
457
 
623
- func documentCameraViewController(
624
- _ controller: VNDocumentCameraViewController,
625
- didFinishWith scan: VNDocumentCameraScan
626
- ) {
627
- parent.scannedImages = (0..<scan.pageCount).map { scan.imageOfPage(at: $0) }
628
- parent.dismiss()
458
+ func documentCameraViewController(_ camera: VNDocumentCameraViewController,
459
+ didFinishWith scan: VNDocumentCameraScan) {
460
+ owner.pages = scan.allPages
461
+ owner.close()
629
462
  }
630
463
 
631
- func documentCameraViewControllerDidCancel(
632
- _ controller: VNDocumentCameraViewController
633
- ) {
634
- parent.dismiss()
464
+ func documentCameraViewControllerDidCancel(_ camera: VNDocumentCameraViewController) {
465
+ owner.close()
635
466
  }
636
467
 
637
- func documentCameraViewController(
638
- _ controller: VNDocumentCameraViewController,
639
- didFailWithError error: Error
640
- ) {
641
- parent.dismiss()
468
+ func documentCameraViewController(_ camera: VNDocumentCameraViewController,
469
+ didFailWithError error: Error) {
470
+ owner.close()
642
471
  }
643
472
  }
644
473
  }
645
474
  ```
646
475
 
647
- ### Document Scanner with OCR Pipeline
476
+ ### Scan then read
648
477
 
649
- Combine document scanning with Vision text recognition for a complete OCR flow:
478
+ A model that runs OCR over every captured page, and a view that drives it:
650
479
 
651
480
  ```swift
481
+ import Observation
652
482
  import SwiftUI
653
- import VisionKit
654
483
  import Vision
484
+ import VisionKit
655
485
 
656
486
  @MainActor
657
487
  @Observable
658
- final class DocumentOCRModel {
659
- var scannedPages: [UIImage] = []
660
- var extractedText: [String] = []
661
- var isProcessing = false
662
-
663
- func processScannedPages() async {
664
- isProcessing = true
665
- defer { isProcessing = false }
666
-
667
- extractedText = []
668
- for page in scannedPages {
669
- guard let cgImage = page.cgImage else { continue }
488
+ final class PaperReader {
489
+ var pages: [UIImage] = []
490
+ var transcripts: [String] = []
491
+ var busy = false
492
+
493
+ func transcribe() async {
494
+ busy = true
495
+ defer { busy = false }
496
+ var results: [String] = []
497
+ for page in pages {
498
+ guard let bitmap = page.cgImage else {
499
+ results.append("(page could not be read)")
500
+ continue
501
+ }
502
+ var request = RecognizeTextRequest()
503
+ request.usesLanguageCorrection = true
504
+ request.recognitionLanguages = [Locale.Language(identifier: "en-GB")]
505
+ request.recognitionLevel = .accurate
670
506
  do {
671
- var request = RecognizeTextRequest()
672
- request.recognitionLevel = .accurate
673
- request.recognitionLanguages = [Locale.Language(identifier: "en-US")]
674
- request.usesLanguageCorrection = true
675
-
676
- let observations = try await request.perform(on: cgImage)
677
- let pageText = observations
678
- .compactMap { $0.topCandidates(1).first?.string }
679
- .joined(separator: "\n")
680
- extractedText.append(pageText)
507
+ let found = try await request.perform(on: bitmap)
508
+ let body = found.compactMap { $0.topCandidates(1).first }.map(\.string)
509
+ results.append(body.joined(separator: "\n"))
681
510
  } catch {
682
- extractedText.append("[Recognition failed]")
511
+ results.append("(recognition failed)")
683
512
  }
684
513
  }
514
+ transcripts = results
685
515
  }
686
516
  }
687
517
 
688
- struct DocumentOCRView: View {
689
- @State private var model = DocumentOCRModel()
690
- @State private var isShowingScanner = false
518
+ struct PaperReaderScreen: View {
519
+ @State private var reader = PaperReader()
520
+ @State private var capturing = false
691
521
 
692
522
  var body: some View {
693
- NavigationStack {
694
- List {
695
- if model.isProcessing {
696
- ProgressView("Recognizing text...")
697
- }
698
- ForEach(Array(model.extractedText.enumerated()), id: \.offset) { index, text in
699
- Section("Page \(index + 1)") {
700
- Text(text)
701
- .font(.body)
702
- .textSelection(.enabled)
703
- }
704
- }
523
+ Form {
524
+ Button("Scan pages") { capturing = true }
525
+ if reader.busy {
526
+ ProgressView()
705
527
  }
706
- .navigationTitle("Document OCR")
707
- .toolbar {
708
- Button("Scan") {
709
- isShowingScanner = true
528
+ ForEach(reader.transcripts.indices, id: \.self) { page in
529
+ Section("Page \(page + 1)") {
530
+ Text(reader.transcripts[page]).textSelection(.enabled)
710
531
  }
711
532
  }
712
- .fullScreenCover(isPresented: $isShowingScanner) {
713
- DocumentScannerRepresentable(scannedImages: $model.scannedPages)
714
- }
715
- .onChange(of: model.scannedPages) {
716
- Task { await model.processScannedPages() }
717
- }
533
+ }
534
+ .sheet(isPresented: $capturing) {
535
+ PaperCamera(pages: $reader.pages)
536
+ }
537
+ .onChange(of: reader.pages) {
538
+ Task { await reader.transcribe() }
718
539
  }
719
540
  }
720
541
  }
721
542
  ```
722
543
 
723
- ## Performance Considerations
544
+ User-facing strings here are literals for brevity; ship them as localized keys.
545
+
546
+ ## Performance
724
547
 
725
- ### DataScannerViewController
548
+ Live scanner:
726
549
 
727
- - Use `.fast` quality for barcode-only scanning
728
- - Set `recognizesMultipleItems = false` when only one result is needed
729
- - Disable `isHighFrameRateTrackingEnabled` for barcode scanning to save power
730
- - Limit `recognizedDataTypes` to only what you need
731
- - Stop scanning when processing results to avoid wasted CPU cycles
550
+ - Use `.fast` when only barcodes are scanned.
551
+ - Set `recognizesMultipleItems = false` when one result is enough.
552
+ - Turn off `isHighFrameRateTrackingEnabled` for barcode-only scanners to save
553
+ power.
554
+ - Ask only for the `recognizedDataTypes` the feature needs.
555
+ - Call `stopScanning()` while you process a result, and resume afterwards.
732
556
 
733
- ### VNDocumentCameraViewController
557
+ Document camera:
734
558
 
735
- - Pages are returned as `UIImage` at full resolution -- resize before
736
- processing if memory is a concern
737
- - Process pages sequentially to avoid memory spikes
738
- - Use `autoreleasepool` when processing many pages in a loop
559
+ - Pages arrive as full-resolution `UIImage` values; downscale them if memory
560
+ is tight.
561
+ - Process pages one after another rather than all at once, and wrap
562
+ synchronous per-page loops in `autoreleasepool` when there are many.