@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +285 -285
  5. package/package.json +3 -3
  6. package/pipeline/lib/redact.mjs +3 -2
  7. package/pipeline/scripts/_notices.mjs +1 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +13 -1
  9. package/pipeline/scripts/pre-commit-check.sh +4 -0
  10. package/pipeline/skills/.skill-manifest.json +69 -69
  11. package/pipeline/skills/shared/README.md +70 -70
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  16. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  17. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  18. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  19. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  20. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  21. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  22. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  23. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +345 -277
  24. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  25. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +107 -121
  26. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +145 -165
  27. package/pipeline/skills/shared/external/app-store-review/SKILL.md +306 -326
  28. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  29. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  30. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  31. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +335 -360
  32. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  34. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  35. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  37. package/pipeline/skills/shared/external/authentication/SKILL.md +277 -381
  38. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  39. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +135 -178
  40. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  41. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  42. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  43. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  44. package/pipeline/skills/shared/external/background-processing/SKILL.md +274 -384
  45. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  46. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +173 -321
  47. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  48. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  49. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  50. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  51. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  52. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  53. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  54. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  55. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  56. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +228 -376
  57. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  58. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  59. package/pipeline/skills/shared/external/core-data/SKILL.md +302 -368
  60. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  61. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  62. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  63. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  64. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  65. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  66. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  67. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  68. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  69. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  70. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  71. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  72. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  73. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  74. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  75. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  76. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  77. package/pipeline/skills/shared/external/device-integrity/SKILL.md +236 -353
  78. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  79. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  80. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  81. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  82. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  83. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  84. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  85. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  86. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  87. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  88. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  89. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  90. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  91. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  92. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  93. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  94. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  95. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  96. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  97. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  98. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  99. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  100. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  101. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  102. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  103. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  104. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  105. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  106. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  107. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  108. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  109. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  111. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  112. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  113. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  114. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +3 -3
  115. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +1 -1
  116. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +8 -7
  117. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +2 -2
  118. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +5 -2
  119. package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +100 -0
  120. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +45 -26
  121. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
  122. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +12 -5
  123. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +2 -1
  124. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +6 -5
  125. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +44 -18
  126. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +5 -2
  127. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +10 -11
  128. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +4 -33
  129. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +12 -59
  130. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +297 -267
  131. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  132. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  133. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  134. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  135. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  136. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  137. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  138. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  139. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  140. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  141. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  142. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  143. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  144. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  145. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  146. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  147. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  148. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  149. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  150. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  151. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  152. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  153. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  154. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  155. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  156. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  157. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  158. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  159. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  160. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  161. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  162. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  163. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  164. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  165. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  166. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  167. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  168. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  169. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  170. package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
  171. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  172. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  173. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  174. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  175. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  176. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  177. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +302 -241
  178. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  179. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  180. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  181. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  182. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  183. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  184. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  185. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  186. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  187. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  188. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  189. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  190. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  191. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  192. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +304 -351
  193. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  194. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  195. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  196. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  197. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  198. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  199. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  200. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  201. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  202. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  203. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  204. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  205. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  206. package/pipeline/skills/shared/external/swift-security/SKILL.md +183 -162
  207. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  208. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  209. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +411 -476
  210. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  211. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  212. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  213. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  214. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  215. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  216. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +375 -491
  217. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  218. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  219. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  220. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +397 -457
  221. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  222. package/pipeline/skills/shared/external/swift-testing/SKILL.md +191 -175
  223. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  224. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +81 -84
  225. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  226. package/pipeline/skills/shared/external/swiftdata/SKILL.md +394 -256
  227. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  228. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  229. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  230. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  231. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  232. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  233. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  234. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  235. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  236. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  237. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  238. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  239. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  240. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  241. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  242. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  243. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  244. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  245. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  246. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  247. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  248. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  249. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  250. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  251. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +201 -168
  252. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  253. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +134 -133
  254. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  255. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +111 -138
  256. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  257. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  258. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  259. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  260. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  261. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  262. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  263. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  264. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  265. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  266. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  267. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  268. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  269. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  270. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  271. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  272. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  273. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  274. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  275. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  276. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  277. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  278. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  279. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  280. package/pipeline/skills/shared/external/weatherkit/SKILL.md +160 -315
  281. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  282. package/pipeline/skills/shared/external/widgetkit/SKILL.md +224 -288
  283. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +416 -719
  284. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,444 +1,276 @@
1
- # CallKit + PushKit Extended Patterns
1
+ # CallKit Patterns
2
2
 
3
- Overflow reference for the `callkit` skill. Contains advanced patterns that exceed the main skill file's scope.
3
+ Longer examples that back the `callkit-voip` skill. Read its SKILL.md first;
4
+ this file assumes the provider setup, push rules and audio rules described
5
+ there.
4
6
 
5
- ## Contents
7
+ ## An observable call manager
6
8
 
7
- - [Full Call Manager](#full-call-manager)
8
- - [Hold and Mute Actions](#hold-and-mute-actions)
9
- - [Multiple Concurrent Calls](#multiple-concurrent-calls)
10
- - [Call State Tracking](#call-state-tracking)
11
- - [Encrypted VoIP Push Filtering](#encrypted-voip-push-filtering)
12
- - [Call Directory Incremental Updates](#call-directory-incremental-updates)
13
- - [Testing VoIP Locally](#testing-voip-locally)
14
-
15
- ## Full Call Manager
9
+ A `@MainActor` model that SwiftUI can watch. It owns the provider and the
10
+ controller and keeps a table of live calls.
16
11
 
17
12
  ```swift
18
13
  import CallKit
19
- import AVFoundation
20
- import PushKit
21
-
22
- @Observable
23
- @MainActor
24
- final class VoIPCallManager: NSObject {
25
- let provider: CXProvider
26
- let callController = CXCallController()
27
-
28
- private(set) var activeCalls: [UUID: CallInfo] = [:]
29
-
30
- struct CallInfo {
31
- let uuid: UUID
32
- let handle: String
33
- let isOutgoing: Bool
34
- var isOnHold: Bool = false
35
- var isMuted: Bool = false
36
- var isConnected: Bool = false
37
- }
14
+ import Observation
15
+
16
+ struct LiveCall: Identifiable, Sendable {
17
+ let id: UUID
18
+ let address: String
19
+ let outgoing: Bool
20
+ var held = false
21
+ var muted = false
22
+ var connected = false
23
+ }
24
+
25
+ @Observable @MainActor
26
+ final class Switchboard: NSObject {
27
+ @ObservationIgnored let provider: CXProvider
28
+ @ObservationIgnored let controller = CXCallController()
29
+ private(set) var calls: [UUID: LiveCall] = [:]
38
30
 
39
31
  override init() {
40
- let config = CXProviderConfiguration()
41
- config.localizedName = "My VoIP"
42
- config.supportsVideo = true
43
- config.maximumCallGroups = 2
44
- config.maximumCallsPerCallGroup = 1
45
- config.supportedHandleTypes = [.phoneNumber]
46
- config.includesCallsInRecents = true
47
-
48
- provider = CXProvider(configuration: config)
32
+ let setup = CXProviderConfiguration()
33
+ setup.supportedHandleTypes = [.phoneNumber]
34
+ setup.includesCallsInRecents = true
35
+ setup.maximumCallsPerCallGroup = 1
36
+ setup.maximumCallGroups = 2
37
+ setup.supportsVideo = true
38
+ provider = CXProvider(configuration: setup)
49
39
  super.init()
50
40
  provider.setDelegate(self, queue: nil)
51
41
  }
52
42
 
53
- // MARK: - Incoming
54
-
55
- func reportIncoming(
56
- uuid: UUID,
57
- handle: String,
58
- callerName: String,
59
- hasVideo: Bool
60
- ) async throws {
61
- let update = CXCallUpdate()
62
- update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
63
- update.localizedCallerName = callerName
64
- update.hasVideo = hasVideo
65
- update.supportsHolding = true
66
- update.supportsDTMF = true
67
- update.supportsGrouping = false
68
- update.supportsUngrouping = false
69
-
70
- try await withCheckedThrowingContinuation {
71
- (continuation: CheckedContinuation<Void, Error>) in
72
- provider.reportNewIncomingCall(
73
- with: uuid, update: update
74
- ) { error in
75
- if let error {
76
- continuation.resume(throwing: error)
77
- } else {
78
- continuation.resume()
79
- }
80
- }
81
- }
82
-
83
- activeCalls[uuid] = CallInfo(
84
- uuid: uuid, handle: handle, isOutgoing: false
85
- )
43
+ func reportIncoming(id: UUID, address: String, name: String) async throws {
44
+ let info = CXCallUpdate()
45
+ info.localizedCallerName = name
46
+ info.remoteHandle = CXHandle(type: .generic, value: address)
47
+ info.supportsDTMF = true
48
+ info.supportsHolding = true
49
+ info.supportsUngrouping = false
50
+ info.supportsGrouping = false
51
+ try await provider.reportNewIncomingCall(with: id, update: info)
52
+ calls[id] = LiveCall(id: id, address: address, outgoing: false)
86
53
  }
87
54
 
88
- // MARK: - Outgoing
89
-
90
- func startCall(handle: String, hasVideo: Bool) {
91
- let uuid = UUID()
92
- let cxHandle = CXHandle(type: .phoneNumber, value: handle)
93
- let action = CXStartCallAction(call: uuid, handle: cxHandle)
94
- action.isVideo = hasVideo
95
-
96
- callController.request(
97
- CXTransaction(action: action)
98
- ) { error in
99
- if let error {
100
- print("Start call failed: \(error)")
101
- }
102
- }
103
-
104
- activeCalls[uuid] = CallInfo(
105
- uuid: uuid, handle: handle, isOutgoing: true
106
- )
55
+ func startCall(to address: String) async throws {
56
+ let id = UUID()
57
+ let action = CXStartCallAction(call: id, handle: CXHandle(type: .phoneNumber, value: address))
58
+ try await controller.request(CXTransaction(action: action))
59
+ calls[id] = LiveCall(id: id, address: address, outgoing: true)
107
60
  }
108
61
 
109
- // MARK: - Actions
110
-
111
- func endCall(uuid: UUID) {
112
- let action = CXEndCallAction(call: uuid)
113
- callController.request(CXTransaction(action: action)) { error in
114
- if let error { print("End call failed: \(error)") }
115
- }
62
+ func hangUp(_ id: UUID) async throws {
63
+ try await controller.request(CXTransaction(action: CXEndCallAction(call: id)))
116
64
  }
117
65
 
118
- func setHeld(uuid: UUID, onHold: Bool) {
119
- let action = CXSetHeldCallAction(call: uuid, onHold: onHold)
120
- callController.request(CXTransaction(action: action)) { error in
121
- if let error { print("Hold failed: \(error)") }
122
- }
66
+ func setHeld(_ id: UUID, _ held: Bool) async throws {
67
+ try await controller.request(CXTransaction(action: CXSetHeldCallAction(call: id, onHold: held)))
123
68
  }
124
69
 
125
- func setMuted(uuid: UUID, muted: Bool) {
126
- let action = CXSetMutedCallAction(call: uuid, muted: muted)
127
- callController.request(CXTransaction(action: action)) { error in
128
- if let error { print("Mute failed: \(error)") }
129
- }
70
+ func setMuted(_ id: UUID, _ muted: Bool) async throws {
71
+ try await controller.request(CXTransaction(action: CXSetMutedCallAction(call: id, muted: muted)))
130
72
  }
131
73
  }
132
74
  ```
133
75
 
134
- ## Hold and Mute Actions
76
+ A call enters `calls` only after its incoming report succeeds; an outgoing call
77
+ enters after the start transaction was accepted. Hanging up, holding and muting
78
+ all follow the same shape: build the action, wrap it in a `CXTransaction`, send
79
+ it with `request`.
135
80
 
136
- ```swift
137
- extension VoIPCallManager: CXProviderDelegate {
138
- nonisolated func providerDidReset(_ provider: CXProvider) {
139
- Task { @MainActor in
140
- for uuid in activeCalls.keys {
141
- disconnectCall(uuid)
142
- }
143
- activeCalls.removeAll()
144
- }
145
- }
146
-
147
- nonisolated func provider(
148
- _ provider: CXProvider,
149
- perform action: CXSetHeldCallAction
150
- ) {
151
- Task { @MainActor in
152
- activeCalls[action.callUUID]?.isOnHold = action.isOnHold
153
- if action.isOnHold {
154
- pauseAudio(for: action.callUUID)
155
- } else {
156
- resumeAudio(for: action.callUUID)
157
- }
158
- action.fulfill()
159
- }
160
- }
81
+ ## Hold and mute in the delegate
161
82
 
162
- nonisolated func provider(
163
- _ provider: CXProvider,
164
- perform action: CXSetMutedCallAction
165
- ) {
166
- Task { @MainActor in
167
- activeCalls[action.callUUID]?.isMuted = action.isMuted
168
- // If call translation is active, mute app input without deactivating
169
- // upstream audio that translated audio may need.
170
- setMicrophoneMuted(action.isMuted)
171
- action.fulfill()
172
- }
173
- }
83
+ The provider calls its delegate on the main queue here (`queue: nil`), so the
84
+ conformance is `@preconcurrency`: the methods stay main-actor isolated and
85
+ touch `calls` directly. Marking them `nonisolated` and hopping back with
86
+ `Task { @MainActor in ... }` does not compile under Swift 6, because the task
87
+ would carry the non-`Sendable` `CXAction` off the delegate call ("sending
88
+ 'hold' risks causing data races").
174
89
 
175
- nonisolated func provider(
176
- _ provider: CXProvider,
177
- perform action: CXAnswerCallAction
178
- ) {
179
- Task { @MainActor in
180
- configureAudioSession()
181
- connectToServer(callUUID: action.callUUID) { success in
182
- if success {
183
- activeCalls[action.callUUID]?.isConnected = true
184
- action.fulfill()
185
- } else {
186
- provider.reportCall(
187
- with: action.callUUID,
188
- endedAt: Date(),
189
- reason: .failed
190
- )
191
- action.fail()
192
- }
193
- }
194
- }
195
- }
196
-
197
- nonisolated func provider(
198
- _ provider: CXProvider,
199
- perform action: CXStartCallAction
200
- ) {
201
- Task { @MainActor in
202
- configureAudioSession()
203
- provider.reportOutgoingCall(
204
- with: action.callUUID,
205
- startedConnectingAt: Date()
206
- )
207
- connectToServer(callUUID: action.callUUID)
208
- provider.reportOutgoingCall(
209
- with: action.callUUID,
210
- connectedAt: Date()
211
- )
212
- activeCalls[action.callUUID]?.isConnected = true
213
- action.fulfill()
214
- }
215
- }
216
-
217
- nonisolated func provider(
218
- _ provider: CXProvider,
219
- perform action: CXEndCallAction
220
- ) {
221
- Task { @MainActor in
222
- disconnectCall(action.callUUID)
223
- activeCalls.removeValue(forKey: action.callUUID)
224
- action.fulfill()
90
+ ```swift
91
+ extension Switchboard: @preconcurrency CXProviderDelegate {
92
+ func providerDidReset(_ cx: CXProvider) {
93
+ Task {
94
+ for id in calls.keys { await CallMedia.shared.disconnect(id) }
95
+ calls.removeAll()
225
96
  }
226
97
  }
227
98
 
228
- nonisolated func provider(
229
- _ provider: CXProvider,
230
- didActivate audioSession: AVAudioSession
231
- ) {
232
- Task { @MainActor in
233
- startAudioEngine()
234
- }
99
+ func provider(_ cx: CXProvider, perform hold: CXSetHeldCallAction) {
100
+ let id = hold.callUUID
101
+ calls[id]?.held = hold.isOnHold
102
+ if hold.isOnHold { CallMedia.shared.pause(id) } else { CallMedia.shared.resume(id) }
103
+ hold.fulfill()
235
104
  }
236
105
 
237
- nonisolated func provider(
238
- _ provider: CXProvider,
239
- didDeactivate audioSession: AVAudioSession
240
- ) {
241
- Task { @MainActor in
242
- stopAudioEngine()
243
- }
106
+ func provider(_ cx: CXProvider, perform mute: CXSetMutedCallAction) {
107
+ calls[mute.callUUID]?.muted = mute.isMuted
108
+ CallMedia.shared.setMicrophoneMuted(mute.isMuted)
109
+ mute.fulfill()
244
110
  }
245
111
  }
246
112
  ```
247
113
 
248
- ## Multiple Concurrent Calls
114
+ Muting only silences the microphone input. Upstream audio keeps flowing,
115
+ because iOS 26 call translation depends on it.
249
116
 
250
- When a second call arrives while one is active, CallKit automatically puts
251
- the first call on hold. Handle the hold action to pause your audio stream:
117
+ The answer, start, end, `didActivate` and `didDeactivate` handlers are the same
118
+ as in SKILL.md, with bookkeeping added: set `connected = true` once media
119
+ connects, and remove the entry when the call ends.
252
120
 
253
- ```swift
254
- nonisolated func provider(
255
- _ provider: CXProvider,
256
- perform action: CXSetHeldCallAction
257
- ) {
258
- Task { @MainActor in
259
- if action.isOnHold {
260
- // Pause the RTP stream for this call
261
- pauseMediaStream(for: action.callUUID)
262
- } else {
263
- // Resume the RTP stream
264
- resumeMediaStream(for: action.callUUID)
265
- }
266
- activeCalls[action.callUUID]?.isOnHold = action.isOnHold
267
- action.fulfill()
268
- }
269
- }
270
- ```
121
+ ## Several calls at once
271
122
 
272
- Configure `maximumCallGroups` and `maximumCallsPerCallGroup` in
273
- `CXProviderConfiguration` to control how many concurrent calls your app
274
- supports.
123
+ When a second call rings while one is active and the user accepts it, CallKit
124
+ holds the first call on its own. The app sees a `CXSetHeldCallAction` for the
125
+ first call and must pause or resume its media stream (for example the RTP
126
+ session) in response. How many calls may coexist is set by
127
+ `maximumCallGroups` and `maximumCallsPerCallGroup` in the configuration.
275
128
 
276
- ## Call State Tracking
129
+ ## Watching call state
277
130
 
278
- Use `CXCallObserver` to monitor call state changes from outside the provider
279
- delegate:
131
+ `CXCallObserver` reports every call on the device, which is useful outside the
132
+ provider delegate, for example in a status view.
280
133
 
281
134
  ```swift
282
135
  import CallKit
283
136
 
284
- final class CallStateObserver: NSObject, CXCallObserverDelegate {
285
- let observer = CXCallObserver()
137
+ final class CallWatcher: NSObject, CXCallObserverDelegate {
138
+ private let monitor = CXCallObserver()
286
139
 
287
140
  override init() {
288
141
  super.init()
289
- observer.setDelegate(self, queue: nil)
142
+ monitor.setDelegate(self, queue: .main)
290
143
  }
291
144
 
292
- func callObserver(
293
- _ callObserver: CXCallObserver,
294
- callChanged call: CXCall
295
- ) {
145
+ func callObserver(_ watcher: CXCallObserver, callChanged call: CXCall) {
146
+ let phase: String
296
147
  if call.hasEnded {
297
- print("Call \(call.uuid) ended")
148
+ phase = "ended"
298
149
  } else if call.hasConnected {
299
- print("Call \(call.uuid) connected")
150
+ phase = "connected"
300
151
  } else if call.isOutgoing {
301
- print("Outgoing call \(call.uuid) ringing")
152
+ phase = "dialing"
302
153
  } else {
303
- print("Incoming call \(call.uuid) ringing")
154
+ phase = "ringing"
304
155
  }
156
+ Log.calls.info("\(call.uuid) is \(phase)")
305
157
  }
306
158
  }
307
159
  ```
308
160
 
309
- ## Encrypted VoIP Push Filtering
161
+ ## Filtering encrypted VoIP pushes
310
162
 
311
- Use a notification service extension with
312
- `CXProvider.reportNewIncomingVoIPPushPayload` only when server-side metadata
313
- encryption means the server cannot determine whether the outgoing notification
314
- is a VoIP call request or some other data. If the server knows the content is
315
- a VoIP call, send a normal PushKit VoIP push instead.
163
+ When the server sends end-to-end encrypted content and cannot tell whether it
164
+ is a call, a notification service extension decrypts it and hands call
165
+ payloads to CallKit.
316
166
 
317
167
  ```swift
318
- import UserNotifications
319
168
  import CallKit
169
+ import UserNotifications
320
170
 
321
- final class NotificationService: UNNotificationServiceExtension {
322
- override func didReceive(
323
- _ request: UNNotificationRequest,
324
- withContentHandler contentHandler:
325
- @escaping (UNNotificationContent) -> Void
326
- ) {
327
- guard let encryptedPayload = request.content
328
- .userInfo["encrypted"] as? [AnyHashable: Any] else {
329
- contentHandler(request.content)
171
+ final class PushFilter: UNNotificationServiceExtension {
172
+ override func didReceive(_ incoming: UNNotificationRequest,
173
+ withContentHandler deliver: @escaping (UNNotificationContent) -> Void) {
174
+ guard let sealed = incoming.content.userInfo["sealed"] as? String,
175
+ let opened = Envelope.open(sealed) else {
176
+ deliver(incoming.content)
330
177
  return
331
178
  }
332
-
333
- let decryptedPayload = decryptPayload(encryptedPayload)
334
-
335
- CXProvider.reportNewIncomingVoIPPushPayload(
336
- decryptedPayload
337
- ) { error in
338
- if let error {
339
- // Show a missed-call notification instead
340
- let content = UNMutableNotificationContent()
341
- content.title = "Missed Call"
342
- content.body = decryptedPayload["callerName"] as? String ?? ""
343
- contentHandler(content)
344
- } else {
345
- // Call was reported; suppress the notification
346
- contentHandler(UNNotificationContent())
179
+ nonisolated(unsafe) let deliver = deliver
180
+ CXProvider.reportNewIncomingVoIPPushPayload(opened) { failure in
181
+ guard failure != nil else {
182
+ deliver(UNNotificationContent())
183
+ return
347
184
  }
185
+ let missed = UNMutableNotificationContent()
186
+ missed.title = String(localized: "Missed call")
187
+ deliver(missed)
348
188
  }
349
189
  }
350
190
  }
351
191
  ```
352
192
 
353
- This requires the `com.apple.developer.usernotifications.filtering` entitlement.
193
+ Content that was not encrypted passes through untouched. The content handler
194
+ is not `Sendable`, and both forms of `reportNewIncomingVoIPPushPayload` need it
195
+ somewhere concurrent: the completion form runs its callback as `@Sendable`, and
196
+ the async form needs a `Task` whose closure Swift 6 rejects for capturing the
197
+ handler. The `nonisolated(unsafe)` copy lets the `@Sendable` callback hold it. A failed report turns
198
+ into a missed-call notification; a successful one is suppressed with empty
199
+ content. If the server does know the push is a call, skip all of this and use
200
+ an ordinary PushKit VoIP push. Entitlement required for the extension:
201
+ `com.apple.developer.usernotifications.filtering`.
354
202
 
355
- ## Call Directory Incremental Updates
203
+ ## Incremental Call Directory updates
356
204
 
357
- After the first full load, use incremental updates to add or remove entries
358
- without reloading the entire dataset:
205
+ Once the initial complete load has run, the extension can add and remove individual numbers
206
+ instead of rebuilding everything.
359
207
 
360
208
  ```swift
361
- private func addOrRemoveIncrementalEntries(
362
- to context: CXCallDirectoryExtensionContext
363
- ) {
364
- let removedNumbers: [CXCallDirectoryPhoneNumber] = fetchRemovedNumbers()
365
- for number in removedNumbers {
366
- context.removeBlockingEntry(withPhoneNumber: number)
367
- context.removeIdentificationEntry(withPhoneNumber: number)
368
- }
369
-
370
- let newBlocked: [CXCallDirectoryPhoneNumber] = fetchNewBlockedNumbers()
371
- for number in newBlocked.sorted() {
372
- context.addBlockingEntry(withNextSequentialPhoneNumber: number)
373
- }
374
-
375
- let newIdentified: [(CXCallDirectoryPhoneNumber, String)] = fetchNewIdentified()
376
- for (number, label) in newIdentified.sorted(by: { $0.0 < $1.0 }) {
377
- context.addIdentificationEntry(
378
- withNextSequentialPhoneNumber: number,
379
- label: label
380
- )
209
+ extension SpamListProvider {
210
+ func applyChanges(to context: CXCallDirectoryExtensionContext) {
211
+ let delta = SharedStore.pendingDelta()
212
+ for number in delta.unblocked {
213
+ context.removeBlockingEntry(withPhoneNumber: number)
214
+ }
215
+ for number in delta.unlabeled {
216
+ context.removeIdentificationEntry(withPhoneNumber: number)
217
+ }
218
+ for number in delta.blocked.sorted() {
219
+ context.addBlockingEntry(withNextSequentialPhoneNumber: number)
220
+ }
221
+ for pair in delta.labeled.sorted(by: { $0.key < $1.key }) {
222
+ context.addIdentificationEntry(withNextSequentialPhoneNumber: pair.key, label: pair.value)
223
+ }
224
+ SharedStore.clearDelta()
381
225
  }
382
226
  }
383
227
  ```
384
228
 
385
- Call Directory data is bulk data. The system calls `beginRequest(with:)` when
386
- loading the extension, not for each individual incoming call, so keep web
387
- lookups and dataset sync in the containing app before reloading the extension.
388
-
389
- ## Testing VoIP Locally
229
+ Additions are sorted before they are added. The system runs
230
+ `beginRequest(with:)` when it loads the extension, never per incoming call, so
231
+ any web lookup and dataset sync happens in the containing app before it calls
232
+ `reloadExtension(withIdentifier:)`.
390
233
 
391
- ### Simulating VoIP Pushes
234
+ ## Testing VoIP locally
392
235
 
393
- Use the Push Notifications Console or a command-line tool to send test pushes.
394
- The payload must target the VoIP topic (`<bundle-id>.voip`):
236
+ - Fire test pushes with the Push Notifications Console, or with any APNs
237
+ client you run from a terminal. The topic is the bundle identifier with `.voip` appended, for example
238
+ `com.example.dialer.voip`.
239
+ - A test payload needs only an empty `aps` plus your own keys:
395
240
 
396
241
  ```json
397
242
  {
398
- "aps": {},
399
- "handle": "+15551234567",
400
- "callerName": "Test Caller",
401
- "hasVideo": false
243
+ "aps": {},
244
+ "from": "+14155550123",
245
+ "display": "Test Caller",
246
+ "video": false
402
247
  }
403
248
  ```
404
249
 
405
- ### Testing Without a Server
406
-
407
- For development, you can bypass PushKit and directly call the incoming call
408
- reporting method:
250
+ - Without a server, a debug-only helper can call the incoming report directly:
409
251
 
410
252
  ```swift
411
253
  #if DEBUG
412
- func simulateIncomingCall() {
413
- let uuid = UUID()
414
- Task {
415
- try? await CallManager.shared.reportIncomingCall(
416
- uuid: uuid,
417
- handle: "+15551234567",
418
- hasVideo: false
419
- )
254
+ extension Switchboard {
255
+ func simulateIncomingCall() async {
256
+ try? await reportIncoming(id: UUID(), address: "+14155550123", name: "Test Caller")
420
257
  }
421
258
  }
422
259
  #endif
423
260
  ```
424
261
 
425
- ### Checking Extension Status
426
-
427
- Verify that the Call Directory extension is enabled:
262
+ - Check the Call Directory extension state from the app:
428
263
 
429
264
  ```swift
430
- CXCallDirectoryManager.sharedInstance.getEnabledStatusForExtension(
431
- withIdentifier: "com.example.app.CallDirectory"
432
- ) { status, error in
433
- switch status {
434
- case .enabled:
435
- print("Extension is enabled")
436
- case .disabled:
437
- print("Extension is disabled -- prompt user to enable in Settings")
438
- case .unknown:
439
- print("Status unknown")
440
- @unknown default:
441
- break
265
+ func checkBlockerStatus() {
266
+ let manager = CXCallDirectoryManager.sharedInstance
267
+ manager.getEnabledStatusForExtension(withIdentifier: "org.sample.dialer.blocker") { state, failure in
268
+ switch state {
269
+ case .enabled: Log.calls.info("blocker active")
270
+ case .disabled: Log.calls.info("ask the user to enable it in Settings")
271
+ case .unknown: Log.calls.info("status unknown: \(String(describing: failure))")
272
+ @unknown default: break
273
+ }
442
274
  }
443
275
  }
444
276
  ```