@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +285 -285
  5. package/package.json +3 -3
  6. package/pipeline/lib/redact.mjs +3 -2
  7. package/pipeline/scripts/_notices.mjs +1 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +13 -1
  9. package/pipeline/scripts/pre-commit-check.sh +4 -0
  10. package/pipeline/skills/.skill-manifest.json +69 -69
  11. package/pipeline/skills/shared/README.md +70 -70
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  16. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  17. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  18. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  19. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  20. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  21. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  22. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  23. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +345 -277
  24. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  25. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +107 -121
  26. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +145 -165
  27. package/pipeline/skills/shared/external/app-store-review/SKILL.md +306 -326
  28. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  29. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  30. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  31. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +335 -360
  32. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  34. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  35. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  37. package/pipeline/skills/shared/external/authentication/SKILL.md +277 -381
  38. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  39. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +135 -178
  40. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  41. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  42. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  43. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  44. package/pipeline/skills/shared/external/background-processing/SKILL.md +274 -384
  45. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  46. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +173 -321
  47. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  48. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  49. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  50. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  51. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  52. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  53. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  54. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  55. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  56. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +228 -376
  57. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  58. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  59. package/pipeline/skills/shared/external/core-data/SKILL.md +302 -368
  60. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  61. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  62. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  63. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  64. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  65. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  66. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  67. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  68. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  69. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  70. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  71. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  72. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  73. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  74. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  75. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  76. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  77. package/pipeline/skills/shared/external/device-integrity/SKILL.md +236 -353
  78. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  79. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  80. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  81. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  82. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  83. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  84. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  85. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  86. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  87. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  88. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  89. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  90. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  91. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  92. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  93. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  94. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  95. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  96. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  97. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  98. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  99. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  100. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  101. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  102. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  103. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  104. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  105. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  106. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  107. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  108. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  109. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  111. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  112. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  113. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  114. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +3 -3
  115. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +1 -1
  116. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +8 -7
  117. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +2 -2
  118. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +5 -2
  119. package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +100 -0
  120. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +45 -26
  121. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
  122. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +12 -5
  123. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +2 -1
  124. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +6 -5
  125. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +44 -18
  126. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +5 -2
  127. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +10 -11
  128. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +4 -33
  129. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +12 -59
  130. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +297 -267
  131. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  132. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  133. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  134. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  135. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  136. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  137. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  138. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  139. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  140. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  141. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  142. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  143. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  144. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  145. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  146. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  147. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  148. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  149. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  150. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  151. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  152. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  153. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  154. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  155. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  156. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  157. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  158. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  159. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  160. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  161. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  162. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  163. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  164. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  165. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  166. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  167. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  168. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  169. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  170. package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
  171. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  172. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  173. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  174. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  175. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  176. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  177. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +302 -241
  178. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  179. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  180. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  181. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  182. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  183. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  184. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  185. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  186. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  187. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  188. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  189. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  190. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  191. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  192. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +304 -351
  193. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  194. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  195. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  196. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  197. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  198. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  199. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  200. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  201. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  202. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  203. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  204. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  205. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  206. package/pipeline/skills/shared/external/swift-security/SKILL.md +183 -162
  207. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  208. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  209. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +411 -476
  210. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  211. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  212. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  213. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  214. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  215. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  216. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +375 -491
  217. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  218. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  219. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  220. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +397 -457
  221. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  222. package/pipeline/skills/shared/external/swift-testing/SKILL.md +191 -175
  223. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  224. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +81 -84
  225. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  226. package/pipeline/skills/shared/external/swiftdata/SKILL.md +394 -256
  227. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  228. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  229. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  230. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  231. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  232. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  233. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  234. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  235. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  236. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  237. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  238. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  239. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  240. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  241. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  242. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  243. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  244. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  245. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  246. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  247. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  248. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  249. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  250. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  251. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +201 -168
  252. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  253. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +134 -133
  254. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  255. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +111 -138
  256. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  257. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  258. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  259. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  260. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  261. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  262. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  263. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  264. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  265. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  266. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  267. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  268. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  269. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  270. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  271. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  272. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  273. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  274. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  275. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  276. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  277. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  278. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  279. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  280. package/pipeline/skills/shared/external/weatherkit/SKILL.md +160 -315
  281. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  282. package/pipeline/skills/shared/external/widgetkit/SKILL.md +224 -288
  283. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +416 -719
  284. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,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)