@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,76 +1,62 @@
1
1
  ---
2
2
  name: callkit-voip
3
- description: "Implement VoIP calling with CallKit and PushKit. Use when building incoming/outgoing call flows, registering for VoIP push notifications, configuring CXProvider and CXCallController, handling call actions, coordinating audio sessions, or creating Call Directory extensions for caller ID and call blocking."
3
+ description: "CallKit and PushKit VoIP calling: CXProvider and CXCallController, incoming and outgoing call flows, call actions, VoIP push registration and reporting, call audio sessions, Call Directory caller ID and blocking. Use when implementing VoIP calls or VoIP push handling."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
- # CallKit
8
+ # CallKit and PushKit VoIP
9
9
 
10
- Build VoIP calling features that integrate with the native iOS call UI using
11
- CallKit and PushKit. Covers incoming/outgoing call flows, VoIP push
12
- registration, audio session coordination, and call directory extensions.
13
- Targets Swift 6.3 / iOS 26+.
10
+ CallKit puts an app's VoIP calls into the same system call UI that cellular
11
+ calls use: the lock-screen answer slider, the in-call screen, Recents and
12
+ Bluetooth controls. PushKit wakes the app when a call arrives. Everything here
13
+ targets Swift 6.3 and iOS 26 or later unless a line names another release.
14
14
 
15
- ## Contents
16
-
17
- - [Setup](#setup)
18
- - [Provider Configuration](#provider-configuration)
19
- - [Incoming Call Flow](#incoming-call-flow)
20
- - [Outgoing Call Flow](#outgoing-call-flow)
21
- - [PushKit VoIP Registration](#pushkit-voip-registration)
22
- - [Audio Session Coordination](#audio-session-coordination)
23
- - [Call Directory Extension and Manager](#call-directory-extension-and-manager)
24
- - [Common Mistakes](#common-mistakes)
25
- - [Review Checklist](#review-checklist)
26
- - [References](#references)
15
+ Rotating APNs auth keys and everyday remote-notification registration live
16
+ in `push-notifications`. This skill starts at the VoIP push type.
27
17
 
28
18
  ## Setup
29
19
 
30
- ### Project Configuration
20
+ ### Project configuration
31
21
 
32
- 1. Enable the **Voice over IP** background mode in Signing & Capabilities
33
- 2. Add the **Push Notifications** capability
34
- 3. For call directory extensions, add a **Call Directory Extension** target
22
+ 1. Signing & Capabilities: turn on Background Modes and tick **Voice over IP**.
23
+ 2. Push Notifications must also appear in the capability list.
24
+ 3. Caller ID or blocking lists need a separate target of type Call Directory Extension.
35
25
 
36
- ### Key Types
26
+ ### The types you touch
37
27
 
38
- | Type | Role |
39
- |---|---|
40
- | `CXProvider` | Reports calls to the system, receives call actions |
41
- | `CXCallController` | Requests call actions (start, end, hold, mute) |
42
- | `CXCallUpdate` | Describes call metadata (caller name, video, handle) |
43
- | `CXProviderDelegate` | Handles system call actions and audio session events |
44
- | `PKPushRegistry` | Registers for and receives VoIP push notifications |
45
- | `PKVoIPPushMetadata` | iOS 26.4+ metadata that says whether a VoIP push must be reported |
28
+ | Type | Job |
29
+ | --- | --- |
30
+ | `CXProvider` | Tells the system about calls and receives the actions the system wants performed |
31
+ | `CXCallController` | Asks the system to perform an action: start, end, hold, mute |
32
+ | `CXCallUpdate` | Describes a call: caller name, handle, video |
33
+ | `CXProviderDelegate` | Performs call actions and receives audio session activation |
34
+ | `PKPushRegistry` | Registers for VoIP pushes and delivers them |
35
+ | `PKVoIPPushMetadata` | iOS 26.4+: says whether a given VoIP push has to be reported |
46
36
 
47
- ## Provider Configuration
37
+ ## Provider configuration
48
38
 
49
- Create a single `CXProvider` at app launch and keep it alive for the app
50
- lifetime. Configure it with a `CXProviderConfiguration` that describes your
51
- calling capabilities.
39
+ Build one `CXProvider` when the app launches and keep that instance until the
40
+ process dies. Creating a second provider or recreating it per call confuses the
41
+ system about which calls exist.
52
42
 
53
43
  ```swift
54
44
  import CallKit
55
45
 
56
- /// CXProvider dispatches all delegate calls to the queue passed to `setDelegate(_:queue:)`.
57
- /// The `let` properties are initialized once and never mutated, making this type
58
- /// safe to share across concurrency domains despite @unchecked Sendable.
59
- final class CallManager: NSObject, @unchecked Sendable {
60
- static let shared = CallManager()
46
+ @MainActor
47
+ final class PhoneLine: NSObject {
48
+ static let shared = PhoneLine()
61
49
 
62
50
  let provider: CXProvider
63
- let callController = CXCallController()
51
+ let controller = CXCallController()
64
52
 
65
- private override init() {
53
+ override init() {
66
54
  let config = CXProviderConfiguration()
67
- config.localizedName = "My VoIP App"
68
55
  config.supportsVideo = true
69
- config.maximumCallsPerCallGroup = 1
70
56
  config.maximumCallGroups = 2
57
+ config.maximumCallsPerCallGroup = 1
71
58
  config.supportedHandleTypes = [.phoneNumber, .emailAddress]
72
59
  config.includesCallsInRecents = true
73
-
74
60
  provider = CXProvider(configuration: config)
75
61
  super.init()
76
62
  provider.setDelegate(self, queue: nil)
@@ -78,204 +64,188 @@ final class CallManager: NSObject, @unchecked Sendable {
78
64
  }
79
65
  ```
80
66
 
81
- ## Incoming Call Flow
67
+ `PhoneLine` is `@MainActor` because every delegate callback arrives on the
68
+ queue passed to `setDelegate(_:queue:)`, and `nil` means the main queue. The
69
+ delegate conformance below is `@preconcurrency`, so those callbacks run as
70
+ main-actor code and the `Task` each one starts inherits that isolation. That is
71
+ what lets a `Task` hold on to the non-`Sendable` `CXAction` and `CXProvider`
72
+ it was handed; from a nonisolated method Swift 6 rejects the same capture with
73
+ "passing closure as a 'sending' parameter risks causing data races". The name the
74
+ system shows comes from `localizedName`; on current SDKs it is read from the
75
+ bundle display name, and the old `init(localizedName:)` is deprecated.
82
76
 
83
- When a required VoIP call push arrives, report the incoming call to CallKit
84
- immediately. The system displays the native call UI. You must report required
85
- calls before the PushKit completion handler returns -- failure to do so causes
86
- the system to terminate your app.
77
+ ## Incoming call flow
78
+
79
+ When a VoIP push says a call is ringing, report it to CallKit right away; the
80
+ system then draws the native incoming-call UI. For a push that must be
81
+ reported, the report has to happen before the PushKit completion handler runs,
82
+ or the system kills the app.
87
83
 
88
84
  ```swift
89
- func reportIncomingCall(
90
- uuid: UUID,
91
- handle: String,
92
- hasVideo: Bool
93
- ) async throws {
94
- let update = CXCallUpdate()
95
- update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
96
- update.hasVideo = hasVideo
97
- update.localizedCallerName = "Jane Doe"
98
-
99
- try await withCheckedThrowingContinuation {
100
- (continuation: CheckedContinuation<Void, Error>) in
101
- provider.reportNewIncomingCall(
102
- with: uuid,
103
- update: update
104
- ) { error in
105
- if let error {
106
- continuation.resume(throwing: error)
107
- } else {
108
- continuation.resume()
85
+ extension PhoneLine {
86
+ func announceIncoming(id: UUID, number: String, name: String, video: Bool) async throws {
87
+ let update = CXCallUpdate()
88
+ update.remoteHandle = CXHandle(type: .phoneNumber, value: number.trimmingCharacters(in: .whitespaces))
89
+ update.localizedCallerName = name
90
+ update.hasVideo = video
91
+
92
+ try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
93
+ provider.reportNewIncomingCall(with: id, update: update) { error in
94
+ if let error { cont.resume(throwing: error) } else { cont.resume() }
109
95
  }
110
96
  }
111
97
  }
112
98
  }
113
99
  ```
114
100
 
115
- ### Handling the Answer Action
101
+ The report throws when Do Not Disturb or the user's block list filters the call.
102
+ That is a normal outcome, not a crash path.
116
103
 
117
- Implement `CXProviderDelegate` to respond when the user answers:
104
+ ### Answering and ending
118
105
 
119
106
  ```swift
120
- extension CallManager: CXProviderDelegate {
121
- func providerDidReset(_ provider: CXProvider) {
122
- // End all calls, reset audio
107
+ extension PhoneLine: @preconcurrency CXProviderDelegate {
108
+ func providerDidReset(_ cx: CXProvider) {
109
+ MediaSession.shared.hangUpEverything()
110
+ AudioRoute.tearDown()
123
111
  }
124
112
 
125
- func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
126
- // Prepare audio, then fulfill only after the call is actually ready
127
- configureAudioSession()
128
- connectToCallServer(callUUID: action.callUUID) { success in
129
- if success {
130
- action.fulfill()
131
- } else {
132
- provider.reportCall(
133
- with: action.callUUID,
134
- endedAt: Date(),
135
- reason: .failed
136
- )
137
- action.fail()
113
+ func provider(_ cx: CXProvider, perform answer: CXAnswerCallAction) {
114
+ AudioRoute.prepareForCall()
115
+ Task {
116
+ do {
117
+ try await SignalingClient.shared.join(call: answer.callUUID)
118
+ answer.fulfill()
119
+ } catch {
120
+ cx.reportCall(with: answer.callUUID, endedAt: .now, reason: .failed)
121
+ answer.fail()
138
122
  }
139
123
  }
140
124
  }
141
125
 
142
- func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
143
- disconnectFromCallServer(callUUID: action.callUUID)
144
- action.fulfill()
126
+ func provider(_ cx: CXProvider, perform hangup: CXEndCallAction) {
127
+ Task {
128
+ await SignalingClient.shared.leave(call: hangup.callUUID)
129
+ hangup.fulfill()
130
+ }
145
131
  }
146
132
  }
147
133
  ```
148
134
 
149
- ## Outgoing Call Flow
135
+ `providerDidReset(_:)` means the system dropped every call: end all of them and
136
+ reset audio. The answer action stays pending until the media server connection
137
+ is up; only then is it fulfilled. If the connection fails, report the call as
138
+ ended with `.failed` and fail the action.
139
+
140
+ ## Outgoing call flow
150
141
 
151
- Use `CXCallController` to request an outgoing call. The system routes the
152
- request through your `CXProviderDelegate`.
142
+ Outgoing calls start with a request to `CXCallController`. The system checks it
143
+ and hands it back through `provider(_:perform:)` with a `CXStartCallAction`.
153
144
 
154
145
  ```swift
155
- func startOutgoingCall(handle: String, hasVideo: Bool) {
156
- let uuid = UUID()
157
- let handle = CXHandle(type: .phoneNumber, value: handle)
158
- let startAction = CXStartCallAction(call: uuid, handle: handle)
159
- startAction.isVideo = hasVideo
160
-
161
- let transaction = CXTransaction(action: startAction)
162
- callController.request(transaction) { error in
163
- if let error {
164
- print("Failed to start call: \(error)")
146
+ extension PhoneLine {
147
+ func dial(_ address: String, video: Bool) -> UUID {
148
+ let id = UUID()
149
+ let start = CXStartCallAction(call: id, handle: CXHandle(type: .phoneNumber, value: address))
150
+ start.isVideo = video
151
+ controller.request(CXTransaction(action: start)) { error in
152
+ if let error { Log.calls.error("dial refused: \(error)") }
165
153
  }
154
+ return id
166
155
  }
167
- }
168
- ```
169
156
 
170
- ### Delegate Methods for Outgoing Calls
171
-
172
- ```swift
173
- extension CallManager {
174
- func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
175
- configureAudioSession()
176
- // Begin connecting to server
177
- provider.reportOutgoingCall(
178
- with: action.callUUID,
179
- startedConnectingAt: Date()
180
- )
181
-
182
- connectToServer(callUUID: action.callUUID) {
183
- provider.reportOutgoingCall(
184
- with: action.callUUID,
185
- connectedAt: Date()
186
- )
157
+ func provider(_ cx: CXProvider, perform dial: CXStartCallAction) {
158
+ AudioRoute.prepareForCall()
159
+ let id = dial.callUUID
160
+ cx.reportOutgoingCall(with: id, startedConnectingAt: .now)
161
+ Task {
162
+ do {
163
+ try await SignalingClient.shared.place(call: id, to: dial.handle.value)
164
+ cx.reportOutgoingCall(with: id, connectedAt: .now)
165
+ dial.fulfill()
166
+ } catch {
167
+ dial.fail()
168
+ }
187
169
  }
188
- action.fulfill()
189
170
  }
190
171
  }
191
172
  ```
192
173
 
193
- ## PushKit VoIP Registration
174
+ Report both `startedConnectingAt` and `connectedAt`; Recents and the call timer
175
+ use them.
176
+
177
+ ## PushKit VoIP registration
178
+
179
+ Create the registry on every launch, not lazily, and forward each token change
180
+ to your server.
181
+
182
+ Report rules:
194
183
 
195
- Register for VoIP pushes at every app launch and send token changes to your
196
- server. For iOS 13 SDK+ apps, every report-required VoIP call push must be
197
- reported before PushKit completion using CallKit, or LiveCommunicationKit for
198
- apps built on that framework. On iOS 26.4+, `PKVoIPPushMetadata.mustReport` is
199
- the gate: `true` means report before completion; `false` means no CallKit or
200
- LiveCommunicationKit report is required. Missing a required report before
201
- completion can terminate the app, and repeated failures may stop VoIP delivery.
184
+ - Linked against SDK 13 or newer: every VoIP push that is a call must be
185
+ reported to CallKit (LiveCommunicationKit apps report there instead)
186
+ before the PushKit completion handler is called.
187
+ - iOS 26.4 and later: `PKVoIPPushMetadata.mustReport` decides. When it is `true`,
188
+ the report precedes completion; when `false`, that push can be handled
189
+ without any report at all.
190
+ - A skipped required report may end the process; keep skipping and the
191
+ system may stop delivering VoIP pushes altogether.
202
192
 
203
- | Path | Report decision | Completion timing |
204
- |---|---|---|
205
- | iOS 26.4+ `mustReport == true` | Report with CallKit or LiveCommunicationKit | After report callback |
206
- | iOS 26.4+ `mustReport == false` | No CallKit/LiveCommunicationKit report required | After local handling |
207
- | Older delegate | iOS 13 SDK+ treats VoIP call pushes as report-required | After report callback |
193
+ | Delivery path | Report? | When to call completion |
194
+ | --- | --- | --- |
195
+ | 26.4+ delegate, `mustReport == true` | Yes | After the report callback |
196
+ | 26.4+ delegate, `mustReport == false` | No | After local handling |
197
+ | Older delegate | Treat every VoIP call push as required | After the report callback |
208
198
 
209
199
  ```swift
210
200
  import PushKit
211
201
 
212
- final class PushManager: NSObject, PKPushRegistryDelegate {
213
- let registry: PKPushRegistry
202
+ @MainActor
203
+ final class VoIPInbox: NSObject, @preconcurrency PKPushRegistryDelegate {
204
+ private let registry = PKPushRegistry(queue: .main)
214
205
 
215
- override init() {
216
- registry = PKPushRegistry(queue: .main)
217
- super.init()
206
+ func start() {
218
207
  registry.delegate = self
219
208
  registry.desiredPushTypes = [.voIP]
220
209
  }
221
210
 
222
- func pushRegistry(
223
- _ registry: PKPushRegistry,
224
- didUpdate pushCredentials: PKPushCredentials,
225
- for type: PKPushType
226
- ) {
227
- let token = pushCredentials.token
228
- .map { String(format: "%02x", $0) }
229
- .joined()
230
- // Send token to your server
231
- sendTokenToServer(token)
211
+ func pushRegistry(_ reg: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
212
+ let hex = credentials.token.reduce(into: "") { out, byte in out += String(format: "%02x", byte) }
213
+ Backend.shared.upload(voipToken: hex)
232
214
  }
233
215
 
234
216
  @available(iOS 26.4, *)
235
- func pushRegistry(
236
- _ registry: PKPushRegistry,
237
- didReceiveIncomingVoIPPushWith payload: PKPushPayload,
238
- metadata: PKVoIPPushMetadata,
239
- withCompletionHandler completion: @escaping @Sendable () -> Void
240
- ) {
217
+ func pushRegistry(_ reg: PKPushRegistry,
218
+ didReceiveIncomingVoIPPushWith payload: PKPushPayload,
219
+ metadata: PKVoIPPushMetadata,
220
+ withCompletionHandler completion: @escaping @Sendable () -> Void) {
241
221
  guard metadata.mustReport else {
242
222
  completion()
243
223
  return
244
224
  }
245
- handleIncomingVoIPPush(payload, completion: completion)
225
+ ring(from: payload, then: completion)
246
226
  }
247
227
 
248
- // Keep the older callback for iOS 26.0-26.3 and older deployment targets.
249
- func pushRegistry(
250
- _ registry: PKPushRegistry,
251
- didReceiveIncomingPushWith payload: PKPushPayload,
252
- for type: PKPushType,
253
- completion: @escaping () -> Void
254
- ) {
228
+ func pushRegistry(_ reg: PKPushRegistry,
229
+ didReceiveIncomingPushWith payload: PKPushPayload,
230
+ for type: PKPushType,
231
+ completion: @escaping () -> Void) {
255
232
  guard type == .voIP else {
256
233
  completion()
257
234
  return
258
235
  }
259
-
260
- handleIncomingVoIPPush(payload, completion: completion)
236
+ ring(from: payload, then: completion)
261
237
  }
262
238
 
263
- private func handleIncomingVoIPPush(
264
- _ payload: PKPushPayload,
265
- completion: @escaping () -> Void
266
- ) {
267
- let callUUID = UUID()
268
- let handle = payload.dictionaryPayload["handle"] as? String ?? "Unknown"
269
-
239
+ private func ring(from payload: PKPushPayload, then completion: @escaping () -> Void) {
240
+ let body = payload.dictionaryPayload
241
+ let number = body["from"] as? String ?? "Unknown"
242
+ let name = body["display"] as? String ?? number
243
+ let video = body["video"] as? Bool ?? false
270
244
  Task {
271
245
  do {
272
- try await CallManager.shared.reportIncomingCall(
273
- uuid: callUUID,
274
- handle: handle,
275
- hasVideo: false
276
- )
246
+ try await PhoneLine.shared.announceIncoming(id: UUID(), number: number, name: name, video: video)
277
247
  } catch {
278
- // Call was filtered by DND or block list
248
+ Log.calls.notice("call filtered or rejected: \(error)")
279
249
  }
280
250
  completion()
281
251
  }
@@ -283,218 +253,167 @@ final class PushManager: NSObject, PKPushRegistryDelegate {
283
253
  }
284
254
  ```
285
255
 
286
- Server-side VoIP pushes should use a short lifetime: set `apns-expiration` to
287
- `0` or only a few seconds. After the initial push wakes the app, send hangups
288
- and call-detail changes over the app-server connection instead of sending more
289
- VoIP pushes.
256
+ Keep the older delegate method for iOS 26.0 through 26.3 and for lower
257
+ deployment targets. The registry delivers on `.main`, which is why the inbox is
258
+ `@MainActor`: the `Task` in `ring` can then carry the non-`Sendable` completion
259
+ handler without leaving the actor. Completion runs after the report attempt on both the
260
+ success and the error path.
290
261
 
291
- ## Audio Session Coordination
262
+ Server side: send VoIP pushes with `apns-expiration: 0` or a few seconds; a
263
+ ring that arrives a minute late is worse than none. After the first push has
264
+ woken the app, hangups and changed call details travel over your own socket or
265
+ API channel instead of further VoIP pushes.
292
266
 
293
- CallKit manages audio session activation/deactivation. Configure your audio
294
- session when CallKit tells you to, not before.
295
- Review answers should name both sides: start media only in
296
- `provider(_:didActivate:)`, and stop/tear down media in
297
- `provider(_:didDeactivate:)` or reset paths.
267
+ ## Audio session coordination
298
268
 
299
- ```swift
300
- extension CallManager {
301
- func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
302
- // Audio session is now active -- start audio engine / WebRTC
303
- startAudioEngine()
304
- }
269
+ CallKit owns activation and deactivation of the audio session. Set the category
270
+ while preparing the call, and wait for CallKit before touching media.
305
271
 
306
- func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
307
- // Audio session deactivated -- stop audio engine
308
- stopAudioEngine()
309
- }
272
+ ```swift
273
+ import AVFAudio
310
274
 
311
- func configureAudioSession() {
312
- let session = AVAudioSession.sharedInstance()
275
+ enum AudioRoute {
276
+ static func prepareForCall() {
313
277
  do {
314
- try session.setCategory(
315
- .playAndRecord,
316
- mode: .voiceChat,
317
- options: [.allowBluetooth, .allowBluetoothA2DP]
318
- )
278
+ try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat,
279
+ options: [.allowBluetoothHFP, .allowBluetoothA2DP])
319
280
  } catch {
320
- print("Audio session configuration failed: \(error)")
281
+ Log.calls.error("audio category failed: \(error)")
321
282
  }
322
283
  }
323
- }
324
- ```
325
-
326
- ## Call Directory Extension and Manager
327
284
 
328
- Use Call Directory for preloaded caller ID/blocking, not per-call API lookup.
329
- The extension loads sorted bulk data in `beginRequest(with:)`; the main app uses
330
- `CXCallDirectoryManager` to check enabled status, open Call Blocking &
331
- Identification settings when disabled, and reload after data changes. Store
332
- `CXCallDirectoryPhoneNumber` as country code plus digits in ascending order
333
- (for example `18005551234`), not a formatted string.
334
-
335
- ```swift
336
- import CallKit
285
+ static func tearDown() { MediaSession.shared.stopEngine() }
286
+ }
337
287
 
338
- final class CallDirectoryHandler: CXCallDirectoryProvider {
339
- override func beginRequest(
340
- with context: CXCallDirectoryExtensionContext
341
- ) {
342
- if context.isIncremental {
343
- addOrRemoveIncrementalEntries(to: context)
344
- } else {
345
- addAllEntries(to: context)
346
- }
347
- context.completeRequest()
288
+ extension PhoneLine {
289
+ func provider(_ cx: CXProvider, didActivate session: AVAudioSession) {
290
+ MediaSession.shared.startEngine()
348
291
  }
349
292
 
350
- private func addAllEntries(
351
- to context: CXCallDirectoryExtensionContext
352
- ) {
353
- // Country code + digits, sorted in ascending order
354
- let blockedNumbers: [CXCallDirectoryPhoneNumber] = [
355
- 18005551234, 18005555678
356
- ]
357
- for number in blockedNumbers {
358
- context.addBlockingEntry(
359
- withNextSequentialPhoneNumber: number
360
- )
361
- }
362
-
363
- let identifiedNumbers: [(CXCallDirectoryPhoneNumber, String)] = [
364
- (18005551111, "Local Pizza"),
365
- (18005552222, "Dentist Office")
366
- ]
367
- for (number, label) in identifiedNumbers {
368
- context.addIdentificationEntry(
369
- withNextSequentialPhoneNumber: number,
370
- label: label
371
- )
372
- }
293
+ func provider(_ cx: CXProvider, didDeactivate session: AVAudioSession) {
294
+ MediaSession.shared.stopEngine()
373
295
  }
374
296
  }
375
297
  ```
376
298
 
377
- ### Main-App Manager: Status, Settings, Reload
299
+ Media starts in `provider(_:didActivate:)` and stops in
300
+ `provider(_:didDeactivate:)` or a reset path. A review should point at both
301
+ callbacks by name.
302
+
303
+ ## Call Directory extension and manager
304
+
305
+ A Call Directory supplies caller ID labels and blocked numbers that are loaded
306
+ in advance. It cannot look anything up while a call is ringing.
378
307
 
379
308
  ```swift
380
- let manager = CXCallDirectoryManager.sharedInstance
381
- manager.getEnabledStatusForExtension(withIdentifier: extensionID) { status, _ in
382
- guard status == .enabled else {
383
- manager.openSettings { _ in } // Call Blocking & Identification
384
- return
309
+ import CallKit
310
+
311
+ final class SpamListProvider: CXCallDirectoryProvider {
312
+ override func beginRequest(with ctx: CXCallDirectoryExtensionContext) {
313
+ if ctx.isIncremental {
314
+ applyChanges(to: ctx)
315
+ } else {
316
+ for number in SharedStore.blocked().sorted() {
317
+ ctx.addBlockingEntry(withNextSequentialPhoneNumber: number)
318
+ }
319
+ for entry in SharedStore.labels().sorted(by: { $0.key < $1.key }) {
320
+ ctx.addIdentificationEntry(withNextSequentialPhoneNumber: entry.key, label: entry.value)
321
+ }
322
+ }
323
+ ctx.completeRequest()
385
324
  }
386
- manager.reloadExtension(withIdentifier: extensionID) { _ in }
387
325
  }
388
326
  ```
389
327
 
390
- Check `getEnabledStatusForExtension(...)` before assuming the extension is
391
- active, use `openSettings(...)` for Call Blocking & Identification when
392
- disabled, and call `reloadExtension(...)` after data changes. Route APNs
393
- auth-key rotation and normal remote-notification setup to push-notifications.
394
-
395
- ## Common Mistakes
396
-
397
- ### DON'T: Fail to report a required call on VoIP push receipt
398
-
399
- Follow the PushKit report rules above: iOS 13 SDK+ apps must report
400
- report-required VoIP call pushes before completion, and on iOS 26.4+
401
- `PKVoIPPushMetadata.mustReport` identifies which pushes are required. Missing a
402
- required report can terminate the app; repeated failures may stop VoIP delivery.
403
-
404
- Do not treat a required VoIP push as a data-only notification. Report the call
405
- to CallKit and call the PushKit completion handler from the report completion.
406
-
407
- ### DON'T: Fulfill answer before the call is connected
408
-
409
- When the user answers before your app has established the server/media
410
- connection, leave the `CXAnswerCallAction` pending while connecting. Fulfill it
411
- after the call is ready; if connection fails, fail the action and report the
412
- call ended with `.failed`.
413
-
414
- ### DON'T: Start audio before CallKit activates the session
415
-
416
- Starting your audio engine before `provider(_:didActivate:)` causes silence
417
- or immediate deactivation. CallKit manages session priority with the system.
418
-
419
- Prepare audio in the answer/start action if needed, then start media only from
420
- `provider(_:didActivate:)`.
421
-
422
- For iOS 26 call translation, set `CXProviderConfiguration.supportsAudioTranslation`
423
- when your service supports it and handle `CXSetTranslatingCallAction`. If a
424
- person mutes during a translated call, mute app input with
425
- `CXSetMutedCallAction`; do not deactivate upstream audio that translated audio
426
- depends on.
427
-
428
- For encrypted VoIP metadata, use `CXProvider.reportNewIncomingVoIPPushPayload`
429
- only from a notification service extension when the server cannot determine
430
- whether encrypted content is a VoIP call or other data. That path requires the
431
- `com.apple.developer.usernotifications.filtering` entitlement; otherwise send a
432
- normal PushKit VoIP push.
433
-
434
- ### DON'T: Forget to call action.fulfill() or action.fail()
435
-
436
- Failing to fulfill or fail an action leaves the call in a limbo state and
437
- triggers the timeout handler.
438
-
439
- Every provider action path must eventually call `fulfill()` or `fail()`,
440
- including network-error and cancellation paths.
441
-
442
- ### DON'T: Ignore push token refresh
443
-
444
- The VoIP push token can change at any time. If your server has a stale token,
445
- pushes silently fail and incoming calls never arrive.
446
-
447
- Send the token to your server every time `didUpdate pushCredentials` fires,
448
- not just during first-run onboarding.
449
-
450
- ### DON'T: Use Call Directory for per-call lookup
451
-
452
- Call Directory extensions provide preloaded caller ID and blocking data. They
453
- cannot ask a web service for the incoming caller during call presentation.
454
- Fetch or generate the dataset ahead of time, reload the extension, and add
455
- entries in sorted sequential order.
456
-
457
- ## Review Checklist
458
-
459
- - [ ] VoIP background mode enabled in capabilities
460
- - [ ] Single `CXProvider` instance created at app launch and retained
461
- - [ ] `CXProviderDelegate` set before reporting any calls
462
- - [ ] iOS 26.4+ PushKit path reports when `mustReport` is true and may skip when false
463
- - [ ] iOS 13 SDK+ PushKit VoIP call pushes report to CallKit before completion
464
- - [ ] VoIP APNs requests use `apns-expiration` of `0` or only a few seconds
465
- - [ ] Hangups and detail updates use the app-server connection after the initial push
466
- - [ ] `action.fulfill()` or `action.fail()` called for every provider delegate action
467
- - [ ] `CXAnswerCallAction` fulfilled only after the call server/media connection is ready
468
- - [ ] Audio engine started only after `provider(_:didActivate:)` callback
469
- - [ ] Audio engine stopped in `provider(_:didDeactivate:)` callback
470
- - [ ] Audio session category set to `.playAndRecord` with `.voiceChat` mode
471
- - [ ] VoIP push token sent to server on every `didUpdate pushCredentials` callback
472
- - [ ] `PKPushRegistry` created at every app launch (not lazily)
473
- - [ ] Call Directory data is preloaded, not fetched per incoming call
474
- - [ ] `CXCallDirectoryPhoneNumber` documented as country calling code + digits
475
- - [ ] `CXCallDirectoryManager` names status check, reload, and settings-opening APIs
476
- - [ ] `CXCallUpdate` populated with `localizedCallerName` and `remoteHandle`
477
- - [ ] Outgoing calls report `startedConnectingAt` and `connectedAt` timestamps
478
- - [ ] iOS 26 call translation keeps upstream audio active during mute
479
- - [ ] Encrypted metadata filtering mentions the notification service extension entitlement
328
+ A `CXCallDirectoryPhoneNumber` is an `Int64`: country calling code followed by
329
+ the national digits, for example `14155550123`, never a formatted string.
330
+ Entries go in ascending order.
331
+
332
+ In the containing app, use `CXCallDirectoryManager.sharedInstance`:
333
+
334
+ - `getEnabledStatusForExtension(withIdentifier:)` before assuming the extension
335
+ is on.
336
+ - When the status is not `.enabled`, `openSettings { _ in }` takes the user to
337
+ Call Blocking & Identification.
338
+ - After the dataset changes, `reloadExtension(withIdentifier:)`.
339
+
340
+ Rotating APNs keys and the usual notification registration are covered by
341
+ `push-notifications`.
342
+
343
+ ## Common mistakes
344
+
345
+ - **Treating a call push as data.** A required VoIP call push that is not
346
+ reported ends the process. Hand it to CallKit first and let the report's
347
+ callback be where PushKit's completion runs.
348
+ - **Fulfilling the answer too early.** Keep `CXAnswerCallAction` pending while
349
+ connecting; fulfill once media is ready; on failure, fail it and report the
350
+ call ended with `.failed`.
351
+ - **Starting audio before activation.** Media started before
352
+ `provider(_:didActivate:)` plays silence or gets deactivated at once, because
353
+ CallKit arbitrates session priority. Prepare in the answer or start action,
354
+ play only after activation.
355
+ - **Missing iOS 26 call translation.** Set
356
+ `CXProviderConfiguration.supportsAudioTranslation` when the app supports it
357
+ and handle `CXSetTranslatingCallAction`.
358
+ - **Muting by killing audio.** During a translated call, mute the app's own
359
+ input in `CXSetMutedCallAction`; do not deactivate the upstream audio that
360
+ translation reads.
361
+ - **Overusing encrypted payload reporting.** `CXProvider.reportNewIncomingVoIPPushPayload`
362
+ belongs in a notification service extension, and only when the server cannot
363
+ tell whether encrypted content is a call. If it can tell, send a normal
364
+ PushKit VoIP push. The extension needs the
365
+ entitlement `com.apple.developer.usernotifications.filtering`.
366
+ - **Leaving an action unresolved.** A call action that is neither fulfilled nor
367
+ failed hangs until its timeout fires. Every branch, including network errors
368
+ and cancellation, ends with `fulfill()` or `fail()`.
369
+ - **Caching the push token.** It can change at any moment; a stale server copy
370
+ makes pushes vanish silently. Upload on every `didUpdate` credentials call.
371
+ - **Per-call lookup in Call Directory.** The extension cannot call a web service
372
+ when a call is presented. Fetch ahead, reload, and add sorted sequential
373
+ entries.
374
+
375
+ ## Review checklist
376
+
377
+ - [ ] Voice over IP background mode is on
378
+ - [ ] Exactly one `CXProvider`, created at launch and retained
379
+ - [ ] Provider delegate is set before any call is reported
380
+ - [ ] On 26.4+, a `mustReport` of true triggers a report; false allows skipping it
381
+ - [ ] With SDK 13 or newer, each VoIP call push is reported ahead of completion
382
+ - [ ] `apns-expiration` is 0 or a few seconds
383
+ - [ ] After the waking push, hangups and call details use the app's own server channel
384
+ - [ ] Every delegate action ends in `fulfill()` or `fail()`
385
+ - [ ] Answer is fulfilled only when the server and media connection are ready
386
+ - [ ] Audio engine starts after `didActivate` and stops in `didDeactivate`
387
+ - [ ] Audio category is `.playAndRecord` with mode `.voiceChat`
388
+ - [ ] Token uploaded on every `didUpdate` credentials callback
389
+ - [ ] `PKPushRegistry` created on every launch, not lazily
390
+ - [ ] Call Directory data is preloaded, never fetched per call
391
+ - [ ] `CXCallDirectoryPhoneNumber` described as country calling code plus digits
392
+ - [ ] `CXCallDirectoryManager` status check, reload and settings APIs are named
393
+ - [ ] `CXCallUpdate` sets `localizedCallerName` and `remoteHandle`
394
+ - [ ] Dialed calls send both `startedConnectingAt` and `connectedAt` timestamps
395
+ - [ ] Translated calls keep upstream audio live while muted
396
+ - [ ] Encrypted payload filtering lists the filtering entitlement the extension needs
480
397
 
481
398
  ## References
482
399
 
483
- - Extended patterns (hold, mute, group calls, delegate lifecycle): [references/callkit-patterns.md](references/callkit-patterns.md)
484
- - [CallKit framework](https://sosumi.ai/documentation/callkit)
485
- - [CXProvider](https://sosumi.ai/documentation/callkit/cxprovider)
486
- - [CXCallController](https://sosumi.ai/documentation/callkit/cxcallcontroller)
487
- - [CXCallAction](https://sosumi.ai/documentation/callkit/cxcallaction)
488
- - [CXCallUpdate](https://sosumi.ai/documentation/callkit/cxcallupdate)
489
- - [CXProviderConfiguration](https://sosumi.ai/documentation/callkit/cxproviderconfiguration)
490
- - [CXProviderDelegate](https://sosumi.ai/documentation/callkit/cxproviderdelegate)
491
- - [PKPushRegistry](https://sosumi.ai/documentation/pushkit/pkpushregistry)
492
- - [PKPushRegistryDelegate](https://sosumi.ai/documentation/pushkit/pkpushregistrydelegate)
493
- - [PKVoIPPushMetadata](https://sosumi.ai/documentation/pushkit/pkvoippushmetadata)
494
- - [CXCallDirectoryProvider](https://sosumi.ai/documentation/callkit/cxcalldirectoryprovider)
495
- - [CXCallDirectoryPhoneNumber](https://sosumi.ai/documentation/callkit/cxcalldirectoryphonenumber)
496
- - [CXCallDirectoryManager](https://sosumi.ai/documentation/callkit/cxcalldirectorymanager)
497
- - [CXSetTranslatingCallAction](https://sosumi.ai/documentation/callkit/cxsettranslatingcallaction)
498
- - [reportNewIncomingVoIPPushPayload(_:completion:)](https://sosumi.ai/documentation/callkit/cxprovider/reportnewincomingvoippushpayload(_:completion:))
499
- - [Making and receiving VoIP calls](https://sosumi.ai/documentation/callkit/making-and-receiving-voip-calls)
500
- - [Responding to VoIP Notifications from PushKit](https://sosumi.ai/documentation/pushkit/responding-to-voip-notifications-from-pushkit)
400
+ - [Longer patterns](references/callkit-patterns.md): observable manager, hold
401
+ and mute, concurrent calls, call observer, encrypted filtering, incremental
402
+ Call Directory, local testing
403
+ - [CallKit](https://developer.apple.com/documentation/callkit)
404
+ - [CXProvider](https://developer.apple.com/documentation/callkit/cxprovider)
405
+ - [CXCallController](https://developer.apple.com/documentation/callkit/cxcallcontroller)
406
+ - [CXCallAction](https://developer.apple.com/documentation/callkit/cxcallaction)
407
+ - [CXCallUpdate](https://developer.apple.com/documentation/callkit/cxcallupdate)
408
+ - [CXProviderConfiguration](https://developer.apple.com/documentation/callkit/cxproviderconfiguration)
409
+ - [CXProviderDelegate](https://developer.apple.com/documentation/callkit/cxproviderdelegate)
410
+ - [CXSetTranslatingCallAction](https://developer.apple.com/documentation/callkit/cxsettranslatingcallaction)
411
+ - [reportNewIncomingVoIPPushPayload(_:completion:)](https://developer.apple.com/documentation/callkit/cxprovider/reportnewincomingvoippushpayload(_:completion:))
412
+ - [CXCallDirectoryProvider](https://developer.apple.com/documentation/callkit/cxcalldirectoryprovider)
413
+ - [CXCallDirectoryPhoneNumber](https://developer.apple.com/documentation/callkit/cxcalldirectoryphonenumber)
414
+ - [CXCallDirectoryManager](https://developer.apple.com/documentation/callkit/cxcalldirectorymanager)
415
+ - [PKPushRegistry](https://developer.apple.com/documentation/pushkit/pkpushregistry)
416
+ - [PKPushRegistryDelegate](https://developer.apple.com/documentation/pushkit/pkpushregistrydelegate)
417
+ - [PKVoIPPushMetadata](https://developer.apple.com/documentation/pushkit/pkvoippushmetadata)
418
+ - Article: [VoIP call handling with CallKit](https://developer.apple.com/documentation/callkit/making-and-receiving-voip-calls)
419
+ - Article: [PushKit VoIP notification handling](https://developer.apple.com/documentation/pushkit/responding-to-voip-notifications-from-pushkit)