@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,216 +1,222 @@
1
1
  ---
2
2
  name: live-activities
3
- description: "Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time updating widgets for the Lock Screen and Dynamic Island - delivery tracking, sports scores, ride-sharing status, workout timers, media playback, or any time-sensitive information that updates in real time. Also use when working with ActivityKit, ActivityAttributes, Activity lifecycle (request/update/end), Dynamic Island layouts (compact/minimal/expanded), push-to-update Live Activities, or Lock Screen live widgets."
3
+ description: "ActivityKit Live Activities on the Lock Screen and Dynamic Island: ActivityAttributes and ContentState, request/update/end lifecycle, compact, minimal and expanded layouts, stale dates, push-to-update, push-to-start and channel pushes, update tokens, scheduled starts, ActivityStyle. Use when building, reviewing or improving glanceable live status (delivery tracking, scores, rides, workout timers, media playback) or sending Live Activity APNs payloads. Not for Home Screen or timeline widgets (widgetkit) or generic APNs setup (push-notifications)."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
- # ActivityKit
9
-
10
- ActivityKit owns real-time, glanceable Live Activities displayed on the Lock
11
- Screen and, on supported devices, Dynamic Island. StandBy, CarPlay, and a
12
- paired Mac can also display Live Activities, but do not blur that core routing:
13
- ordinary Home Screen/timeline widgets belong in `widgetkit`, and generic APNs
14
- setup belongs in `push-notifications`. Live Activity push payload shape stays in
15
- ActivityKit: device-token updates use `apns-push-type: liveactivity` and
16
- `apns-topic: <bundle-id>.push-type.liveactivity`, while `aps.content-state` must
17
- decode into the app's actual `ActivityAttributes.ContentState` `Codable` shape.
18
- Do not assume `Date` or `ClosedRange<Date>` use Unix timestamp
19
- `lowerBound`/`upperBound` dictionaries unless the Swift model and server
20
- contract coordinate that encoding. Boundary answers that keep ActivityKit APNs
21
- payloads, `content-state`, or Live Activity data contracts in scope should
22
- include these payload-shape invariants even when routing generic APNs setup
23
- elsewhere. Patterns target iOS 26+ with Swift 6.3;
24
- modern `ActivityContent` lifecycle examples require iOS 16.2+ unless noted.
25
-
26
- See [references/activitykit-patterns.md](references/activitykit-patterns.md) for complete code patterns including push payload formats, concurrent activities, state observation, and testing.
27
-
28
- ## Contents
29
-
30
- - [Workflow](#workflow)
31
- - [ActivityAttributes Definition](#activityattributes-definition)
32
- - [Activity Lifecycle](#activity-lifecycle)
33
- - [Lock Screen Presentation](#lock-screen-presentation)
34
- - [Dynamic Island](#dynamic-island)
35
- - [Push-to-Update](#push-to-update)
36
- - [Recent Additions](#recent-additions)
37
- - [Common Mistakes](#common-mistakes)
38
- - [Review Checklist](#review-checklist)
39
- - [References](#references)
8
+ # Live Activities (ActivityKit)
9
+
10
+ A Live Activity is a small, time-boxed status card that ActivityKit keeps on the
11
+ Lock Screen and, on hardware that has one, in the Dynamic Island. A paired
12
+ Mac, CarPlay and StandBy may mirror the same card; none of that changes where
13
+ the work lives. The code and the push contract belong here.
14
+
15
+ Boundaries:
16
+
17
+ - Home Screen widgets, `TimelineProvider` and timeline reloads are `widgetkit`
18
+ work. A Live Activity is driven by the ActivityKit lifecycle, not a timeline.
19
+ - APNs auth keys, certificates and app-level remote-notification registration
20
+ are `push-notifications` work.
21
+ - The Live Activity payload itself stays here, even in an answer that routes
22
+ the rest of APNs away. Always state the two invariants:
23
+ - token pushes carry `apns-push-type: liveactivity` and
24
+ `apns-topic: BUNDLE_ID.push-type.liveactivity`;
25
+ - the JSON in `aps.content-state` is decoded with the app's own
26
+ `ActivityAttributes.ContentState` `Codable` conformance, so it has to
27
+ match that shape exactly. `Date` and
28
+ `ClosedRange<Date>` have no universal wire format: do not assume a Unix
29
+ timestamp or a `lowerBound`/`upperBound` object unless the Swift model and
30
+ the server were written to agree on it.
31
+
32
+ Examples target iOS 26 and Swift 6.3. Anything built on `ActivityContent`
33
+ needs iOS 16.2 or later unless a section says otherwise. Longer patterns (a
34
+ full attributes model, server payloads, concurrent activities, state
35
+ observation, errors, testing) are in the
36
+ [patterns reference](references/activitykit-patterns.md).
40
37
 
41
38
  ## Workflow
42
39
 
43
- ### 1. Create a new Live Activity
40
+ ### Adding a Live Activity
44
41
 
45
- 1. Add `NSSupportsLiveActivities = YES` to the host app's Info.plist.
46
- 2. Define an `ActivityAttributes` struct with a nested `ContentState`.
47
- 3. Create an `ActivityConfiguration` in the widget bundle with Lock Screen
48
- content and Dynamic Island closures.
49
- 4. Start the activity with `Activity.request(attributes:content:pushType:)`.
50
- 5. Update with `activity.update(_:)` and end with `activity.end(_:dismissalPolicy:)`.
51
- 6. Forward push tokens to your server for remote updates.
42
+ 1. In the main app target's Info.plist, turn on `NSSupportsLiveActivities`
43
+ (`YES`).
44
+ 2. Declare an `ActivityAttributes` type with a nested `ContentState`.
45
+ 3. In the widget extension's bundle, add an `ActivityConfiguration` that
46
+ supplies the Lock Screen view and the Dynamic Island closures.
47
+ 4. Start it: `Activity.request(attributes:content:pushType:)`.
48
+ 5. Change it with `activity.update(_:)` and finish it with
49
+ `activity.end(_:dismissalPolicy:)`.
50
+ 6. If a server will drive it, send the activity's push token to that server.
52
51
 
53
- ### 2. Review existing Live Activity code
52
+ ### Reviewing one
54
53
 
55
- Run through the Review Checklist at the end of this document.
54
+ Go through the [Review checklist](#review-checklist) item by item.
56
55
 
57
- ## ActivityAttributes Definition
56
+ ## Modelling the data
58
57
 
59
- Define both static data (immutable for the activity lifetime) and dynamic
60
- `ContentState` (changes with each update). Keep `ContentState` small because
61
- the entire struct is serialized on every update and push payload.
58
+ Two layers, with different lifetimes:
59
+
60
+ - Properties on the attributes struct are fixed for the whole life of the
61
+ activity.
62
+ - `ContentState` carries whatever changes. The entire value is encoded on
63
+ every update and in every push, so keep it lean.
62
64
 
63
65
  ```swift
64
66
  import ActivityKit
65
67
 
66
- struct DeliveryAttributes: ActivityAttributes {
67
- // Static -- set once at activity creation, never changes
68
- var orderNumber: Int
69
- var restaurantName: String
70
-
71
- // Dynamic -- updated throughout the activity lifetime
68
+ struct ChargingAttributes: ActivityAttributes {
72
69
  struct ContentState: Codable, Hashable {
73
- var driverName: String
74
- var estimatedDeliveryTime: ClosedRange<Date>
75
- var currentStep: DeliveryStep
70
+ var batteryPercent: Int
71
+ var finishWindow: ClosedRange<Date>
72
+ var phase: ChargePhase
76
73
  }
74
+
75
+ var stationName: String
76
+ var connectorLabel: String
77
77
  }
78
78
 
79
- enum DeliveryStep: String, Codable, Hashable, CaseIterable {
80
- case confirmed, preparing, pickedUp, delivering, delivered
79
+ enum ChargePhase: String, Codable, Hashable, CaseIterable {
80
+ case queued, connecting, charging, balancing, complete
81
81
 
82
- var icon: String {
82
+ var symbolName: String {
83
83
  switch self {
84
- case .confirmed: "checkmark.circle"
85
- case .preparing: "frying.pan"
86
- case .pickedUp: "bag.fill"
87
- case .delivering: "box.truck.fill"
88
- case .delivered: "house.fill"
84
+ case .queued: "hourglass"
85
+ case .connecting: "powerplug"
86
+ case .charging: "bolt.car"
87
+ case .balancing: "slider.horizontal.3"
88
+ case .complete: "checkmark.circle.fill"
89
89
  }
90
90
  }
91
91
  }
92
92
  ```
93
93
 
94
- ### Stale Date
94
+ ### Stale date
95
95
 
96
- Set `staleDate` on `ActivityContent` to tell the system when content becomes outdated. The system sets `context.isStale` to `true` after this date; show fallback UI (e.g., "Updating...") in your views.
96
+ `ActivityContent` takes a `staleDate`. Once that moment passes without a newer
97
+ update, the system flips `context.isStale` to `true`, and the view should swap
98
+ in a fallback such as a "Refreshing" label instead of showing a number that may
99
+ be wrong.
97
100
 
98
101
  ```swift
99
- let content = ActivityContent(
102
+ let snapshot = ActivityContent(
100
103
  state: state,
101
- staleDate: Date().addingTimeInterval(300), // stale after 5 minutes
104
+ staleDate: .now.addingTimeInterval(5 * 60),
102
105
  relevanceScore: 75
103
106
  )
104
107
  ```
105
108
 
106
- ## Activity Lifecycle
109
+ ## Lifecycle
107
110
 
108
- ### Starting
111
+ ### Start
109
112
 
110
- Use `Activity.request` to create and display a Live Activity. Pass `.token` as
111
- the `pushType` to enable remote updates via APNs. The `ActivityContent` request
112
- shown here requires iOS 16.2+.
113
+ `Activity.request` creates the activity and puts it on screen. Pass
114
+ `pushType: .token` when a server should be able to update it. The
115
+ `ActivityContent` form of `request` is iOS 16.2+.
113
116
 
114
117
  ```swift
115
- let attributes = DeliveryAttributes(orderNumber: 42, restaurantName: "Pizza Place")
116
- let state = DeliveryAttributes.ContentState(
117
- driverName: "Alex",
118
- estimatedDeliveryTime: Date()...Date().addingTimeInterval(1800),
119
- currentStep: .preparing
120
- )
121
- let content = ActivityContent(state: state, staleDate: nil, relevanceScore: 75)
122
-
123
- do {
124
- let activity = try Activity.request(
125
- attributes: attributes,
126
- content: content,
127
- pushType: .token
118
+ func beginCharging(at station: String) {
119
+ let attributes = ChargingAttributes(stationName: station, connectorLabel: "B2")
120
+ let window = Date.now...Date.now.addingTimeInterval(30 * 60)
121
+ let initial = ChargingAttributes.ContentState(
122
+ batteryPercent: 18, finishWindow: window, phase: .connecting
128
123
  )
129
- print("Started activity: \(activity.id)")
130
- } catch {
131
- print("Failed to start activity: \(error)")
124
+ do {
125
+ let activity = try Activity.request(
126
+ attributes: attributes,
127
+ content: ActivityContent(state: initial, staleDate: nil, relevanceScore: 75),
128
+ pushType: .token
129
+ )
130
+ sessionStore.remember(activity.id)
131
+ } catch {
132
+ logger.error("Live Activity refused: \(error.localizedDescription)")
133
+ }
132
134
  }
133
135
  ```
134
136
 
135
- ### Updating
137
+ ### Update
136
138
 
137
- Update the dynamic content state from the app. Use `AlertConfiguration` to
138
- trigger a visible banner and sound alongside the update.
139
+ `await activity.update(content)` changes the card silently. To also light the
140
+ screen with a banner and a sound, use `update(_:alertConfiguration:)`:
139
141
 
140
142
  ```swift
141
- let updatedState = DeliveryAttributes.ContentState(
142
- driverName: "Alex",
143
- estimatedDeliveryTime: Date()...Date().addingTimeInterval(600),
144
- currentStep: .delivering
145
- )
146
- let updatedContent = ActivityContent(
147
- state: updatedState,
148
- staleDate: Date().addingTimeInterval(300),
143
+ let next = ActivityContent(
144
+ state: newState,
145
+ staleDate: .now.addingTimeInterval(300),
149
146
  relevanceScore: 90
150
147
  )
151
-
152
- // Silent update
153
- await activity.update(updatedContent)
154
-
155
- // Update with an alert
156
- await activity.update(updatedContent, alertConfiguration: AlertConfiguration(
157
- title: "Order Update",
158
- body: "Your driver is nearby!",
159
- sound: .default
160
- ))
148
+ await activity.update(
149
+ next,
150
+ alertConfiguration: AlertConfiguration(
151
+ title: "Almost full",
152
+ body: "Battery is at 95 percent.",
153
+ sound: .default
154
+ )
155
+ )
161
156
  ```
162
157
 
163
- ### Ending
158
+ ### End
159
+
160
+ End the activity when the thing it tracks is over. The dismissal policy decides
161
+ how long the ended card lingers on the Lock Screen:
164
162
 
165
- End the activity when the tracked event completes. Choose a dismissal policy
166
- to control how long the ended activity lingers on the Lock Screen.
163
+ | Policy | Removal |
164
+ |---|---|
165
+ | `.default` | The system chooses, at most 4 hours later |
166
+ | `.immediate` | Right away |
167
+ | `.after(date)` | At `date`, no later than 4 hours from now |
167
168
 
168
169
  ```swift
169
- let finalState = DeliveryAttributes.ContentState(
170
- driverName: "Alex",
171
- estimatedDeliveryTime: Date()...Date(),
172
- currentStep: .delivered
170
+ let closing = ChargingAttributes.ContentState(
171
+ batteryPercent: 100, finishWindow: Date.now...Date.now, phase: .complete
173
172
  )
174
- let finalContent = ActivityContent(state: finalState, staleDate: nil, relevanceScore: 0)
175
-
176
- // System decides when to remove (up to 4 hours)
177
- await activity.end(finalContent, dismissalPolicy: .default)
173
+ await activity.end(
174
+ ActivityContent(state: closing, staleDate: nil, relevanceScore: 0),
175
+ dismissalPolicy: .after(.now.addingTimeInterval(3600))
176
+ )
177
+ ```
178
178
 
179
- // Remove immediately
180
- await activity.end(finalContent, dismissalPolicy: .immediate)
179
+ End on every terminal path, not just the happy one: success, cancellation by
180
+ the user or the app, sign-out or session teardown, an app error you cannot
181
+ recover from, and a terminal failure reported by the server. If the backend
182
+ reports that the tracked event is over or that its status is no longer
183
+ reliable, push or apply a
184
+ final terminal state and end; never leave frozen progress on the Lock Screen.
181
185
 
182
- // Remove after a specific time (max 4 hours from now)
183
- await activity.end(finalContent, dismissalPolicy: .after(Date().addingTimeInterval(3600)))
184
- ```
186
+ Time limits, which are easy to conflate:
185
187
 
186
- Always end activities on all terminal code paths -- success, user/app
187
- cancellation, sign-out/session stop, unrecoverable app error, and terminal server
188
- failure. If the server says the tracked event can no longer continue or be
189
- represented accurately, apply or send a final terminal state and end the activity
190
- instead of leaving stale progress visible. When reviewing duration claims,
191
- distinguish the active lifetime (up to 8 hours unless the app or user ends it
192
- sooner), system-ended Lock Screen presence (up to 4 additional hours, for 12
193
- hours total from start), and app-ended `.default` dismissal linger (up to 4 hours
194
- after ending).
188
+ - The active phase lasts at most 8 hours; the app or the user may end it
189
+ earlier.
190
+ - When the system ends it at that limit, the card may remain on the Lock Screen
191
+ for up to 4 more hours, 12 hours in total from the start.
192
+ - When the app ends it with `.default`, the card can stay visible for as much
193
+ as 4 hours past the end call.
195
194
 
196
- ## Lock Screen Presentation
195
+ ## Lock Screen
197
196
 
198
- The Lock Screen is the primary Live Activity display surface. Every device with
199
- iOS 16.1+ displays Live Activities here. Design this layout first, then adapt
200
- for Dynamic Island where available.
197
+ The Lock Screen is the surface that always exists: every device on iOS 16.1 or
198
+ later shows Live Activities there. Lay it out first, then squeeze it into the
199
+ Dynamic Island.
201
200
 
202
201
  ```swift
203
- struct DeliveryActivityWidget: Widget {
204
- var body: some WidgetConfiguration {
205
- ActivityConfiguration(for: DeliveryAttributes.self) { context in
206
- VStack(alignment: .leading) {
207
- Text(context.attributes.restaurantName).font(.headline)
202
+ import SwiftUI
203
+ import WidgetKit
208
204
 
205
+ struct ChargingLiveActivity: Widget {
206
+ var body: some WidgetConfiguration {
207
+ ActivityConfiguration(for: ChargingAttributes.self) { context in
208
+ HStack(spacing: 12) {
209
+ Image(systemName: context.state.phase.symbolName)
210
+ VStack(alignment: .leading) {
211
+ Text(context.attributes.stationName).font(.headline)
212
+ Text("\(context.state.batteryPercent)%")
213
+ }
214
+ Spacer()
209
215
  if context.isStale {
210
- Label("Updating...", systemImage: "arrow.trianglehead.2.clockwise")
216
+ Label("Refreshing", systemImage: "arrow.trianglehead.2.clockwise")
211
217
  .foregroundStyle(.secondary)
212
218
  } else {
213
- Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
219
+ Text(timerInterval: context.state.finishWindow, countsDown: true)
214
220
  .monospacedDigit()
215
221
  }
216
222
  }
@@ -218,274 +224,226 @@ struct DeliveryActivityWidget: Widget {
218
224
  } dynamicIsland: { context in
219
225
  DynamicIsland {
220
226
  DynamicIslandExpandedRegion(.center) {
221
- Text(context.attributes.restaurantName).font(.headline)
227
+ Text(context.attributes.stationName)
222
228
  }
223
229
  DynamicIslandExpandedRegion(.trailing) {
224
- Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
230
+ Text("\(context.state.batteryPercent)%")
225
231
  }
226
232
  } compactLeading: {
227
- Image(systemName: "box.truck.fill")
233
+ Image(systemName: "bolt.car")
228
234
  } compactTrailing: {
229
- Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
235
+ Text(timerInterval: context.state.finishWindow, countsDown: true)
236
+ .monospacedDigit()
230
237
  } minimal: {
231
- Image(systemName: "box.truck.fill")
238
+ Image(systemName: "bolt.car")
232
239
  }
233
240
  }
234
241
  }
235
242
  }
236
243
  ```
237
244
 
238
- ### Supplemental Activity Families
245
+ Static values come from `context.attributes`; changing values from
246
+ `context.state`.
239
247
 
240
- The Lock Screen presentation has limited vertical space. Avoid layouts taller
241
- than roughly 160 points. On iOS 18+, use `supplementalActivityFamilies` when
242
- you provide adaptive layouts beyond the default: `.medium` for iOS/macOS
243
- Live Activity sizing and `.small` for watchOS Live Activity sizing.
248
+ ### Height and supplemental families
244
249
 
245
- ```swift
246
- ActivityConfiguration(for: DeliveryAttributes.self) { context in
247
- // Lock Screen content
248
- } dynamicIsland: { context in
249
- // Dynamic Island
250
- }
251
- .supplementalActivityFamilies([.medium, .small])
252
- ```
250
+ The Lock Screen gives a Live Activity little vertical room; keep the layout
251
+ under roughly 160 points. From iOS 18,
252
+ `.supplementalActivityFamilies([.small, .medium])` on the
253
+ `ActivityConfiguration` opts into adaptive sizes: `.medium` is the iPhone and
254
+ Mac size, `.small` the Apple Watch size.
253
255
 
254
256
  ## Dynamic Island
255
257
 
256
- Dynamic Island presentations appear only on devices that include Dynamic Island.
257
- Design all three modes, but treat the Lock Screen as the primary surface since
258
- not all devices have a Dynamic Island.
259
-
260
- ### Compact (Leading + Trailing)
261
-
262
- Used when one Live Activity occupies Dynamic Island compact space. Space is
263
- extremely limited -- show only the most critical information.
264
-
265
- | Region | Purpose |
266
- |---|---|
267
- | `compactLeading` | Icon or tiny label identifying the activity |
268
- | `compactTrailing` | One key value (timer, score, status) |
269
-
270
- ### Minimal
271
-
272
- Shown when multiple Live Activities compete for space. Only one activity gets
273
- the minimal slot. Display a single icon or glyph.
274
-
275
- ### Expanded Regions
276
-
277
- Shown when the user long-presses the Dynamic Island.
258
+ Only devices with a Dynamic Island show these presentations. Implement all
259
+ three modes anyway; the Lock Screen remains the main design.
278
260
 
279
- | Region | Position |
280
- |---|---|
281
- | `.leading` | Left of the TrueDepth camera; wraps below |
282
- | `.trailing` | Right of the TrueDepth camera; wraps below |
283
- | `.center` | Directly below the camera |
284
- | `.bottom` | Below all other regions |
285
-
286
- ### Keyline Tint
287
-
288
- Apply a subtle tint to the Dynamic Island border:
289
-
290
- ```swift
291
- DynamicIsland { /* expanded */ }
292
- compactLeading: { /* ... */ }
293
- compactTrailing: { /* ... */ }
294
- minimal: { /* ... */ }
295
- .keylineTint(.blue)
296
- ```
261
+ - **Compact**: one Live Activity owns the island. Show the single most
262
+ important fact.
263
+ - `compactLeading`: an icon or a very short label saying what this is.
264
+ - `compactTrailing`: one value, such as a timer, a score or a status.
265
+ - **Minimal**: several Live Activities compete; one of them gets the minimal
266
+ slot. Show a single glyph.
267
+ - **Expanded**: appears on a long press of the island. Regions:
268
+ - `.leading`: the camera's left side; content can wrap below it.
269
+ - `.trailing`: the camera's right side; content can wrap below it.
270
+ - `.center`: directly below the camera.
271
+ - `.bottom`: below everything else.
297
272
 
298
- ## Push-to-Update
273
+ `.keylineTint(_:)` on the `DynamicIsland` tints the island's outline subtly.
299
274
 
300
- Push-to-update sends Live Activity updates through APNs, which is more
301
- efficient than polling from the app and works when the app is suspended, subject
302
- to APNs delivery, priority, budget, and throttling.
275
+ ## Server-driven updates
303
276
 
304
- ### Setup
277
+ APNs pushes are cheaper than having the app poll, and they reach an activity
278
+ while the app is suspended. They are still subject to APNs delivery, priority,
279
+ the system budget and throttling.
305
280
 
306
- Pass `.token` as the `pushType` when starting the activity, then forward the
307
- per-activity update token to your server. Update tokens can rotate, so observe
308
- `activity.pushTokenUpdates` and re-register every emitted token:
281
+ Setup: request with `pushType: .token`, then give the server the update token
282
+ that belongs to this one activity. Tokens rotate, so consume `activity.pushTokenUpdates` (an async
283
+ sequence of `Data`) for the whole life of the activity and re-register every
284
+ value it emits.
309
285
 
310
286
  ```swift
311
- let activity = try Activity.request(
312
- attributes: attributes,
313
- content: content,
314
- pushType: .token
315
- )
316
-
317
- // Observe token changes -- tokens can rotate
318
287
  Task {
319
- for await token in activity.pushTokenUpdates {
320
- let tokenString = token.map { String(format: "%02x", $0) }.joined()
321
- try await ServerAPI.shared.registerActivityToken(
322
- tokenString, activityID: activity.id
323
- )
288
+ for await tokenData in activity.pushTokenUpdates {
289
+ let hex = tokenData.reduce(into: "") { $0 += String(format: "%02x", $1) }
290
+ try? await backend.registerActivityToken(hex, for: activity.id)
324
291
  }
325
292
  }
326
293
  ```
327
294
 
328
- ### APNs Payload Format
295
+ ### Payload and headers
329
296
 
330
- Send an HTTP/2 POST to APNs with these headers and JSON body:
297
+ Send an HTTP/2 `POST` to APNs carrying these headers:
331
298
 
332
- **Required device-token HTTP headers:**
333
- - `apns-push-type: liveactivity`
334
- - `apns-topic: <bundle-id>.push-type.liveactivity`
335
- - `apns-priority: 5` (lower priority) or `10` (immediate, counts against budget)
299
+ - `apns-push-type: liveactivity` (required)
300
+ - `apns-topic: BUNDLE_ID.push-type.liveactivity` (required)
301
+ - `apns-priority: 5` for low priority, or `10` for immediate delivery that
302
+ counts against the budget
336
303
 
337
- The `aps.alert` payload controls visible alert/banner/sound behavior; priority
338
- alone does not create an alert.
304
+ Priority never produces an alert. A visible banner or sound comes only from an
305
+ `aps.alert` object.
339
306
 
340
- **Payload body:** Put `timestamp`, `event`, and the full `content-state` inside `aps`. Use `event: "update"` for updates, `event: "end"` plus optional `dismissal-date` for ending, and `event: "start"` with `attributes-type`, `attributes`, `content-state`, and required `alert` for push-to-start. Add `stale-date`, `relevance-score`, or `alert` when appropriate.
307
+ Everything sits inside `aps`: `timestamp`, `event`, and the full
308
+ `content-state`.
341
309
 
342
- The `content-state` JSON must decode into `ActivityAttributes.ContentState`. Use the default synthesized `Codable` key and value shape unless the Swift model declares custom `CodingKeys`; then coordinate those exact keys and value shapes server-side. Do not assume `Date` or `ClosedRange<Date>` values are Unix timestamp dictionaries unless your Swift model explicitly encodes them that way. Mismatched keys or types can prevent ActivityKit from applying the update.
310
+ - `"event": "update"` changes the activity.
311
+ - `"event": "end"` ends it; `dismissal-date` is optional.
312
+ - `"event": "start"` is push-to-start; it adds `attributes-type` and
313
+ `attributes` alongside `content-state`, and `alert` is required.
314
+ - Optional on any event: `stale-date`, `relevance-score`, `alert`.
343
315
 
344
- ### Push-to-Start
316
+ `content-state` must decode into `ContentState`. With synthesized `Codable`,
317
+ the keys are the property names and the values use the default encodings. If
318
+ the type has custom `CodingKeys` or custom coding, agree on the exact keys and
319
+ value shapes with the server team. A misspelled key, or a number sent as text,
320
+ can be enough for ActivityKit to drop the update.
345
321
 
346
- Start a Live Activity remotely without the app running (iOS 17.2+). Push-to-start tokens are ActivityKit-specific tokens from `Activity<Attributes>.pushToStartTokenUpdates`; they are distinct from ordinary app/device APNs tokens and per-activity update tokens:
322
+ ### Push-to-start (iOS 17.2+)
347
323
 
348
- ```swift
349
- Task {
350
- for await token in Activity<DeliveryAttributes>.pushToStartTokenUpdates {
351
- let tokenString = token.map { String(format: "%02x", $0) }.joined()
352
- try await ServerAPI.shared.registerPushToStartToken(tokenString)
353
- }
354
- }
355
- ```
324
+ A server can start an activity while the app is not running. The token comes
325
+ from the static sequence `Activity<ChargingAttributes>.pushToStartTokenUpdates`.
326
+ It is neither the app's ordinary device token nor any activity's update token;
327
+ store and send it separately.
356
328
 
357
- ### Frequent Push Updates
329
+ ### Frequent updates
358
330
 
359
- Add `NSSupportsLiveActivitiesFrequentUpdates = YES` to Info.plist to increase
360
- the system-managed push update budget. When cadence matters, check
361
- `ActivityAuthorizationInfo.frequentPushesEnabled` and observe
362
- `frequentPushEnablementUpdates`; Apple does not guarantee a fixed update rate.
331
+ `NSSupportsLiveActivitiesFrequentUpdates = YES` in Info.plist raises the
332
+ system-managed push budget. The user can switch it off, so when cadence
333
+ matters read `ActivityAuthorizationInfo().frequentPushesEnabled` and observe
334
+ `frequentPushEnablementUpdates`. There is no guaranteed update rate; do not
335
+ promise one.
363
336
 
364
- ## Recent Additions
337
+ ## Newer capabilities
365
338
 
366
- ### Scheduled Live Activities (iOS 26+)
339
+ ### ActivityStyle (iOS 18+)
367
340
 
368
- Schedule a Live Activity to start at a future time. The system starts the
369
- activity automatically without the app being in the foreground. Use for events
370
- with known start times (sports games, product launches, scheduled deliveries).
341
+ `Activity.request(...style:)` is available from iOS 18, not only iOS 26.
371
342
 
372
- ```swift
373
- let scheduledDate = Calendar.current.date(
374
- from: DateComponents(year: 2026, month: 3, day: 15, hour: 19, minute: 0)
375
- )!
376
-
377
- let activity = try Activity.request(
378
- attributes: attributes,
379
- content: content,
380
- pushType: .token,
381
- style: .standard,
382
- alertConfiguration: AlertConfiguration(
383
- title: "Game Starting",
384
- body: "The live score is ready.",
385
- sound: .default
386
- ),
387
- start: scheduledDate
388
- )
389
- ```
343
+ - `.standard`: for anything that should persist: deliveries, rides, scores,
344
+ timers, order boards.
345
+ - `.transient`: only for brief expanded Dynamic Island moments. The system may
346
+ end it when the device locks, when the expanded view collapses, when the
347
+ user leaves the app, or when the activity would appear outside the Dynamic
348
+ Island.
390
349
 
391
- ### ActivityStyle (iOS 18+ request parameter)
350
+ ### Scheduled start (iOS 26+)
392
351
 
393
- Use the iOS 18+ `style:` request parameter to choose persistence behavior. Use
394
- `.standard` for persistent Live Activities such as deliveries, rides, sports
395
- scores, timers, and order-status boards. Use `.transient` only for a
396
- short-lived expanded Dynamic Island presentation; it can auto-end when the user
397
- locks the device, collapses or shrinks the expanded presentation, leaves the
398
- app, or does other work outside Dynamic Island.
352
+ For events with a known start time (a match, a product launch, a booked
353
+ delivery slot), schedule the activity and let the system start it without the
354
+ app in the foreground:
399
355
 
400
356
  ```swift
357
+ guard let kickoff = Calendar.current.date(
358
+ from: DateComponents(year: 2026, month: 11, day: 14, hour: 19, minute: 30)
359
+ ) else { return }
401
360
  let activity = try Activity.request(
402
361
  attributes: attributes,
403
362
  content: content,
404
363
  pushType: .token,
405
- style: .standard
406
- )
407
- ```
408
-
409
- ### Paired Mac & CarPlay (iOS 26+)
410
-
411
- Live Activities can appear on a paired Mac and on the CarPlay Home Screen. No additional ActivityKit API is required, but validate compact layouts; buttons and toggles in Live Activities do not perform actions in CarPlay.
412
-
413
- ### Channel-Based Push (iOS 18+)
414
-
415
- Broadcast updates to many Live Activities at once with an APNs-created channel
416
- ID. Enable the broadcast capability outside Xcode, create the channel on the
417
- server, then subscribe with `.channel(channelID)`. Channel pushes update or end
418
- Live Activities; they do not start them. Use `apns-channel-id` and expiration
419
- for channel pushes instead of the device-token `apns-topic` example above.
420
-
421
- ```swift
422
- let activity = try Activity.request(
423
- attributes: attributes, content: content,
424
- pushType: .channel(channelIDFromServer)
364
+ style: .standard,
365
+ alertConfiguration: AlertConfiguration(
366
+ title: "Match starting", body: "Tap to follow live.", sound: .default
367
+ ),
368
+ start: kickoff
425
369
  )
426
370
  ```
427
371
 
428
- ## Common Mistakes
429
-
430
- **DON'T:** Put too much content in the compact presentation -- it is tiny.
431
- **DO:** Show only the most critical info (icon + one value) in compact leading/trailing.
432
-
433
- **DON'T:** Update Live Activities too frequently from the app (drains battery).
434
- **DO:** Use push-to-update for server-driven updates. Limit app-side updates to user actions.
435
-
436
- **DON'T:** Forget to end the activity when the event reaches any terminal state.
437
- **DO:** End activities on success, cancellation, sign-out, unrecoverable errors, and terminal server failures. A leaked activity frustrates users.
438
-
439
- **DON'T:** Assume every device has Dynamic Island.
440
- **DO:** Design for the Lock Screen as the primary surface; Dynamic Island is supplementary.
441
-
442
- **DON'T:** Treat Lock Screen or Dynamic Island Live Activities as ordinary Home Screen/timeline widgets.
443
- **DO:** Use ActivityKit for the Live Activity lifecycle and those display surfaces; route ordinary Home Screen/timeline widgets to `widgetkit`.
444
-
445
- **DON'T:** Reduce Live Activity payload routing to generic `content-state` matching when the prompt involves APNs payloads.
446
- **DO:** Include the actual `ContentState` `Codable` contract and coordinated `Date`/`ClosedRange<Date>` encoding caveat; route generic APNs auth and registration to `push-notifications`.
447
-
448
- **DON'T:** Store sensitive information in ActivityAttributes (visible on Lock Screen).
449
- **DO:** Keep sensitive data in the app and show only safe-to-display summaries.
450
-
451
- **DON'T:** Forget to handle stale dates.
452
- **DO:** Check `context.isStale` in views and show fallback UI ("Updating..." or similar).
453
-
454
- **DON'T:** Ignore push token rotation. Tokens can change at any time.
455
- **DO:** Use `activity.pushTokenUpdates` async sequence and re-register on every emission.
456
-
457
- **DON'T:** Forget the `NSSupportsLiveActivities` Info.plist key.
458
- **DO:** Add `NSSupportsLiveActivities = YES` to the host app's Info.plist (not the extension).
459
-
460
- **DON'T:** Use the deprecated `contentState`-based API for request/update/end.
461
- **DO:** Use `ActivityContent` for all lifecycle calls.
462
-
463
- **DON'T:** Fetch network data or location directly from Live Activity views.
464
- **DO:** Pre-compute display values in the app or server and pass them through ActivityKit updates or pushes.
465
-
466
- ## Review Checklist
467
-
468
- - [ ] `ActivityAttributes` defines static properties and `ContentState`
469
- - [ ] `NSSupportsLiveActivities = YES` in host app Info.plist
470
- - [ ] Activity uses `ActivityContent` (not deprecated contentState API)
471
- - [ ] Activity ended in all terminal paths (success, error, cancellation, sign-out, terminal server failure)
472
- - [ ] ActivityKit lifecycle and Lock Screen/Dynamic Island Live Activity surfaces are separated from ordinary Home Screen/timeline widget work
473
- - [ ] Lock Screen layout, the primary Live Activity surface, handles `context.isStale`
474
- - [ ] Dynamic Island compact, expanded, and minimal implemented with Lock Screen fallback
475
- - [ ] Push update token forwarded to server via `activity.pushTokenUpdates`
476
- - [ ] Push-to-start token collected via `Activity<Attributes>.pushToStartTokenUpdates`
477
- - [ ] Push-to-start payload includes required `alert`
478
- - [ ] `content-state` JSON matches the actual `ContentState` `Codable` shape, including coordinated date/range encoding
479
- - [ ] Review distinguishes 8-hour active lifetime, 12-hour total system-ended Lock Screen presence, and 4-hour app-ended `.default` linger
372
+ ### Paired Mac and CarPlay (iOS 26+)
373
+
374
+ A paired Mac and the CarPlay Home Screen both show Live Activities, and
375
+ ActivityKit needs no additional code for either. Check that the compact layouts read well there.
376
+ Buttons and toggles inside a Live Activity do nothing in CarPlay.
377
+
378
+ ### Channel pushes (iOS 18+)
379
+
380
+ One push can update many activities through a channel ID that APNs creates.
381
+ Turn on the broadcast capability for the app in the developer portal (it is not
382
+ an Xcode setting), have the server create the channel, then request with `pushType: .channel(channelID)`. Channel pushes
383
+ update or end activities; they cannot start one. They are addressed with
384
+ `apns-channel-id` and an expiration rather than the device-token `apns-topic`.
385
+
386
+ ## Common mistakes
387
+
388
+ 1. A crowded compact view. One icon and one value is the budget.
389
+ 2. Updating from the app too often, which drains battery. Let the server push
390
+ its own changes; update locally only in response to user actions.
391
+ 3. Forgetting to end on some terminal path. A leftover activity is a bug the
392
+ user has to clear by hand.
393
+ 4. Designing for the Dynamic Island first. Many devices have none; the Lock
394
+ Screen is primary.
395
+ 5. Treating a Live Activity as a Home Screen widget with a timeline. Use the
396
+ ActivityKit lifecycle; send timeline widgets to `widgetkit`.
397
+ 6. Reducing payload guidance to "match the content-state". State the real
398
+ `ContentState` `Codable` contract and the `Date` / `ClosedRange<Date>`
399
+ caveat; only the generic APNs pieces (auth keys, registration) move to
400
+ `push-notifications`.
401
+ 7. Sensitive values in attributes or state. The card is readable on a locked
402
+ phone; show a safe summary.
403
+ 8. Ignoring `staleDate`. Check `context.isStale` and render a fallback.
404
+ 9. Registering the update token once. It can change at any time; re-send it on
405
+ every `pushTokenUpdates` value.
406
+ 10. `NSSupportsLiveActivities` placed in the extension target's plist. The key
407
+ is read from the main app.
408
+ 11. The deprecated `contentState:` variants of request, update and end. Use
409
+ `ActivityContent` throughout.
410
+ 12. Network or location work inside Live Activity views. Compute it in the app
411
+ or on the server and deliver the result through an update or a push.
412
+
413
+ ## Review checklist
414
+
415
+ - [ ] `ActivityAttributes` separates static properties from `ContentState`
416
+ - [ ] Host app Info.plist has `NSSupportsLiveActivities = YES`
417
+ - [ ] Request, update and end use `ActivityContent`, not the deprecated
418
+ `contentState` API
419
+ - [ ] Activity ends on success, error, cancellation, sign-out and terminal
420
+ server failure
421
+ - [ ] ActivityKit lifecycle and surfaces are kept apart from Home Screen or
422
+ timeline widget code
423
+ - [ ] Lock Screen view handles `context.isStale`
424
+ - [ ] Compact, expanded and minimal Dynamic Island modes exist, with the Lock
425
+ Screen as the fallback
426
+ - [ ] Update token forwarded from `activity.pushTokenUpdates`
427
+ - [ ] Push-to-start token collected from
428
+ `Activity<Attributes>.pushToStartTokenUpdates`
429
+ - [ ] Push-to-start payload carries `alert`
430
+ - [ ] `content-state` JSON matches the real `ContentState` coding, including an
431
+ agreed date and range encoding
432
+ - [ ] Durations are stated separately: 8 h active, 12 h total after a system
433
+ end, 4 h linger after an app end with `.default`
480
434
  - [ ] `ActivityAuthorizationInfo` checked before starting
481
- - [ ] `frequentPushesEnabled` checked before assuming high-cadence pushes
482
- - [ ] ContentState kept small (serialized on every update)
483
- - [ ] iOS 18+ availability guarded for `style:`, `.channel`, and supplemental families
484
- - [ ] iOS 18+ `style:` choices are justified: `.standard` for persistent Live Activities, `.transient` only for short-lived expanded Dynamic Island presentations
485
- - [ ] ActivityKit push priority and `aps.alert` behavior are handled separately
486
- - [ ] Live Activity views avoid direct network/location work
487
- - [ ] Tested on device for push delivery and Dynamic Island behavior
435
+ - [ ] `frequentPushesEnabled` checked before relying on high-cadence pushes
436
+ - [ ] `ContentState` is small
437
+ - [ ] `style:`, `.channel` and supplemental families are gated on iOS 18
438
+ - [ ] Each `style:` choice is justified: `.standard` for persistent,
439
+ `.transient` only for short expanded moments
440
+ - [ ] Push priority and `aps.alert` are handled as separate concerns
441
+ - [ ] Views do no network or location work
442
+ - [ ] Push delivery and Dynamic Island tested on a real device
488
443
 
489
444
  ## References
490
445
 
491
- - See [references/activitykit-patterns.md](references/activitykit-patterns.md) for patterns and code examples
446
+ - [ActivityKit patterns](references/activitykit-patterns.md): complete
447
+ attributes model, start/update/end variants, server payloads and headers, channel pushes, concurrent activities, state and token observation,
448
+ authorization, errors, background behaviour, testing, Info.plist keys and
449
+ documentation links.