@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,591 +1,541 @@
1
- # AlarmKit Patterns
1
+ # AlarmKit patterns
2
2
 
3
- Complete implementation patterns for AlarmKit alarms, countdown timers,
4
- authorization, state observation, and Live Activity integration. All patterns
5
- target iOS 26+ / iPadOS 26+ with Swift 6.3. The alerting UI is system-managed;
6
- countdown and paused states use a widget extension for custom Live Activity UI.
3
+ Longer, end-to-end examples. Everything here targets iOS 26 and iPadOS 26 with
4
+ Swift 6.3. Code that uses the stopButton-free
5
+ `AlarmPresentation.Alert(title:secondaryButton:secondaryButtonBehavior:)`
6
+ initializer is marked `@available(iOS 26.1, *)`.
7
7
 
8
8
  ## Contents
9
- - Complete Alarm Scheduling Flow
10
- - Complete Countdown Timer Flow
11
- - Authorization Manager
12
- - State Observation with Async Sequences
13
- - Live Activity Widget Extension for Alarms
14
- - Recurring Alarm Patterns
15
- - Snooze and Dismiss Handling
16
- - Info.plist Configuration
17
- - Error Handling
18
- - Apple Documentation Links
19
9
 
20
- ## Complete Alarm Scheduling Flow
10
+ - [Full alarm flow with intents](#full-alarm-flow-with-intents)
11
+ - [Full timer flow](#full-timer-flow)
12
+ - [Authorization manager](#authorization-manager)
13
+ - [State store with async sequences](#state-store-with-async-sequences)
14
+ - [Widget extension for countdown and paused UI](#widget-extension-for-countdown-and-paused-ui)
15
+ - [Recurrence](#recurrence)
16
+ - [Snooze and custom secondary actions](#snooze-and-custom-secondary-actions)
17
+ - [Info.plist](#infoplist)
18
+ - [Error handling](#error-handling)
19
+ - [Apple documentation](#apple-documentation)
21
20
 
22
- End-to-end pattern for scheduling a wake-up alarm with snooze support.
21
+ ## Full alarm flow with intents
22
+
23
+ Stop and snooze intents are `LiveActivityIntent` types from AppIntents. Each one
24
+ receives the alarm ID as a string parameter and calls back into `AlarmManager`.
23
25
 
24
26
  ```swift
25
27
  import AlarmKit
26
28
  import AppIntents
29
+ import SwiftUI
30
+
31
+ struct DismissMedicineAlarm: LiveActivityIntent {
32
+ static let title: LocalizedStringResource = "Dismiss medicine reminder"
33
+
34
+ @Parameter(title: "Alarm ID")
35
+ var alarmID: String
27
36
 
28
- struct StopAlarmIntent: LiveActivityIntent {
29
- static var title: LocalizedStringResource = "Stop Alarm"
30
- @Parameter(title: "Alarm ID") var alarmID: String
31
37
  init() {}
32
- init(alarmID: String) { self.alarmID = alarmID }
38
+ init(alarmID: UUID) { self.alarmID = alarmID.uuidString }
39
+
33
40
  func perform() async throws -> some IntentResult {
34
- try AlarmManager.shared.stop(id: UUID(uuidString: alarmID)!)
41
+ if let id = UUID(uuidString: alarmID) {
42
+ try AlarmManager.shared.stop(id: id)
43
+ }
35
44
  return .result()
36
45
  }
37
46
  }
38
47
 
39
- struct SnoozeAlarmIntent: LiveActivityIntent {
40
- static var title: LocalizedStringResource = "Snooze Alarm"
41
- @Parameter(title: "Alarm ID") var alarmID: String
48
+ struct RemindAgainIntent: LiveActivityIntent {
49
+ static let title: LocalizedStringResource = "Remind again"
50
+
51
+ @Parameter(title: "Alarm ID")
52
+ var alarmID: String
53
+
42
54
  init() {}
43
- init(alarmID: String) { self.alarmID = alarmID }
55
+ init(alarmID: UUID) { self.alarmID = alarmID.uuidString }
56
+
44
57
  func perform() async throws -> some IntentResult {
45
- try AlarmManager.shared.countdown(id: UUID(uuidString: alarmID)!)
58
+ if let id = UUID(uuidString: alarmID) {
59
+ try AlarmManager.shared.countdown(id: id)
60
+ }
46
61
  return .result()
47
62
  }
48
63
  }
49
64
 
50
- struct WakeUpMetadata: AlarmMetadata {
51
- var label: String
65
+ struct DoseInfo: AlarmMetadata {
66
+ var medicine: String
67
+ var doseLabel: String
52
68
  }
53
69
 
70
+ enum DoseAlarmError: Error {
71
+ case accessNotGranted
72
+ }
73
+
74
+ @available(iOS 26.1, *)
54
75
  @MainActor
55
- func scheduleWakeUpAlarm(
56
- hour: Int, minute: Int, label: String
57
- ) async throws -> Alarm {
58
- let manager = AlarmManager.shared
59
- let authState = try await manager.requestAuthorization()
60
- guard authState == .authorized else { throw AlarmSchedulingError.notAuthorized }
76
+ func scheduleDoseReminder(hour: Int, minute: Int, medicine: String) async throws -> Alarm {
77
+ let center = AlarmManager.shared
78
+ guard try await center.requestAuthorization() == .authorized else {
79
+ throw DoseAlarmError.accessNotGranted
80
+ }
61
81
 
62
82
  let alert = AlarmPresentation.Alert(
63
- title: LocalizedStringResource(stringLiteral: label),
64
- secondaryButton: AlarmButton(
65
- text: "Snooze", textColor: .white, systemImageName: "bell.slash"
66
- ),
83
+ title: LocalizedStringResource(stringLiteral: "Time for \(medicine)"),
84
+ secondaryButton: AlarmButton(text: "In 5 min", textColor: .white, systemImageName: "clock.arrow.circlepath"),
67
85
  secondaryButtonBehavior: .countdown
68
86
  )
69
- let presentation = AlarmPresentation(alert: alert)
70
87
  let attributes = AlarmAttributes(
71
- presentation: presentation,
72
- metadata: WakeUpMetadata(label: label),
88
+ presentation: AlarmPresentation(alert: alert),
89
+ metadata: DoseInfo(medicine: medicine, doseLabel: "1 tablet"),
73
90
  tintColor: .indigo
74
91
  )
75
92
 
93
+ let doseTime = Alarm.Schedule.Relative.Time(hour: hour, minute: minute)
76
94
  let id = UUID()
77
- let snooze = Alarm.CountdownDuration(preAlert: nil, postAlert: 300)
78
95
  let config = AlarmManager.AlarmConfiguration(
79
- countdownDuration: snooze,
80
- schedule: .relative(.init(
81
- time: .init(hour: hour, minute: minute), repeats: .never
82
- )),
96
+ countdownDuration: Alarm.CountdownDuration(preAlert: nil, postAlert: 5 * 60),
97
+ schedule: .relative(.init(time: doseTime)),
83
98
  attributes: attributes,
84
- stopIntent: StopAlarmIntent(alarmID: id.uuidString),
85
- secondaryIntent: SnoozeAlarmIntent(alarmID: id.uuidString),
99
+ stopIntent: DismissMedicineAlarm(alarmID: id),
100
+ secondaryIntent: RemindAgainIntent(alarmID: id),
86
101
  sound: .default
87
102
  )
88
- return try await manager.schedule(id: id, configuration: config)
89
- }
90
-
91
- enum AlarmSchedulingError: Error {
92
- case notAuthorized
103
+ return try await center.schedule(id: id, configuration: config)
93
104
  }
94
105
  ```
95
106
 
96
- ## Complete Countdown Timer Flow
107
+ Generate the `UUID` first. Both intents need to carry the same ID string that
108
+ `schedule(id:configuration:)` will use, so the ID must exist before the intents
109
+ are built. Remember the intents themselves are optional; this example passes
110
+ them only to show the wiring. Declare the intent's `title` with `static let`:
111
+ in the Swift 6 language mode a `static var` is rejected as unprotected global
112
+ mutable state.
97
113
 
98
- End-to-end pattern for a countdown timer with pause/resume support.
114
+ ## Full timer flow
99
115
 
100
116
  ```swift
101
117
  import AlarmKit
102
- import AppIntents
103
-
104
- struct StopTimerIntent: LiveActivityIntent {
105
- static var title: LocalizedStringResource = "Stop Timer"
106
- @Parameter(title: "Timer ID") var timerID: String
107
- init() {}
108
- init(timerID: String) { self.timerID = timerID }
109
- func perform() async throws -> some IntentResult {
110
- try AlarmManager.shared.stop(id: UUID(uuidString: timerID)!)
111
- return .result()
112
- }
113
- }
118
+ import SwiftUI
114
119
 
115
- struct CookingTimerMetadata: AlarmMetadata {
116
- var recipeName: String
117
- var stepDescription: String
120
+ struct BakeStep: AlarmMetadata {
121
+ var loafName: String
122
+ var stage: String
118
123
  }
119
124
 
125
+ @available(iOS 26.1, *)
120
126
  @MainActor
121
- func startCookingTimer(
122
- durationSeconds: TimeInterval, recipeName: String, step: String
123
- ) async throws -> Alarm {
124
- let manager = AlarmManager.shared
125
- let authState = try await manager.requestAuthorization()
126
- guard authState == .authorized else { throw AlarmSchedulingError.notAuthorized }
127
-
128
- let alert = AlarmPresentation.Alert(
129
- title: LocalizedStringResource(stringLiteral: "\(recipeName): \(step)"),
130
- secondaryButton: nil, secondaryButtonBehavior: nil
131
- )
132
- let countdown = AlarmPresentation.Countdown(
133
- title: LocalizedStringResource(stringLiteral: recipeName),
134
- pauseButton: AlarmButton(
135
- text: "Pause", textColor: .orange, systemImageName: "pause.fill"
136
- )
137
- )
138
- let paused = AlarmPresentation.Paused(
139
- title: "Paused",
140
- resumeButton: AlarmButton(
141
- text: "Resume", textColor: .green, systemImageName: "play.fill"
142
- )
143
- )
127
+ func startProofingTimer(minutes: Double) async throws -> Alarm {
144
128
  let presentation = AlarmPresentation(
145
- alert: alert, countdown: countdown, paused: paused
129
+ alert: AlarmPresentation.Alert(title: "Dough has proofed", secondaryButton: nil, secondaryButtonBehavior: nil),
130
+ countdown: AlarmPresentation.Countdown(
131
+ title: "Proofing",
132
+ pauseButton: AlarmButton(text: "Pause", textColor: .brown, systemImageName: "pause.fill")
133
+ ),
134
+ paused: AlarmPresentation.Paused(
135
+ title: "Proofing paused",
136
+ resumeButton: AlarmButton(text: "Resume", textColor: .brown, systemImageName: "play.fill")
137
+ )
146
138
  )
147
139
  let attributes = AlarmAttributes(
148
140
  presentation: presentation,
149
- metadata: CookingTimerMetadata(recipeName: recipeName, stepDescription: step),
141
+ metadata: BakeStep(loafName: "Rye", stage: "Second rise"),
150
142
  tintColor: .orange
151
143
  )
152
-
153
- let id = UUID()
154
144
  let config = AlarmManager.AlarmConfiguration.timer(
155
- duration: durationSeconds,
145
+ duration: minutes * 60,
156
146
  attributes: attributes,
157
- stopIntent: StopTimerIntent(timerID: id.uuidString),
147
+ stopIntent: nil,
158
148
  sound: .default
159
149
  )
160
- return try await manager.schedule(id: id, configuration: config)
150
+ let center = AlarmManager.shared
151
+ return try await center.schedule(id: UUID(), configuration: config)
161
152
  }
162
153
  ```
163
154
 
164
- ## Authorization Manager
165
-
166
- Observable pattern for centralized authorization management.
155
+ ## Authorization manager
167
156
 
168
157
  ```swift
169
158
  import AlarmKit
170
159
  import Observation
160
+ import SwiftUI
171
161
 
172
162
  @Observable
173
163
  @MainActor
174
- final class AlarmAuthorizationManager {
175
- private let manager = AlarmManager.shared
176
- private(set) var isAuthorized = false
177
- private(set) var authState: AlarmManager.AuthorizationState = .notDetermined
164
+ final class AlarmAccess {
165
+ private let center = AlarmManager.shared
166
+ private(set) var state: AlarmManager.AuthorizationState
178
167
 
179
168
  init() {
180
- authState = manager.authorizationState
181
- isAuthorized = authState == .authorized
169
+ state = AlarmManager.shared.authorizationState
182
170
  }
183
171
 
184
- func requestIfNeeded() async throws -> Bool {
185
- guard authState == .notDetermined else { return isAuthorized }
186
- let state = try await manager.requestAuthorization()
187
- authState = state
188
- isAuthorized = state == .authorized
189
- return isAuthorized
172
+ func askIfNeeded() async {
173
+ guard state == .notDetermined else { return }
174
+ if let answer = try? await AlarmManager.shared.requestAuthorization() {
175
+ state = answer
176
+ }
190
177
  }
191
178
 
192
- func observeAuthorizationChanges() async {
193
- for await state in manager.authorizationUpdates {
194
- authState = state
195
- isAuthorized = state == .authorized
179
+ func follow() async {
180
+ for await next in center.authorizationUpdates {
181
+ state = next
196
182
  }
197
183
  }
198
184
  }
199
185
 
200
- // Usage in SwiftUI
201
- struct AlarmSettingsView: View {
202
- @State private var authManager = AlarmAuthorizationManager()
186
+ struct AlarmAccessView: View {
187
+ @State private var access = AlarmAccess()
203
188
 
204
189
  var body: some View {
205
190
  Group {
206
- if authManager.isAuthorized {
207
- Text("Alarms are enabled")
208
- } else if authManager.authState == .denied {
191
+ switch access.state {
192
+ case .authorized:
193
+ Text("Alarms are on")
194
+ case .denied:
209
195
  ContentUnavailableView(
210
- "Alarms Disabled", systemImage: "alarm.waves.left.and.right",
211
- description: Text("Enable in Settings > Your App > Alarms & Timers.")
196
+ "Alarms are off",
197
+ systemImage: "alarm",
198
+ description: Text("Turn them on in Settings > this app > Alarms & Timers.")
212
199
  )
213
- } else {
214
- Button("Enable Alarms") {
215
- Task { try? await authManager.requestIfNeeded() }
200
+ default:
201
+ Button("Allow alarms") {
202
+ Task { await access.askIfNeeded() }
216
203
  }
217
204
  }
218
205
  }
219
- .task { await authManager.observeAuthorizationChanges() }
206
+ .task { await access.follow() }
220
207
  }
221
208
  }
222
209
  ```
223
210
 
224
- ## State Observation with Async Sequences
211
+ The path users follow to recover from a denial is Settings, then the app's own
212
+ page, then Alarms & Timers.
225
213
 
226
- Pattern for tracking all alarms and reacting to state changes.
214
+ `askIfNeeded()` reads `AlarmManager.shared` at the call instead of the stored
215
+ `center`. `AlarmManager` is a non-`Sendable` class and `requestAuthorization()`
216
+ is a nonisolated `async` method, so calling it on a property of a main-actor
217
+ object sends main-actor state off the actor, and Swift 6 rejects that in its
218
+ region-isolation pass ("sending 'self.center' risks causing data races").
219
+
220
+ ## State store with async sequences
227
221
 
228
222
  ```swift
229
223
  import AlarmKit
230
224
  import Observation
225
+ import SwiftUI
231
226
 
232
227
  @Observable
233
228
  @MainActor
234
- final class AlarmStore {
235
- private let manager = AlarmManager.shared
236
- private(set) var alarms: [Alarm] = []
229
+ final class AlarmBoard {
230
+ private let center = AlarmManager.shared
231
+ private(set) var items: [Alarm] = []
237
232
 
238
- func refreshAlarms() throws {
239
- alarms = try manager.alarms
233
+ func reload() throws {
234
+ items = try center.alarms
240
235
  }
241
236
 
242
- func startObserving() async {
243
- for await updatedAlarms in manager.alarmUpdates {
244
- alarms = updatedAlarms
237
+ func observe() async {
238
+ for await snapshot in center.alarmUpdates {
239
+ items = snapshot
245
240
  }
246
241
  }
247
242
 
248
- func alarm(for id: UUID) -> Alarm? { alarms.first { $0.id == id } }
249
- func alarms(in state: Alarm.State) -> [Alarm] { alarms.filter { $0.state == state } }
243
+ func item(for id: Alarm.ID) -> Alarm? {
244
+ items.first { $0.id == id }
245
+ }
250
246
 
251
- func cancel(_ id: Alarm.ID) throws { try manager.cancel(id: id) }
252
- func pause(_ id: Alarm.ID) throws { try manager.pause(id: id) }
253
- func resume(_ id: Alarm.ID) throws { try manager.resume(id: id) }
254
- func stop(_ id: Alarm.ID) throws { try manager.stop(id: id) }
255
- func snooze(_ id: Alarm.ID) throws { try manager.countdown(id: id) }
247
+ func items(in state: Alarm.State) -> [Alarm] {
248
+ items.filter { $0.state == state }
249
+ }
250
+
251
+ func cancel(_ alarmID: Alarm.ID) throws { try center.cancel(id: alarmID) }
252
+ func pause(_ alarmID: Alarm.ID) throws { try center.pause(id: alarmID) }
253
+ func resume(_ alarmID: Alarm.ID) throws { try center.resume(id: alarmID) }
254
+ func stop(_ alarmID: Alarm.ID) throws { try center.stop(id: alarmID) }
255
+ func snooze(_ alarmID: Alarm.ID) throws { try center.countdown(id: alarmID) }
256
256
  }
257
257
 
258
- // Usage in SwiftUI
259
- struct AlarmListView: View {
260
- @State private var store = AlarmStore()
258
+ struct AlarmBoardView: View {
259
+ @State private var board = AlarmBoard()
261
260
 
262
261
  var body: some View {
263
- List(store.alarms, id: \.id) { alarm in
262
+ List(board.items) { alarm in
264
263
  HStack {
265
- Text(alarm.id.uuidString.prefix(8)).font(.headline)
264
+ Text(alarm.id.uuidString.prefix(6))
266
265
  Spacer()
267
266
  switch alarm.state {
268
267
  case .scheduled:
269
- Button("Cancel", role: .destructive) { try? store.cancel(alarm.id) }
268
+ Button("Cancel") { try? board.cancel(alarm.id) }
270
269
  case .countdown:
271
- Button("Pause") { try? store.pause(alarm.id) }
270
+ Button("Pause") { try? board.pause(alarm.id) }
272
271
  case .paused:
273
- Button("Resume") { try? store.resume(alarm.id) }
272
+ Button("Resume") { try? board.resume(alarm.id) }
274
273
  case .alerting:
275
- Button("Stop") { try? store.stop(alarm.id) }
274
+ Button("Stop") { try? board.stop(alarm.id) }
276
275
  @unknown default:
277
276
  EmptyView()
278
277
  }
279
278
  }
280
279
  }
281
280
  .task {
282
- try? store.refreshAlarms()
283
- await store.startObserving()
281
+ try? board.reload()
282
+ await board.observe()
284
283
  }
285
284
  }
286
285
  }
287
286
  ```
288
287
 
289
- ## Live Activity Widget Extension for Alarms
288
+ ## Widget extension for countdown and paused UI
290
289
 
291
- Widget extension that renders countdown and paused states. AlarmKit expects this
292
- when your alarm uses countdown presentation; the system fallback countdown UI is
293
- limited to cases such as after restart before first unlock.
290
+ The system only falls back to its own countdown UI in narrow situations, such as
291
+ a device that restarted and has not been unlocked yet. Anything that shows a
292
+ countdown should ship this extension.
294
293
 
295
294
  ```swift
296
- import WidgetKit
297
- import SwiftUI
295
+ import ActivityKit
298
296
  import AlarmKit
297
+ import SwiftUI
298
+ import WidgetKit
299
299
 
300
- // MARK: - Widget bundle
301
-
302
- struct AlarmWidgetBundle: WidgetBundle {
303
- var body: some Widget {
304
- AlarmLiveActivityWidget()
305
- }
300
+ struct LapInfo: AlarmMetadata {
301
+ var routeName: String
302
+ var segment: String
306
303
  }
307
304
 
308
- // MARK: - Live Activity configuration
309
-
310
- struct AlarmLiveActivityWidget: Widget {
305
+ struct LapAlarmActivity: Widget {
311
306
  var body: some WidgetConfiguration {
312
- ActivityConfiguration(for: AlarmAttributes<CookingTimerMetadata>.self) { context in
313
- AlarmLockScreenView(context: context)
307
+ ActivityConfiguration(for: AlarmAttributes<LapInfo>.self) { context in
308
+ LapLockScreenView(context: context)
314
309
  } dynamicIsland: { context in
315
310
  DynamicIsland {
316
311
  DynamicIslandExpandedRegion(.leading) {
317
- Image(systemName: "alarm.fill")
318
- .font(.title2)
312
+ Image(systemName: "figure.run")
319
313
  .foregroundStyle(context.attributes.tintColor)
320
314
  }
321
315
  DynamicIslandExpandedRegion(.trailing) {
322
- AlarmCountdownText(state: context.state)
323
- .font(.title3.monospacedDigit())
316
+ remainingLabel(for: context.state.mode)
317
+ .monospacedDigit()
324
318
  }
325
319
  DynamicIslandExpandedRegion(.center) {
326
- Text(context.attributes.metadata?.recipeName ?? "Timer")
327
- .font(.headline)
320
+ Text(context.attributes.metadata?.routeName ?? "Run")
328
321
  }
329
322
  DynamicIslandExpandedRegion(.bottom) {
330
- if let step = context.attributes.metadata?.stepDescription {
331
- Text(step).font(.subheadline).foregroundStyle(.secondary)
332
- }
323
+ Text(context.attributes.metadata?.segment ?? "")
324
+ .font(.caption)
333
325
  }
334
326
  } compactLeading: {
335
- Image(systemName: "alarm.fill").foregroundStyle(context.attributes.tintColor)
327
+ Image(systemName: "figure.run")
336
328
  } compactTrailing: {
337
- AlarmCountdownText(state: context.state)
338
- .frame(width: 44).monospacedDigit()
329
+ remainingLabel(for: context.state.mode)
330
+ .monospacedDigit()
331
+ .frame(maxWidth: 44)
339
332
  } minimal: {
340
- Image(systemName: "alarm.fill").foregroundStyle(context.attributes.tintColor)
333
+ Image(systemName: "figure.run")
341
334
  }
342
335
  .keylineTint(context.attributes.tintColor)
343
336
  }
344
337
  }
345
338
  }
346
339
 
347
- // MARK: - Lock Screen view
348
-
349
- struct AlarmLockScreenView: View {
350
- let context: ActivityViewContext<AlarmAttributes<CookingTimerMetadata>>
340
+ struct LapLockScreenView: View {
341
+ let context: ActivityViewContext<AlarmAttributes<LapInfo>>
351
342
 
352
343
  var body: some View {
353
- VStack(alignment: .leading) {
354
- HStack {
355
- Image(systemName: "alarm.fill")
356
- .foregroundStyle(context.attributes.tintColor)
357
- Text(context.attributes.metadata?.recipeName ?? "Timer")
358
- .font(.headline)
359
- Spacer()
360
- AlarmCountdownText(state: context.state)
361
- .font(.title3.monospacedDigit().bold())
362
- }
363
-
364
- switch context.state.mode {
365
- case .countdown(let info):
366
- ProgressView(
367
- value: info.previouslyElapsedDuration,
368
- total: info.totalCountdownDuration
369
- )
370
- .tint(context.attributes.tintColor)
371
- case .paused:
372
- Label("Paused", systemImage: "pause.fill")
373
- .font(.subheadline).foregroundStyle(.secondary)
374
- case .alert:
375
- EmptyView() // System handles alerting UI
376
- @unknown default:
377
- EmptyView()
344
+ switch context.state.mode {
345
+ case .countdown(let run):
346
+ VStack(alignment: .leading) {
347
+ Text(context.attributes.presentation.countdown?.title ?? "Running")
348
+ ProgressView(value: run.previouslyElapsedDuration, total: run.totalCountdownDuration)
378
349
  }
350
+ .padding()
351
+ case .paused:
352
+ Label("Paused", systemImage: "pause.circle").padding()
353
+ case .alert:
354
+ EmptyView()
355
+ @unknown default:
356
+ EmptyView()
379
357
  }
380
- .padding()
381
358
  }
382
359
  }
383
360
 
384
- // MARK: - Helper views
385
-
386
- struct AlarmCountdownText: View {
387
- let state: AlarmPresentationState
388
-
389
- var body: some View {
390
- switch state.mode {
391
- case .countdown(let info):
392
- Text(info.fireDate, style: .timer)
393
- case .paused(let info):
394
- let remaining = info.totalCountdownDuration - info.previouslyElapsedDuration
395
- Text(Duration.seconds(remaining), format: .time(pattern: .minuteSecond))
396
- case .alert(let info):
397
- Text("\(info.time.hour):\(String(format: "%02d", info.time.minute))")
398
- @unknown default:
399
- Text("--:--")
400
- }
361
+ @ViewBuilder
362
+ func remainingLabel(for mode: AlarmPresentationState.Mode) -> some View {
363
+ switch mode {
364
+ case .countdown(let run):
365
+ Text(run.fireDate, style: .timer)
366
+ case .paused(let hold):
367
+ let left = hold.totalCountdownDuration - hold.previouslyElapsedDuration
368
+ Text(Duration.seconds(left).formatted(.time(pattern: .minuteSecond)))
369
+ case .alert(let ring):
370
+ Text(String(format: "%02d:%02d", ring.time.hour, ring.time.minute))
371
+ @unknown default:
372
+ EmptyView()
401
373
  }
402
374
  }
403
375
  ```
404
376
 
405
- ## Recurring Alarm Patterns
377
+ The alert case renders `EmptyView()` on the Lock Screen because the system draws
378
+ the ringing UI itself.
379
+
380
+ Fields by mode:
381
+
382
+ | Mode | Fields |
383
+ |---|---|
384
+ | `.countdown` | `fireDate`, `previouslyElapsedDuration`, `totalCountdownDuration` |
385
+ | `.paused` | `previouslyElapsedDuration`, `totalCountdownDuration` |
386
+ | `.alert` | `time` (`hour`, `minute`) |
387
+
388
+ ## Recurrence
406
389
 
407
390
  ```swift
408
- // Daily alarm (every day)
409
- let dailySchedule = Alarm.Schedule.relative(.init(
410
- time: .init(hour: 7, minute: 0),
411
- repeats: .weekly([.sunday, .monday, .tuesday, .wednesday,
412
- .thursday, .friday, .saturday])
413
- ))
414
-
415
- // Weekday-only alarm
416
- let weekdaySchedule = Alarm.Schedule.relative(.init(
417
- time: .init(hour: 6, minute: 30),
418
- repeats: .weekly([.monday, .tuesday, .wednesday, .thursday, .friday])
419
- ))
420
-
421
- // Weekend alarm
422
- let weekendSchedule = Alarm.Schedule.relative(.init(
423
- time: .init(hour: 9, minute: 0),
424
- repeats: .weekly([.saturday, .sunday])
425
- ))
426
-
427
- // One-time alarm at a specific Date
428
- let targetDate = Calendar.current.date(
429
- from: DateComponents(year: 2026, month: 6, day: 15, hour: 14, minute: 30)
430
- )!
431
- let fixedSchedule = Alarm.Schedule.fixed(targetDate)
391
+ import AlarmKit
392
+ import Foundation
393
+
394
+ let allWeek: [Locale.Weekday] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday]
395
+ let officeDays: [Locale.Weekday] = [.monday, .tuesday, .wednesday, .thursday, .friday]
396
+ let restDays: [Locale.Weekday] = [.saturday, .sunday]
397
+
398
+ let bedtime = Alarm.Schedule.relative(.init(time: .init(hour: 21, minute: 30), repeats: .weekly(allWeek)))
399
+ let commute = Alarm.Schedule.relative(.init(time: .init(hour: 7, minute: 15), repeats: .weekly(officeDays)))
400
+ let lazyMorning = Alarm.Schedule.relative(.init(time: .init(hour: 9, minute: 45), repeats: .weekly(restDays)))
401
+
402
+ func examMorning() -> Alarm.Schedule? {
403
+ let parts = DateComponents(calendar: .current, year: 2027, month: 1, day: 8, hour: 6, minute: 50)
404
+ guard let when = Calendar.current.date(from: parts) else { return nil }
405
+ return .fixed(when)
406
+ }
432
407
  ```
433
408
 
434
- ## Snooze and Dismiss Handling
409
+ "Every day" is simply `.weekly` with all seven weekdays. A single calendar date
410
+ is a `Date` built from `DateComponents` and passed to `.fixed`.
435
411
 
436
- Pattern for alarm with snooze (countdown behavior) and custom secondary action.
412
+ ## Snooze and custom secondary actions
437
413
 
438
- ### Snooze with countdown restart
414
+ For a standard snooze, give the alert a `.countdown` secondary button and set
415
+ `postAlert`; that value is the snooze length. A `Countdown` presentation is
416
+ what the user sees while the snooze runs. Metadata can be `nil`; give the
417
+ compiler the type (`Optional<NapInfo>.none`, or `nil as NapInfo?`) when it
418
+ cannot infer the generic parameter.
439
419
 
440
420
  ```swift
441
- func scheduleAlarmWithSnooze(
442
- hour: Int, minute: Int, snoozeDurationSeconds: TimeInterval
443
- ) async throws -> Alarm {
444
- let id = UUID()
445
- let alert = AlarmPresentation.Alert(
446
- title: "Good Morning",
447
- secondaryButton: AlarmButton(
448
- text: "Snooze 5 min", textColor: .white, systemImageName: "zzz"
449
- ),
450
- secondaryButtonBehavior: .countdown // tapping Snooze restarts countdown
451
- )
452
- // postAlert defines the snooze duration
453
- let countdown = Alarm.CountdownDuration(
454
- preAlert: nil, postAlert: snoozeDurationSeconds
455
- )
456
- let countdownPresentation = AlarmPresentation.Countdown(
457
- title: "Snoozing...", pauseButton: nil
458
- )
421
+ import AlarmKit
422
+ import AppIntents
423
+ import SwiftUI
424
+
425
+ struct NapInfo: AlarmMetadata {}
426
+
427
+ @available(iOS 26.1, *)
428
+ func napWithSnooze() -> AlarmManager.AlarmConfiguration<NapInfo> {
459
429
  let presentation = AlarmPresentation(
460
- alert: alert, countdown: countdownPresentation
461
- )
462
- let attributes = AlarmAttributes(
463
- presentation: presentation,
464
- metadata: nil as WakeUpMetadata?,
465
- tintColor: .purple
430
+ alert: .init(title: "Nap over",
431
+ secondaryButton: AlarmButton(text: "5 more", textColor: .white, systemImageName: "moon.zzz"),
432
+ secondaryButtonBehavior: .countdown),
433
+ countdown: .init(title: "Snoozing", pauseButton: nil)
466
434
  )
467
- let config = AlarmManager.AlarmConfiguration(
468
- countdownDuration: countdown,
469
- schedule: .relative(.init(
470
- time: .init(hour: hour, minute: minute), repeats: .never
471
- )),
435
+ let attributes = AlarmAttributes(presentation: presentation, metadata: Optional<NapInfo>.none, tintColor: .purple)
436
+ return AlarmManager.AlarmConfiguration(
437
+ countdownDuration: .init(preAlert: nil, postAlert: 300),
438
+ schedule: .fixed(Date(timeIntervalSinceNow: 1_200)),
472
439
  attributes: attributes,
473
- stopIntent: StopAlarmIntent(alarmID: id.uuidString),
474
- secondaryIntent: SnoozeAlarmIntent(alarmID: id.uuidString),
475
440
  sound: .default
476
441
  )
477
- return try await AlarmManager.shared.schedule(id: id, configuration: config)
478
442
  }
479
- ```
480
-
481
- ### Custom secondary action (open app)
482
443
 
483
- Use `.custom` behavior to trigger the `secondaryIntent` instead of restarting
484
- a countdown. The intent opens the app or performs custom logic.
444
+ struct ShowPackingList: LiveActivityIntent {
445
+ static let title: LocalizedStringResource = "Show packing list"
446
+ static let openAppWhenRun = true
485
447
 
486
- ```swift
487
- struct OpenAppIntent: LiveActivityIntent {
488
- static var title: LocalizedStringResource = "Open App"
489
- func perform() async throws -> some IntentResult { .result() }
448
+ func perform() async -> some IntentResult {
449
+ .result()
450
+ }
490
451
  }
491
452
 
492
- func scheduleAlarmWithOpenAction(hour: Int, minute: Int) async throws -> Alarm {
493
- let id = UUID()
453
+ @available(iOS 26.1, *)
454
+ func departureAlarm() -> AlarmManager.AlarmConfiguration<NapInfo> {
494
455
  let alert = AlarmPresentation.Alert(
495
- title: "Medication Reminder",
496
- secondaryButton: AlarmButton(
497
- text: "Open", textColor: .blue, systemImageName: "pill.fill"
498
- ),
499
- secondaryButtonBehavior: .custom // triggers secondaryIntent
500
- )
501
- let presentation = AlarmPresentation(alert: alert)
502
- let attributes = AlarmAttributes<WakeUpMetadata>(
503
- presentation: presentation, metadata: nil, tintColor: .blue
456
+ title: "Leave for the airport",
457
+ secondaryButton: AlarmButton(text: "Checklist", textColor: .white, systemImageName: "list.bullet"),
458
+ secondaryButtonBehavior: .custom
504
459
  )
505
- let config = AlarmManager.AlarmConfiguration.alarm(
506
- schedule: .relative(.init(
507
- time: .init(hour: hour, minute: minute), repeats: .never
508
- )),
460
+ let attributes = AlarmAttributes<NapInfo>(presentation: .init(alert: alert), tintColor: .blue)
461
+ return .alarm(
462
+ schedule: .fixed(Date(timeIntervalSinceNow: 7_200)),
509
463
  attributes: attributes,
510
- stopIntent: StopAlarmIntent(alarmID: id.uuidString),
511
- secondaryIntent: OpenAppIntent(),
464
+ stopIntent: nil,
465
+ secondaryIntent: ShowPackingList(),
512
466
  sound: .default
513
467
  )
514
- return try await AlarmManager.shared.schedule(id: id, configuration: config)
515
468
  }
516
469
  ```
517
470
 
518
- ## Info.plist Configuration
471
+ With `.custom`, pressing the secondary button runs `secondaryIntent` instead of
472
+ starting a countdown.
519
473
 
520
- ### Required key
521
-
522
- ```xml
523
- <key>NSAlarmKitUsageDescription</key>
524
- <string>We schedule alerts for alarms and timers you create.</string>
525
- ```
474
+ ## Info.plist
526
475
 
527
- This key is **mandatory**. If missing or empty, `schedule(id:configuration:)`
528
- will fail and no alarms can be created by the app.
476
+ | Key | Type | Notes |
477
+ |---|---|---|
478
+ | `NSAlarmKitUsageDescription` | String | Required. Without a non-empty value every `schedule(id:configuration:)` call fails, so the app cannot create alarms at all. |
529
479
 
530
- ### Countdown UI setup
480
+ - Put it in the containing app's Info.plist, not the extension's.
481
+ - Apple does not list `NSSupportsLiveActivities` as part of AlarmKit setup.
482
+ - Using countdown presentation means adding a widget extension target.
531
483
 
532
- If you support countdown presentation, add a widget extension target for the
533
- custom Live Activity UI. Keep `NSAlarmKitUsageDescription` in the host app's
534
- Info.plist; Apple does not document `NSSupportsLiveActivities` as an AlarmKit
535
- setup key.
484
+ ## Error handling
536
485
 
537
- ## Error Handling
486
+ `AlarmManager.AlarmConfiguration` is generic over the metadata type, written in
487
+ full as `AlarmManager.AlarmConfiguration<Metadata>`.
538
488
 
539
489
  ```swift
540
490
  import AlarmKit
541
491
 
542
- func scheduleAlarmSafely<Metadata: AlarmMetadata>(
543
- id: UUID,
544
- configuration: AlarmManager.AlarmConfiguration<Metadata>
545
- ) async {
546
- guard AlarmManager.shared.authorizationState == .authorized else {
547
- print("Not authorized -- request authorization first")
548
- return
549
- }
492
+ enum ScheduleOutcome {
493
+ case scheduled(Alarm)
494
+ case needsAccess
495
+ case tooMany
496
+ case failed(Error)
497
+ }
498
+
499
+ func trySchedule<M: AlarmMetadata>(_ config: AlarmManager.AlarmConfiguration<M>) async -> ScheduleOutcome {
500
+ let center = AlarmManager.shared
501
+ guard center.authorizationState == .authorized else { return .needsAccess }
550
502
  do {
551
- let alarm = try await AlarmManager.shared.schedule(
552
- id: id, configuration: configuration
553
- )
554
- print("Scheduled alarm: \(alarm.id), state: \(alarm.state)")
555
- } catch let error as AlarmManager.AlarmError {
556
- switch error {
503
+ return .scheduled(try await center.schedule(id: UUID(), configuration: config))
504
+ } catch let limitError as AlarmManager.AlarmError {
505
+ switch limitError {
557
506
  case .maximumLimitReached:
558
- print("Too many alarms -- cancel an existing one first")
507
+ return .tooMany
559
508
  @unknown default:
560
- print("AlarmKit error: \(error)")
509
+ return .failed(limitError)
561
510
  }
562
511
  } catch {
563
- print("Unexpected error: \(error)")
512
+ return .failed(error)
564
513
  }
565
514
  }
566
515
 
567
- // State transition helpers -- each throws if alarm is in wrong state
568
- func cancelAlarmSafely(id: Alarm.ID) {
569
- do { try AlarmManager.shared.cancel(id: id) }
570
- catch { print("Failed to cancel: \(error)") }
571
- }
572
-
573
- func pauseAlarmSafely(id: Alarm.ID) {
574
- // Only valid when alarm is in .countdown state
575
- do { try AlarmManager.shared.pause(id: id) }
576
- catch { print("Cannot pause: \(error)") }
516
+ func holdCountdown(_ alarmID: Alarm.ID) {
517
+ do {
518
+ try AlarmManager.shared.pause(id: alarmID)
519
+ } catch {
520
+ // pause(id:) only succeeds while the alarm is in .countdown
521
+ }
577
522
  }
578
523
  ```
579
524
 
580
- ## Apple Documentation Links
581
-
582
- - [AlarmKit](https://sosumi.ai/documentation/alarmkit)
583
- - [AlarmManager](https://sosumi.ai/documentation/alarmkit/alarmmanager)
584
- - [AlarmAttributes](https://sosumi.ai/documentation/alarmkit/alarmattributes)
585
- - [AlarmPresentation](https://sosumi.ai/documentation/alarmkit/alarmpresentation)
586
- - [AlarmPresentationState](https://sosumi.ai/documentation/alarmkit/alarmpresentationstate)
587
- - [AlarmButton](https://sosumi.ai/documentation/alarmkit/alarmbutton)
588
- - [Alarm](https://sosumi.ai/documentation/alarmkit/alarm)
589
- - [Alarm.Schedule](https://sosumi.ai/documentation/alarmkit/alarm/schedule-swift.enum)
590
- - [Alarm.CountdownDuration](https://sosumi.ai/documentation/alarmkit/alarm/countdownduration-swift.struct)
591
- - [Scheduling an alarm with AlarmKit](https://sosumi.ai/documentation/alarmkit/scheduling-an-alarm-with-alarmkit)
525
+ When `.maximumLimitReached` comes back, ask the user to remove an existing
526
+ alarm before adding another. The transition calls (`cancel`, `pause`,
527
+ `resume`, `stop`, `countdown`) throw when the alarm is in the wrong state, so
528
+ wrap them in `do/catch`; for example `pause` is valid only in `.countdown`.
529
+
530
+ ## Apple documentation
531
+
532
+ - [AlarmKit](https://developer.apple.com/documentation/alarmkit)
533
+ - [AlarmManager](https://developer.apple.com/documentation/alarmkit/alarmmanager)
534
+ - [AlarmAttributes](https://developer.apple.com/documentation/alarmkit/alarmattributes)
535
+ - [AlarmPresentation](https://developer.apple.com/documentation/alarmkit/alarmpresentation)
536
+ - [AlarmPresentationState](https://developer.apple.com/documentation/alarmkit/alarmpresentationstate)
537
+ - [AlarmButton](https://developer.apple.com/documentation/alarmkit/alarmbutton)
538
+ - [Alarm](https://developer.apple.com/documentation/alarmkit/alarm)
539
+ - [Alarm.Schedule](https://developer.apple.com/documentation/alarmkit/alarm/schedule)
540
+ - [Alarm.CountdownDuration](https://developer.apple.com/documentation/alarmkit/alarm/countdownduration)
541
+ - [Apple sample: scheduling alarms](https://developer.apple.com/documentation/alarmkit/scheduling-an-alarm-with-alarmkit)