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