@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,567 +1,448 @@
1
- # WeatherKit Extended Patterns
1
+ # WeatherKit Patterns
2
2
 
3
- Overflow reference for the `weatherkit` skill. Contains advanced patterns that exceed the main skill file's scope.
3
+ Larger building blocks for the `weatherkit` skill. Its SKILL.md covers setup,
4
+ queries, alerts, availability and the attribution rules; this file puts them
5
+ together.
4
6
 
5
7
  ## Contents
6
8
 
7
- - [WeatherKit SwiftUI Integration](#weatherkit-swiftui-integration)
8
- - [Charts Integration](#charts-integration)
9
- - [Historical Weather Statistics](#historical-weather-statistics)
10
- - [Weather Condition Mapping](#weather-condition-mapping)
11
- - [Caching Strategy](#caching-strategy)
12
- - [Location-Based Weather](#location-based-weather)
9
+ - [WeatherKit SwiftUI integration](#weatherkit-swiftui-integration)
10
+ - [Attribution view](#attribution-view)
11
+ - [Charts integration](#charts-integration)
12
+ - [Historical weather statistics](#historical-weather-statistics)
13
+ - [Weather condition mapping](#weather-condition-mapping)
14
+ - [Caching strategy](#caching-strategy)
15
+ - [Location-based weather](#location-based-weather)
13
16
 
14
- ## WeatherKit SwiftUI Integration
17
+ ## WeatherKit SwiftUI integration
15
18
 
16
- ### Weather Manager with `@Observable`
19
+ ### A weather model
20
+
21
+ One `@Observable` type fetches the datasets a dashboard needs in a single call,
22
+ together with the attribution, and exposes loading and error state.
17
23
 
18
24
  ```swift
19
- import WeatherKit
20
25
  import CoreLocation
26
+ import Observation
27
+ import WeatherKit
21
28
 
22
- @Observable
23
29
  @MainActor
24
- final class WeatherManager {
25
- private let service = WeatherService.shared
26
-
27
- var current: CurrentWeather?
28
- var hourlyForecast: Forecast<HourWeather>?
29
- var dailyForecast: Forecast<DayWeather>?
30
- var alerts: [WeatherAlert]?
31
- var attribution: WeatherAttribution?
32
- var isLoading = false
33
- var error: Error?
34
-
35
- func fetchWeather(for location: CLLocation) async {
36
- isLoading = true
37
- error = nil
38
-
30
+ @Observable
31
+ final class ForecastBoard {
32
+ private(set) var now: CurrentWeather?
33
+ private(set) var hours: Forecast<HourWeather>?
34
+ private(set) var days: Forecast<DayWeather>?
35
+ private(set) var alerts: [WeatherAlert] = []
36
+ private(set) var credit: WeatherAttribution?
37
+ private(set) var busy = false
38
+ private(set) var failure: (any Error)?
39
+
40
+ private let source = WeatherService.shared
41
+
42
+ func load(for place: CLLocation) async {
43
+ busy = true
44
+ defer { busy = false }
39
45
  do {
40
- let (current, hourly, daily, alerts) = try await service.weather(
41
- for: location,
42
- including: .current, .hourly, .daily, .alerts
43
- )
44
- self.current = current
45
- self.hourlyForecast = hourly
46
- self.dailyForecast = daily
47
- self.alerts = alerts
48
- self.attribution = try await service.attribution
46
+ let (current, hourly, daily, warnings) = try await source.weather(
47
+ for: place, including: .current, .hourly, .daily, .alerts)
48
+ now = current
49
+ hours = hourly
50
+ days = daily
51
+ alerts = warnings ?? []
52
+ credit = try await source.attribution
53
+ failure = nil
49
54
  } catch {
50
- self.error = error
55
+ failure = error
51
56
  }
52
-
53
- isLoading = false
54
57
  }
55
58
  }
56
59
  ```
57
60
 
58
- ### Weather Dashboard View
61
+ ### Dashboard view
59
62
 
60
63
  ```swift
61
- import SwiftUI
62
- import WeatherKit
63
-
64
- struct WeatherDashboardView: View {
65
- @Environment(WeatherManager.self) private var manager
66
- let location: CLLocation
64
+ struct ForecastScreen: View {
65
+ @State private var board = ForecastBoard()
66
+ let place: CLLocation
67
67
 
68
68
  var body: some View {
69
- NavigationStack {
70
- ScrollView {
71
- VStack {
72
- if manager.isLoading {
73
- ProgressView("Loading weather...")
74
- } else if let current = manager.current {
75
- currentConditionsCard(current)
76
- }
77
-
78
- if let hourly = manager.hourlyForecast {
79
- hourlyForecastSection(hourly)
80
- }
81
-
82
- if let daily = manager.dailyForecast {
83
- dailyForecastSection(daily)
84
- }
85
-
86
- if let alerts = manager.alerts, !alerts.isEmpty {
87
- alertsSection(alerts)
88
- }
89
-
90
- if let attribution = manager.attribution {
91
- WeatherAttributionView(attribution: attribution)
92
- }
69
+ ScrollView {
70
+ if board.busy && board.now == nil {
71
+ ProgressView().padding(.top, 80)
72
+ } else {
73
+ VStack(alignment: .leading, spacing: 24) {
74
+ if let now = board.now { NowPanel(now: now) }
75
+ if let hours = board.hours { HourStrip(hours: hours) }
76
+ if let days = board.days { DayList(days: days) }
77
+ if !board.alerts.isEmpty { AlertList(alerts: board.alerts) }
78
+ if let credit = board.credit { CreditFooter(credit: credit) }
93
79
  }
94
80
  .padding()
95
81
  }
96
- .navigationTitle("Weather")
97
- .task {
98
- await manager.fetchWeather(for: location)
99
- }
100
- .refreshable {
101
- await manager.fetchWeather(for: location)
102
- }
103
82
  }
83
+ .task { await board.load(for: place) }
84
+ .refreshable { await board.load(for: place) }
104
85
  }
86
+ }
87
+ ```
105
88
 
106
- private func currentConditionsCard(_ current: CurrentWeather) -> some View {
107
- VStack {
108
- Image(systemName: current.symbolName)
109
- .font(.system(size: 60))
110
- .symbolRenderingMode(.multicolor)
89
+ The panels:
90
+
91
+ ```swift
92
+ struct NowPanel: View {
93
+ let now: CurrentWeather
111
94
 
112
- Text(current.temperature.formatted())
113
- .font(.system(size: 48, weight: .thin))
114
-
115
- Text(current.condition.description)
116
- .font(.title3)
117
- .foregroundStyle(.secondary)
118
-
119
- HStack {
120
- Label(
121
- "Humidity \(current.humidity.formatted(.percent))",
122
- systemImage: "humidity"
123
- )
124
- Label(
125
- "Wind \(current.wind.speed.formatted())",
126
- systemImage: "wind"
127
- )
128
- Label(
129
- "UV \(current.uvIndex.value)",
130
- systemImage: "sun.max"
131
- )
95
+ var body: some View {
96
+ HStack(spacing: 16) {
97
+ Image(systemName: now.symbolName)
98
+ .symbolRenderingMode(.multicolor)
99
+ .font(.system(size: 56))
100
+ VStack(alignment: .leading) {
101
+ Text(now.temperature.formatted()).font(.largeTitle.bold())
102
+ Text(now.condition.description)
103
+ Text("Humidity \(now.humidity.formatted(.percent)) Wind \(now.wind.speed.formatted()) UV \(now.uvIndex.value)")
104
+ .font(.footnote)
105
+ .foregroundStyle(.secondary)
132
106
  }
133
- .font(.caption)
134
107
  }
135
- .padding()
136
108
  }
109
+ }
110
+
111
+ struct HourStrip: View {
112
+ let hours: Forecast<HourWeather>
137
113
 
138
- private func hourlyForecastSection(_ forecast: Forecast<HourWeather>) -> some View {
139
- VStack(alignment: .leading) {
140
- Text("Hourly Forecast")
141
- .font(.headline)
142
-
143
- ScrollView(.horizontal, showsIndicators: false) {
144
- HStack {
145
- ForEach(Array(forecast.prefix(12)), id: \.date) { hour in
146
- VStack {
147
- Text(hour.date, format: .dateTime.hour())
148
- .font(.caption)
149
- Image(systemName: hour.symbolName)
150
- .symbolRenderingMode(.multicolor)
151
- Text(hour.temperature.formatted())
152
- .font(.subheadline)
153
- }
114
+ var body: some View {
115
+ ScrollView(.horizontal) {
116
+ LazyHStack(spacing: 18) {
117
+ ForEach(hours.prefix(12), id: \.date) { slot in
118
+ VStack(spacing: 6) {
119
+ Text(slot.date, format: .dateTime.hour())
120
+ Image(systemName: slot.symbolName).symbolRenderingMode(.multicolor)
121
+ Text(slot.temperature.formatted(.measurement(width: .narrow)))
154
122
  }
123
+ .font(.caption)
155
124
  }
156
125
  }
157
126
  }
127
+ .scrollIndicators(.hidden)
158
128
  }
129
+ }
159
130
 
160
- private func dailyForecastSection(_ forecast: Forecast<DayWeather>) -> some View {
161
- VStack(alignment: .leading) {
162
- Text("10-Day Forecast")
163
- .font(.headline)
164
-
165
- ForEach(Array(forecast), id: \.date) { day in
166
- HStack {
167
- Text(day.date, format: .dateTime.weekday(.abbreviated))
168
- .frame(width: 40, alignment: .leading)
169
-
170
- Image(systemName: day.symbolName)
171
- .symbolRenderingMode(.multicolor)
172
- .frame(width: 30)
173
-
174
- Text(day.lowTemperature.formatted())
175
- .foregroundStyle(.secondary)
176
- .frame(width: 50, alignment: .trailing)
177
-
178
- temperatureBar(low: day.lowTemperature, high: day.highTemperature)
131
+ struct DayList: View {
132
+ let days: Forecast<DayWeather>
179
133
 
180
- Text(day.highTemperature.formatted())
181
- .frame(width: 50)
134
+ var body: some View {
135
+ Grid(alignment: .leading, verticalSpacing: 10) {
136
+ ForEach(days.forecast, id: \.date) { entry in
137
+ GridRow {
138
+ Text(entry.date.formatted(.dateTime.weekday(.abbreviated)))
139
+ Image(systemName: entry.symbolName).symbolRenderingMode(.multicolor)
140
+ Text(entry.lowTemperature.formatted(.measurement(width: .narrow)))
141
+ Capsule()
142
+ .fill(LinearGradient(colors: [.teal, .orange], startPoint: .leading, endPoint: .trailing))
143
+ .frame(maxHeight: 5)
144
+ .containerRelativeFrame(.horizontal) { width, _ in width * 0.35 }
145
+ Text(entry.highTemperature.formatted(.measurement(width: .narrow)))
182
146
  }
183
- .font(.subheadline)
184
147
  }
185
148
  }
186
149
  }
150
+ }
187
151
 
188
- private func temperatureBar(
189
- low: Measurement<UnitTemperature>,
190
- high: Measurement<UnitTemperature>
191
- ) -> some View {
192
- Capsule()
193
- .fill(.linearGradient(
194
- colors: [.blue, .orange],
195
- startPoint: .leading,
196
- endPoint: .trailing
197
- ))
198
- .frame(height: 4)
199
- .containerRelativeFrame(.horizontal) { length, _ in
200
- length * 0.3
201
- }
202
- }
152
+ struct AlertList: View {
153
+ let alerts: [WeatherAlert]
203
154
 
204
- private func alertsSection(_ alerts: [WeatherAlert]) -> some View {
205
- VStack(alignment: .leading) {
206
- Text("Weather Alerts")
207
- .font(.headline)
208
-
209
- ForEach(alerts, id: \.detailsURL) { alert in
210
- HStack {
211
- Image(systemName: "exclamationmark.triangle.fill")
212
- .foregroundStyle(alert.severity == .extreme ? .red : .orange)
213
- VStack(alignment: .leading) {
214
- Text(alert.summary)
215
- .font(.subheadline)
216
- Text(alert.region)
217
- .font(.caption)
218
- .foregroundStyle(.secondary)
219
- }
220
- }
221
- .padding()
222
- .background(.yellow.opacity(0.1))
223
- .clipShape(.rect(cornerRadius: 8))
155
+ var body: some View {
156
+ ForEach(alerts, id: \.detailsURL) { warning in
157
+ let isExtreme = warning.severity == .extreme
158
+ Link(destination: warning.detailsURL) {
159
+ Label(warning.summary, systemImage: "exclamationmark.octagon.fill")
160
+ .foregroundStyle(isExtreme ? Color.red : Color.orange)
224
161
  }
225
162
  }
226
163
  }
227
164
  }
228
165
  ```
229
166
 
230
- ### Attribution View
167
+ ## Attribution view
168
+
169
+ A compact footer: a 12-point mark that falls back to the service name while it
170
+ loads, and a link to the data sources.
231
171
 
232
172
  ```swift
233
- struct WeatherAttributionView: View {
234
- let attribution: WeatherAttribution
235
- @Environment(\.colorScheme) private var colorScheme
173
+ struct CreditFooter: View {
174
+ let credit: WeatherAttribution
175
+ @Environment(\.colorScheme) private var scheme
236
176
 
237
177
  var body: some View {
238
- VStack {
239
- AsyncImage(url: markURL) { image in
240
- image
241
- .resizable()
242
- .scaledToFit()
243
- .frame(height: 12)
178
+ HStack(spacing: 8) {
179
+ AsyncImage(url: scheme == .dark ? credit.combinedMarkDarkURL : credit.combinedMarkLightURL) { image in
180
+ image.resizable().scaledToFit()
244
181
  } placeholder: {
245
- Text(attribution.serviceName)
246
- .font(.caption2)
182
+ Text(credit.serviceName)
247
183
  }
184
+ .frame(height: 12)
248
185
 
249
- Link(destination: attribution.legalPageURL) {
250
- Text("Data Sources")
251
- .font(.caption2)
252
- .foregroundStyle(.secondary)
253
- }
186
+ Link("Data Sources", destination: credit.legalPageURL)
254
187
  }
255
- .padding(.vertical)
256
- }
257
-
258
- private var markURL: URL {
259
- colorScheme == .dark
260
- ? attribution.combinedMarkDarkURL
261
- : attribution.combinedMarkLightURL
188
+ .font(.caption2)
189
+ .foregroundStyle(.secondary)
262
190
  }
263
191
  }
264
192
  ```
265
193
 
266
- ## Charts Integration
194
+ ## Charts integration
267
195
 
268
- ### Hourly Temperature Chart
196
+ ### Hourly temperature line
269
197
 
270
198
  ```swift
271
- import SwiftUI
272
199
  import Charts
273
- import WeatherKit
274
200
 
275
- struct HourlyTemperatureChart: View {
276
- let forecast: Forecast<HourWeather>
201
+ struct TemperatureTrend: View {
202
+ let hours: Forecast<HourWeather>
277
203
 
278
204
  var body: some View {
279
- Chart(Array(forecast.prefix(24)), id: \.date) { hour in
280
- LineMark(
281
- x: .value("Hour", hour.date),
282
- y: .value("Temperature", hour.temperature.converted(to: .celsius).value)
283
- )
284
- .interpolationMethod(.catmullRom)
285
- .foregroundStyle(.orange)
286
-
287
- AreaMark(
288
- x: .value("Hour", hour.date),
289
- y: .value("Temperature", hour.temperature.converted(to: .celsius).value)
290
- )
291
- .interpolationMethod(.catmullRom)
292
- .foregroundStyle(.orange.opacity(0.1))
205
+ Chart(hours.prefix(24), id: \.date) { slot in
206
+ let degrees = slot.temperature.converted(to: .celsius).value
207
+ AreaMark(x: .value("Time", slot.date), y: .value("Temperature", degrees))
208
+ .foregroundStyle(.orange.opacity(0.2))
209
+ .interpolationMethod(.catmullRom)
210
+ LineMark(x: .value("Time", slot.date), y: .value("Temperature", degrees))
211
+ .foregroundStyle(.orange)
212
+ .interpolationMethod(.catmullRom)
293
213
  }
294
- .chartYAxisLabel("Temperature (C)")
295
214
  .chartXAxis {
296
215
  AxisMarks(values: .stride(by: .hour, count: 3)) { _ in
297
216
  AxisGridLine()
298
217
  AxisValueLabel(format: .dateTime.hour())
299
218
  }
300
219
  }
301
- .frame(height: 200)
220
+ .frame(minHeight: 160)
302
221
  }
303
222
  }
304
223
  ```
305
224
 
306
- ### Daily Precipitation Chart
225
+ Convert to one unit before plotting; mixing units on one axis gives a
226
+ meaningless line. For display text, keep `formatted()` so the user's unit
227
+ applies.
228
+
229
+ ### Daily precipitation bars
307
230
 
308
231
  ```swift
309
- struct DailyPrecipitationChart: View {
310
- let forecast: Forecast<DayWeather>
232
+ struct RainOutlook: View {
233
+ let days: Forecast<DayWeather>
311
234
 
312
235
  var body: some View {
313
- Chart(Array(forecast), id: \.date) { day in
314
- BarMark(
315
- x: .value("Day", day.date, unit: .day),
316
- y: .value("Chance", day.precipitationChance)
317
- )
318
- .foregroundStyle(.blue.gradient)
236
+ Chart(days.forecast, id: \.date) { entry in
237
+ let odds = entry.precipitationChance
238
+ BarMark(x: .value("Date", entry.date, unit: .day), y: .value("Rain", odds))
239
+ .foregroundStyle(Color.blue.gradient)
319
240
  }
320
241
  .chartYScale(domain: 0...1)
321
242
  .chartYAxis {
322
- AxisMarks(format: .percent)
243
+ AxisMarks { mark in
244
+ AxisValueLabel {
245
+ if let share = mark.as(Double.self) { Text(share, format: .percent) }
246
+ }
247
+ }
323
248
  }
324
249
  .chartXAxis {
325
250
  AxisMarks(values: .stride(by: .day)) { _ in
326
- AxisGridLine()
327
- AxisValueLabel(format: .dateTime.weekday(.abbreviated))
251
+ AxisValueLabel(format: .dateTime.weekday(.narrow), centered: true)
328
252
  }
329
253
  }
330
- .frame(height: 150)
331
254
  }
332
255
  }
333
256
  ```
334
257
 
335
- ## Historical Weather Statistics
258
+ ## Historical weather statistics
336
259
 
337
- WeatherKit provides historical weather data through daily and monthly statistics.
338
-
339
- ### Daily Statistics
260
+ iOS 18 added statistics that describe what is normal for a place, derived from
261
+ long-term records. The methods take variadic queries and return one result per
262
+ query.
340
263
 
341
264
  ```swift
342
- func fetchDailyStats(
343
- for location: CLLocation,
344
- dateRange: DateInterval
345
- ) async throws {
346
- let stats = try await WeatherService.shared.dailyStatistics(
347
- for: location,
348
- forDaysIn: dateRange,
349
- including: [.temperature, .precipitation]
265
+ @available(iOS 18.0, *)
266
+ func typicalJune(of year: Int, at place: CLLocation) async throws -> [(day: Int, low: String, high: String)] {
267
+ let calendar = Calendar(identifier: .gregorian)
268
+ let juneFirst = DateComponents(year: year, month: 6, day: 1)
269
+ let julyFirst = DateComponents(year: year, month: 7, day: 1)
270
+ guard let start = calendar.date(from: juneFirst), let end = calendar.date(from: julyFirst) else { return [] }
271
+
272
+ let service = WeatherService.shared
273
+ let (temperatures, precipitation) = try await service.dailyStatistics(
274
+ for: place,
275
+ forDaysIn: DateInterval(start: start, end: end),
276
+ including: .temperature, .precipitation
350
277
  )
278
+ _ = precipitation.days.map(\.averagePrecipitationProbability)
351
279
 
352
- for dayStat in stats {
353
- print("Date: \(dayStat.date)")
354
- if let temp = dayStat.statistics(for: .temperature) {
355
- print(" Avg temp: \(temp.mean?.formatted() ?? "N/A")")
356
- print(" Min temp: \(temp.minimum?.formatted() ?? "N/A")")
357
- print(" Max temp: \(temp.maximum?.formatted() ?? "N/A")")
358
- }
280
+ return temperatures.days.map { stats in
281
+ (stats.day, stats.averageLowTemperature.formatted(), stats.averageHighTemperature.formatted())
359
282
  }
360
283
  }
361
284
  ```
362
285
 
363
- ### Monthly Statistics
286
+ - `DailyWeatherStatistics<T>` is a collection; its `days` array holds
287
+ `DayTemperatureStatistics` (`day`, `averageLowTemperature`,
288
+ `averageHighTemperature`) or `DayPrecipitationStatistics` (`day`,
289
+ `averagePrecipitationProbability`, `averagePrecipitationAmount`,
290
+ `averageSnowfallAmount`). `day` is a day number, not a `Date`.
291
+ - `monthlyStatistics(for:including:)` returns `MonthlyWeatherStatistics<T>`
292
+ whose `months` array holds `MonthTemperatureStatistics` (`month`,
293
+ `averageLowTemperature`, `averageHighTemperature`) or the precipitation
294
+ equivalent.
295
+ - `hourlyStatistics(for:forHoursIn:including:)` and
296
+ `dailySummary(for:forDaysIn:including:)` follow the same shape for hourly
297
+ normals and for observed daily totals.
364
298
 
365
299
  ```swift
366
- func fetchMonthlyStats(for location: CLLocation) async throws {
367
- let stats = try await WeatherService.shared.monthlyStatistics(
368
- for: location,
369
- including: [.temperature, .precipitation]
370
- )
371
-
372
- for monthStat in stats {
373
- print("Month: \(monthStat.month)")
374
- }
300
+ @available(iOS 18.0, *)
301
+ func monthlyNormals(at place: CLLocation) async throws -> [Int: String] {
302
+ let service = WeatherService.shared
303
+ let normals = try await service.monthlyStatistics(for: place, including: .temperature)
304
+ return Dictionary(uniqueKeysWithValues: normals.months.map { ($0.month, $0.averageHighTemperature.formatted()) })
375
305
  }
376
306
  ```
377
307
 
378
- ## Weather Condition Mapping
308
+ ## Weather condition mapping
379
309
 
380
- ### Mapping Conditions to Colors
310
+ `WeatherCondition` has a readable `description`, but apps often want their own
311
+ icon or grouping. Switch over every case and keep `@unknown default` for cases
312
+ added later.
381
313
 
382
314
  ```swift
383
- extension WeatherCondition {
384
- var themeColor: Color {
385
- switch self {
386
- case .clear, .mostlyClear:
387
- return .yellow
388
- case .partlyCloudy, .mostlyCloudy, .cloudy:
389
- return .gray
390
- case .rain, .heavyRain, .drizzle:
391
- return .blue
392
- case .snow, .heavySnow, .flurries, .sleet, .freezingRain,
393
- .freezingDrizzle, .wintryMix, .blizzard:
394
- return .cyan
395
- case .thunderstorms, .strongStorms, .tropicalStorm, .hurricane:
396
- return .purple
397
- case .foggy, .haze, .smoky:
398
- return .gray.opacity(0.6)
399
- case .breezy, .windy:
400
- return .teal
401
- case .hot:
402
- return .red
403
- case .frigid, .blowingDust:
404
- return .indigo
405
- @unknown default:
406
- return .primary
407
- }
315
+ func skyGroup(_ condition: WeatherCondition) -> String {
316
+ switch condition {
317
+ case .clear, .mostlyClear, .hot:
318
+ return "sunny"
319
+ case .partlyCloudy, .mostlyCloudy, .cloudy:
320
+ return "cloudy"
321
+ case .drizzle, .rain, .heavyRain, .sunShowers:
322
+ return "rain"
323
+ case .snow, .heavySnow, .flurries, .sunFlurries, .blowingSnow, .blizzard:
324
+ return "snow"
325
+ case .sleet, .freezingRain, .freezingDrizzle, .wintryMix, .hail, .frigid:
326
+ return "ice"
327
+ case .thunderstorms, .isolatedThunderstorms, .scatteredThunderstorms,
328
+ .strongStorms, .tropicalStorm, .hurricane:
329
+ return "storm"
330
+ case .foggy, .haze, .smoky, .blowingDust:
331
+ return "low visibility"
332
+ case .breezy, .windy:
333
+ return "wind"
334
+ @unknown default:
335
+ return "unknown"
408
336
  }
409
337
  }
410
338
  ```
411
339
 
412
- ### Mapping Severity to Priority
340
+ `WeatherSeverity` orders alerts. Rank it explicitly:
413
341
 
414
342
  ```swift
415
- extension WeatherSeverity {
416
- var displayPriority: Int {
417
- switch self {
418
- case .extreme:
419
- return 4
420
- case .severe:
421
- return 3
422
- case .moderate:
423
- return 2
424
- case .minor:
425
- return 1
426
- case .unknown:
427
- return 0
428
- @unknown default:
429
- return 0
430
- }
343
+ func rank(_ severity: WeatherSeverity) -> Int {
344
+ switch severity {
345
+ case .extreme: 4
346
+ case .severe: 3
347
+ case .moderate: 2
348
+ case .minor: 1
349
+ case .unknown: 0
350
+ @unknown default: 0
431
351
  }
432
352
  }
433
353
  ```
434
354
 
435
- ## Caching Strategy
355
+ ## Caching strategy
436
356
 
437
- ### Actor-Based Weather Cache
357
+ An actor keeps recent results per rounded coordinate and throws them away once
358
+ they are older than the staleness window.
438
359
 
439
360
  ```swift
440
- actor WeatherCache {
441
- struct CacheEntry {
442
- let weather: CurrentWeather
443
- let hourly: Forecast<HourWeather>
444
- let daily: Forecast<DayWeather>
445
- let fetchDate: Date
361
+ actor ForecastCache {
362
+ struct Snapshot {
363
+ let now: CurrentWeather
364
+ let hours: Forecast<HourWeather>
365
+ let days: Forecast<DayWeather>
366
+ let fetchedAt: Date
446
367
  }
447
368
 
448
- private var cache: [String: CacheEntry] = [:]
449
- private let staleness: TimeInterval
369
+ private var store: [String: Snapshot] = [:]
370
+ private let maxAge: TimeInterval
371
+
372
+ init(maxAge: TimeInterval = 600) { self.maxAge = maxAge }
450
373
 
451
- init(staleness: TimeInterval = 600) { // 10 minutes default
452
- self.staleness = staleness
374
+ // Two decimal places is roughly a one-kilometre grid.
375
+ private func key(for place: CLLocation) -> String {
376
+ let spot = place.coordinate
377
+ return String(format: "%.2f|%.2f", spot.latitude, spot.longitude)
453
378
  }
454
379
 
455
- func get(for key: String) -> CacheEntry? {
456
- guard let entry = cache[key],
457
- Date.now.timeIntervalSince(entry.fetchDate) < staleness else {
458
- cache[key] = nil
380
+ func snapshot(for place: CLLocation) -> Snapshot? {
381
+ let slot = key(for: place)
382
+ guard let hit = store[slot] else { return nil }
383
+ if Date.now.timeIntervalSince(hit.fetchedAt) > maxAge {
384
+ store[slot] = nil
459
385
  return nil
460
386
  }
461
- return entry
387
+ return hit
462
388
  }
463
389
 
464
- func set(_ entry: CacheEntry, for key: String) {
465
- cache[key] = entry
466
- }
467
-
468
- /// Generate a cache key from a location (rounded to ~1km precision)
469
- static func key(for location: CLLocation) -> String {
470
- let lat = (location.coordinate.latitude * 100).rounded() / 100
471
- let lon = (location.coordinate.longitude * 100).rounded() / 100
472
- return "\(lat),\(lon)"
390
+ func keep(_ snapshot: Snapshot, for place: CLLocation) {
391
+ store[key(for: place)] = snapshot
473
392
  }
474
393
  }
475
- ```
476
394
 
477
- ### Using the Cache
478
-
479
- ```swift
480
- @Observable
481
- @MainActor
482
- final class CachedWeatherManager {
483
- private let service = WeatherService.shared
484
- private let cache = WeatherCache()
485
-
486
- var current: CurrentWeather?
487
-
488
- func fetchWeather(for location: CLLocation) async throws {
489
- let key = WeatherCache.key(for: location)
490
-
491
- if let cached = await cache.get(for: key) {
492
- current = cached.weather
493
- return
494
- }
495
-
496
- let (current, hourly, daily) = try await service.weather(
497
- for: location,
498
- including: .current, .hourly, .daily
499
- )
500
-
501
- let entry = WeatherCache.CacheEntry(
502
- weather: current,
503
- hourly: hourly,
504
- daily: daily,
505
- fetchDate: .now
506
- )
507
- await cache.set(entry, for: key)
508
- self.current = current
509
- }
395
+ func freshForecast(at place: CLLocation, cache: ForecastCache) async throws -> ForecastCache.Snapshot {
396
+ if let cached = await cache.snapshot(for: place) { return cached }
397
+ let service = WeatherService.shared
398
+ let (now, hours, days) = try await service.weather(for: place, including: .current, .hourly, .daily)
399
+ let snapshot = ForecastCache.Snapshot(now: now, hours: hours, days: days, fetchedAt: .now)
400
+ await cache.keep(snapshot, for: place)
401
+ return snapshot
510
402
  }
511
403
  ```
512
404
 
513
- ## Location-Based Weather
405
+ ## Location-based weather
514
406
 
515
- ### Combining CoreLocation with WeatherKit
407
+ A small location provider: ask for when-in-use permission, request one fix at
408
+ kilometre accuracy (plenty for weather), then fetch current conditions.
516
409
 
517
410
  ```swift
518
- import CoreLocation
519
- import WeatherKit
520
-
521
- @Observable
522
411
  @MainActor
523
- final class LocationWeatherManager: NSObject, CLLocationManagerDelegate {
524
- private let locationManager = CLLocationManager()
525
- private let weatherService = WeatherService.shared
526
-
527
- var current: CurrentWeather?
528
- var locationError: Error?
412
+ @Observable
413
+ final class LocalConditions: NSObject, CLLocationManagerDelegate {
414
+ private(set) var now: CurrentWeather?
415
+ private(set) var problem: String?
416
+ private let locator = CLLocationManager()
529
417
 
530
418
  override init() {
531
419
  super.init()
532
- locationManager.delegate = self
533
- locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
420
+ locator.delegate = self
421
+ locator.desiredAccuracy = kCLLocationAccuracyKilometer
534
422
  }
535
423
 
536
- func requestWeather() {
537
- locationManager.requestWhenInUseAuthorization()
538
- locationManager.requestLocation()
424
+ func start() {
425
+ locator.requestWhenInUseAuthorization()
426
+ locator.requestLocation()
539
427
  }
540
428
 
541
- nonisolated func locationManager(
542
- _ manager: CLLocationManager,
543
- didUpdateLocations locations: [CLLocation]
544
- ) {
545
- guard let location = locations.last else { return }
546
- Task { @MainActor in
547
- do {
548
- current = try await weatherService.weather(
549
- for: location,
550
- including: .current
551
- )
552
- } catch {
553
- locationError = error
554
- }
555
- }
429
+ nonisolated func locationManager(_ source: CLLocationManager, didUpdateLocations fixes: [CLLocation]) {
430
+ guard let latest = fixes.last else { return }
431
+ Task { @MainActor in await refresh(at: latest) }
556
432
  }
557
433
 
558
- nonisolated func locationManager(
559
- _ manager: CLLocationManager,
560
- didFailWithError error: Error
561
- ) {
562
- Task { @MainActor in
563
- locationError = error
434
+ private func refresh(at spot: CLLocation) async {
435
+ do {
436
+ let service = WeatherService.shared
437
+ now = try await service.weather(for: spot, including: .current)
438
+ } catch {
439
+ problem = error.localizedDescription
564
440
  }
565
441
  }
442
+
443
+ nonisolated func locationManager(_ source: CLLocationManager, didFailWithError error: any Error) {
444
+ let message = error.localizedDescription
445
+ Task { @MainActor in problem = message }
446
+ }
566
447
  }
567
448
  ```