@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
@@ -3,41 +3,42 @@
3
3
  "evals": [
4
4
  {
5
5
  "id": 1,
6
- "prompt": "Audit this proposed MusicKit setup for an iOS 26 app: the team adds a com.apple.developer.musickit entitlement in Xcode, skips NSAppleMusicUsageDescription, calls MusicAuthorization.request() during launch, and says authorization will crash when the entitlement is missing. Write a concise review with corrected setup and authorization guidance.",
7
- "expected_output": "A review that replaces the invented entitlement guidance with MusicKit App Service setup for an explicit bundle ID, requires NSAppleMusicUsageDescription, and explains authorization behavior without overstating crashes.",
6
+ "prompt": "We are adding Apple Music to a running-coach app. What do we have to configure in the portal and in the project before MusicKit works, how should we ask for permission, and do we need anything extra so music keeps going when the screen locks? A teammate says we also need the com.apple.developer.musickit entitlement or authorization will crash.",
7
+ "expected_output": "A setup answer that names the App Service and usage string, explains request() versus currentStatus, treats background audio as conditional, and rejects the entitlement claim.",
8
8
  "files": [],
9
9
  "assertions": [
10
- "Recommends enabling the MusicKit App Service for the app's explicit bundle ID so automatic developer token generation is configured.",
11
- "Requires NSAppleMusicUsageDescription before accessing the user's music data or media library.",
12
- "Explains that MusicAuthorization.request() presents the system consent dialog when necessary and currentStatus can be checked without prompting.",
13
- "Does not claim that com.apple.developer.musickit is the required iOS app entitlement or that a missing entitlement causes authorization to crash.",
14
- "Mentions UIBackgroundModes audio only when the app needs background playback."
10
+ "Says to enable the MusicKit App Service on the app's explicit App ID so developer tokens are generated automatically",
11
+ "Requires NSAppleMusicUsageDescription in Info.plist",
12
+ "Explains that MusicAuthorization.request() prompts only when needed and MusicAuthorization.currentStatus reads the state without prompting",
13
+ "Does not claim a com.apple.developer.musickit entitlement is required on iOS or that its absence crashes authorization",
14
+ "Adds audio to UIBackgroundModes only because playback must continue in the background"
15
15
  ]
16
16
  },
17
17
  {
18
18
  "id": 2,
19
- "prompt": "Implement focused SwiftUI snippets for a MusicKit search-and-play flow: search Apple Music songs, play a selected catalog song with ApplicationMusicPlayer, and offer Apple Music subscription signup when playback is unavailable.",
20
- "expected_output": "Swift snippets that separate catalog search from subscription playback, gate playback on MusicSubscription.current.canPlayCatalogContent, use ApplicationMusicPlayer, and present the Music subscription offer only when the user can become a subscriber.",
19
+ "prompt": "Build the flow where a user searches the Apple Music catalog, taps a song and it plays inside our app. Users without Apple Music should be invited to subscribe instead of seeing a broken play button.",
20
+ "expected_output": "Catalog search, a subscription gate before playback, app-scoped playback, and a correctly gated subscription offer.",
21
21
  "files": [],
22
22
  "assertions": [
23
- "Uses MusicCatalogSearchRequest for catalog search and does not require subscription just to fetch catalog search results.",
24
- "Checks MusicSubscription.current.canPlayCatalogContent before attempting catalog playback.",
25
- "Uses ApplicationMusicPlayer.shared with queue assignment and play() for app-scoped playback.",
26
- "Checks canBecomeSubscriber before presenting the Apple Music subscription offer.",
27
- "Uses musicSubscriptionOffer(isPresented:options:onLoadCompletion:) or accurately notes the options and load-completion parameters for the offer sheet."
23
+ "Searches with MusicCatalogSearchRequest and does not require a subscription for searching",
24
+ "Gates playback on MusicSubscription.current canPlayCatalogContent",
25
+ "Plays through ApplicationMusicPlayer.shared by setting the queue and calling play()",
26
+ "Checks canBecomeSubscriber before showing the offer",
27
+ "Presents the offer with musicSubscriptionOffer(isPresented:options:onLoadCompletion:)"
28
28
  ]
29
29
  },
30
30
  {
31
31
  "id": 3,
32
- "prompt": "Review a MusicKit implementation that fetches top songs with MusicCatalogChartsRequest(kinds: [.mostPlayed], types: [Song.self]), adds songs and playlists to the user's library without checking cloud library state, and manually updates MPNowPlayingInfoCenter for Apple Music playback. Explain what should change.",
33
- "expected_output": "A review that uses the current chart request initializer shape, gates library writes on authorization and iCloud Music Library availability, and keeps Now Playing guidance scoped to custom audio versus MusicKit playback.",
32
+ "prompt": "Add a Top Charts screen, a button that saves a song to the user's library, and make sure the Lock Screen shows what is playing. Should we also build a CarPlay scene and an AVKit player while we are at it?",
33
+ "expected_output": "Charts with an explicit genre argument, guarded library writes, correct Now Playing ownership, and no unrelated AVKit or CarPlay work.",
34
34
  "files": [],
35
35
  "assertions": [
36
- "Uses or recommends MusicCatalogChartsRequest(genre:kinds:types:) with an explicit genre argument such as nil.",
37
- "Checks MusicAuthorization.currentStatus or requests authorization before library mutations.",
38
- "Checks MusicSubscription.current.hasCloudLibraryEnabled before adding songs, albums, or playlist entries to the user's library.",
39
- "Explains that ApplicationMusicPlayer handles Now Playing metadata automatically for Apple Music content.",
40
- "Keeps MediaPlayer guidance limited to custom audio or remote command handling and does not broaden into unrelated AVKit or CarPlay implementation advice."
36
+ "Uses MusicCatalogChartsRequest(genre:kinds:types:) and passes genre explicitly, for example nil",
37
+ "Confirms authorization before library mutations",
38
+ "Checks hasCloudLibraryEnabled before adding to the library",
39
+ "States that ApplicationMusicPlayer publishes Now Playing automatically for Apple Music content",
40
+ "Limits MediaPlayer guidance to custom audio metadata and remote commands",
41
+ "Does not add AVKit player or CarPlay scene implementation"
41
42
  ]
42
43
  }
43
44
  ]
@@ -1,383 +1,294 @@
1
- # MusicKit + MediaPlayer Extended Patterns
1
+ # MusicKit Patterns
2
2
 
3
- Overflow reference for the `musickit` skill. Contains advanced patterns that exceed the main skill file's scope.
3
+ Longer examples for the `musickit-audio` skill. Its SKILL.md covers setup,
4
+ authorization, subscription checks and the basic player; this file builds on
5
+ that.
4
6
 
5
- ## Contents
7
+ ## SwiftUI integration
6
8
 
7
- - [MusicKit SwiftUI Integration](#musickit-swiftui-integration)
8
- - [Genre and Chart Browsing](#genre-and-chart-browsing)
9
- - [Library Management](#library-management)
10
- - [Playlist Access](#playlist-access)
11
- - [Now Playing Session](#now-playing-session)
12
- - [Background Audio Configuration](#background-audio-configuration)
9
+ ### A shared player model
13
10
 
14
- ## MusicKit SwiftUI Integration
15
-
16
- ### Music Player Manager with `@Observable`
11
+ One `@Observable` object on the main actor owns access and subscription state
12
+ and drives the player. Views read it from the environment.
17
13
 
18
14
  ```swift
19
15
  import MusicKit
20
- import MediaPlayer
16
+ import Observation
21
17
 
22
- @Observable
23
18
  @MainActor
24
- final class MusicPlayerManager {
25
- let player = ApplicationMusicPlayer.shared
26
-
27
- var currentSong: Song?
28
- var isPlaying = false
29
- var playbackTime: TimeInterval = 0
30
- var queue: [Song] = []
31
- var hasSubscription = false
32
-
33
- func setup() async {
34
- // Check authorization
35
- let status = await MusicAuthorization.request()
36
- guard status == .authorized else { return }
37
-
38
- // Check subscription
39
- if let subscription = try? await MusicSubscription.current {
40
- hasSubscription = subscription.canPlayCatalogContent
41
- }
19
+ @Observable
20
+ final class ListeningSession {
21
+ private(set) var accessGranted = false
22
+ private(set) var catalogPlayable = false
23
+ private var membershipWatcher: Task<Void, Never>?
24
+
25
+ func prepare() async {
26
+ accessGranted = await MusicAuthorization.request() == .authorized
27
+ guard accessGranted else { return }
28
+
29
+ catalogPlayable = (try? await MusicSubscription.current.canPlayCatalogContent) ?? false
42
30
 
43
- // Observe subscription changes
44
- Task {
45
- for await subscription in MusicSubscription.subscriptionUpdates {
46
- hasSubscription = subscription.canPlayCatalogContent
31
+ membershipWatcher = Task { [weak self] in
32
+ let updates = MusicSubscription.subscriptionUpdates
33
+ for await membership in updates {
34
+ self?.catalogPlayable = membership.canPlayCatalogContent
47
35
  }
48
36
  }
49
37
  }
50
38
 
51
- func play(_ song: Song) async throws {
52
- guard hasSubscription else { return }
53
- player.queue = [song]
39
+ func play(_ track: Song) async throws {
40
+ guard catalogPlayable else { return }
41
+ let player = ApplicationMusicPlayer.shared
42
+ player.queue = [track]
54
43
  try await player.play()
55
- currentSong = song
56
- isPlaying = true
57
44
  }
58
45
 
59
- func togglePlayPause() {
60
- if player.state.playbackStatus == .playing {
61
- player.pause()
62
- isPlaying = false
63
- } else {
64
- Task {
65
- try? await player.play()
66
- isPlaying = true
67
- }
68
- }
46
+ func playOrPause() async throws {
47
+ let player = ApplicationMusicPlayer.shared
48
+ let isRunning = player.state.playbackStatus == .playing
49
+ if isRunning { player.pause() } else { try await player.play() }
69
50
  }
70
51
 
71
- func skip() async {
72
- try? await player.skipToNextEntry()
52
+ func next() async throws {
53
+ try await ApplicationMusicPlayer.shared.skipToNextEntry()
73
54
  }
74
55
  }
75
56
  ```
76
57
 
77
- ### SwiftUI Player View
58
+ Install it once near the root with `.environment(session)` and call
59
+ `prepare()` from a `.task`. The model does not keep the player in a property:
60
+ `ApplicationMusicPlayer` is not `Sendable`, and Swift 6 refuses an async call
61
+ such as `play()` on one reached through main-actor state. Each method reads
62
+ `ApplicationMusicPlayer.shared` into a local instead.
78
63
 
79
- ```swift
80
- import SwiftUI
81
- import MusicKit
64
+ ### Mini player bar
65
+
66
+ `ArtworkImage` renders MusicKit artwork at a requested size.
82
67
 
83
- struct MiniPlayerView: View {
84
- @Environment(MusicPlayerManager.self) private var manager
68
+ ```swift
69
+ struct NowPlayingBar: View {
70
+ @Environment(ListeningSession.self) private var session
71
+ let track: Song
85
72
 
86
73
  var body: some View {
87
- HStack {
88
- if let song = manager.currentSong {
89
- if let artwork = song.artwork {
90
- ArtworkImage(artwork, width: 44, height: 44)
91
- .clipShape(.rect(cornerRadius: 6))
92
- }
93
-
94
- VStack(alignment: .leading) {
95
- Text(song.title)
96
- .font(.subheadline)
97
- .lineLimit(1)
98
- Text(song.artistName)
99
- .font(.caption)
100
- .foregroundStyle(.secondary)
101
- }
102
-
103
- Spacer()
104
-
105
- Button {
106
- manager.togglePlayPause()
107
- } label: {
108
- Image(systemName: manager.isPlaying ? "pause.fill" : "play.fill")
109
- .font(.title2)
110
- }
111
-
112
- Button {
113
- Task { await manager.skip() }
114
- } label: {
115
- Image(systemName: "forward.fill")
116
- }
74
+ HStack(alignment: .center, spacing: 10) {
75
+ if let cover = track.artwork {
76
+ ArtworkImage(cover, width: 48, height: 48)
77
+ .clipShape(.rect(cornerRadius: 8))
117
78
  }
79
+ VStack(alignment: .leading, spacing: 2) {
80
+ Text(track.title).font(.subheadline.bold())
81
+ Text(track.artistName).foregroundStyle(.secondary).font(.caption)
82
+ }
83
+ Spacer(minLength: 8)
84
+ Button("Play or pause", systemImage: "playpause.fill") {
85
+ Task { try? await session.playOrPause() }
86
+ }
87
+ .labelStyle(.iconOnly)
118
88
  }
119
89
  .padding(.horizontal)
120
- .frame(height: 60)
121
90
  }
122
91
  }
123
92
  ```
124
93
 
125
- ### Search View
94
+ ### Search screen
95
+
96
+ Search as the text changes and clear the list when the field is empty.
126
97
 
127
98
  ```swift
128
- struct MusicSearchView: View {
129
- @State private var searchText = ""
130
- @State private var results: MusicItemCollection<Song> = []
131
- @Environment(MusicPlayerManager.self) private var manager
99
+ struct CatalogFinder: View {
100
+ @State private var phrase = ""
101
+ @State private var hits: MusicItemCollection<Song> = []
132
102
 
133
103
  var body: some View {
134
- NavigationStack {
135
- List(results) { song in
136
- Button {
137
- Task { try? await manager.play(song) }
138
- } label: {
139
- HStack {
140
- if let artwork = song.artwork {
141
- ArtworkImage(artwork, width: 50, height: 50)
142
- .clipShape(.rect(cornerRadius: 4))
143
- }
144
- VStack(alignment: .leading) {
145
- Text(song.title)
146
- Text(song.artistName)
147
- .font(.caption)
148
- .foregroundStyle(.secondary)
149
- }
150
- }
151
- }
152
- }
153
- .searchable(text: $searchText, prompt: "Search Apple Music")
154
- .onChange(of: searchText) {
155
- Task { await search() }
156
- }
157
- .navigationTitle("Search")
104
+ List(hits) { song in
105
+ LabeledContent(song.title, value: song.artistName)
106
+ }
107
+ .searchable(text: $phrase, prompt: "Artists, songs, albums")
108
+ .onChange(of: phrase) { _, newPhrase in
109
+ Task { await refresh(for: newPhrase) }
158
110
  }
159
111
  }
160
112
 
161
- private func search() async {
162
- guard !searchText.isEmpty else {
163
- results = []
164
- return
165
- }
166
- var request = MusicCatalogSearchRequest(term: searchText, types: [Song.self])
167
- request.limit = 25
168
- if let response = try? await request.response() {
169
- results = response.songs
170
- }
113
+ private func refresh(for text: String) async {
114
+ guard !text.isEmpty else { hits = []; return }
115
+ var search = MusicCatalogSearchRequest(term: text, types: [Song.self])
116
+ search.limit = 25
117
+ hits = (try? await search.response().songs) ?? []
171
118
  }
172
119
  }
173
120
  ```
174
121
 
175
- ## Genre and Chart Browsing
122
+ ## Genre and chart browsing
176
123
 
177
- ### Fetching Top Charts
124
+ Pass `genre` explicitly, even when you want all genres, so the intent is clear
125
+ at the call site.
178
126
 
179
127
  ```swift
180
- func fetchTopSongs() async throws -> MusicItemCollection<Song> {
181
- var request = MusicCatalogChartsRequest(
182
- genre: nil,
183
- kinds: [.mostPlayed],
184
- types: [Song.self]
185
- )
186
- request.limit = 50
187
- let response = try await request.response()
188
- return response.songCharts.first?.items ?? []
128
+ func topTracks() async throws -> MusicItemCollection<Song> {
129
+ let wanted: [MusicCatalogChartKind] = [.mostPlayed]
130
+ var charts = MusicCatalogChartsRequest(genre: nil, kinds: wanted, types: [Song.self])
131
+ charts.limit = 50
132
+ let result = try await charts.response()
133
+ guard let chart = result.songCharts.first else { return [] }
134
+ return chart.items
189
135
  }
190
136
  ```
191
137
 
192
- ### Fetching by Genre
138
+ A simple way to browse by genre is to search with the genre's name:
193
139
 
194
140
  ```swift
195
- func fetchSongsByGenre(_ genre: Genre) async throws -> MusicItemCollection<Song> {
196
- var request = MusicCatalogSearchRequest(term: genre.name, types: [Song.self])
197
- request.limit = 25
198
- let response = try await request.response()
199
- return response.songs
141
+ func tracks(tagged style: Genre) async throws -> MusicItemCollection<Song> {
142
+ let label = style.name
143
+ let search = MusicCatalogSearchRequest(term: label, types: [Song.self])
144
+ return try await search.response().songs
200
145
  }
201
146
  ```
202
147
 
203
- ## Library Management
204
-
205
- Library writes require MusicKit authorization and an enabled iCloud Music Library.
206
- Check `MusicSubscription.current.hasCloudLibraryEnabled` before adding items or
207
- modifying playlists.
148
+ ## Library management
208
149
 
209
- ### Adding to Library
150
+ Writing to the user's library needs two things: authorization, and iCloud Music
151
+ Library turned on for the account.
210
152
 
211
153
  ```swift
212
- func addToLibrary(_ song: Song) async throws {
213
- guard MusicAuthorization.currentStatus == .authorized else { return }
214
- let subscription = try await MusicSubscription.current
215
- guard subscription.hasCloudLibraryEnabled else { return }
216
-
217
- try await MusicLibrary.shared.add(song)
154
+ func canWriteLibrary() async -> Bool {
155
+ if MusicAuthorization.currentStatus != .authorized { return false }
156
+ let membership = try? await MusicSubscription.current
157
+ return membership?.hasCloudLibraryEnabled ?? false
218
158
  }
219
159
 
220
- func addAlbumToLibrary(_ album: Album) async throws {
221
- guard MusicAuthorization.currentStatus == .authorized else { return }
222
- let subscription = try await MusicSubscription.current
223
- guard subscription.hasCloudLibraryEnabled else { return }
160
+ func save(_ track: Song) async throws {
161
+ let library = MusicLibrary.shared
162
+ if await canWriteLibrary() { try await library.add(track) }
163
+ }
224
164
 
225
- try await MusicLibrary.shared.add(album)
165
+ func save(_ record: Album) async throws {
166
+ let library = MusicLibrary.shared
167
+ if await canWriteLibrary() { try await library.add(record) }
226
168
  }
227
169
  ```
228
170
 
229
- ### Fetching Library Content
171
+ Reading from the library uses `MusicLibraryRequest`:
230
172
 
231
173
  ```swift
232
- func fetchLibrarySongs() async throws -> MusicItemCollection<Song> {
233
- var request = MusicLibraryRequest<Song>()
234
- request.sort(by: \.lastPlayedDate, ascending: false)
235
- request.limit = 50
236
- let response = try await request.response()
237
- return response.items
174
+ func lastListened() async throws -> MusicItemCollection<Song> {
175
+ var query = MusicLibraryRequest<Song>()
176
+ query.sort(by: \.lastPlayedDate, ascending: false)
177
+ query.limit = 20
178
+ return try await query.response().items
238
179
  }
239
180
  ```
240
181
 
241
- ## Playlist Access
182
+ ## Playlist access
242
183
 
243
- ### Fetching User Playlists
184
+ A library request for playlists sorts only by `lastPlayedDate`,
185
+ `libraryAddedDate` or `name`; `Playlist.lastModifiedDate` exists but is not a
186
+ sort key.
244
187
 
245
188
  ```swift
246
- func fetchPlaylists() async throws -> MusicItemCollection<Playlist> {
247
- var request = MusicLibraryRequest<Playlist>()
248
- request.sort(by: \.lastModifiedDate, ascending: false)
249
- let response = try await request.response()
250
- return response.items
189
+ func recentPlaylists() async throws -> MusicItemCollection<Playlist> {
190
+ var query = MusicLibraryRequest<Playlist>()
191
+ query.sort(by: \.lastPlayedDate, ascending: false)
192
+ return try await query.response().items
251
193
  }
252
- ```
253
-
254
- ### Adding Tracks to a Playlist
255
194
 
256
- ```swift
257
- func addToPlaylist(_ playlist: Playlist, songs: [Song]) async throws {
258
- guard MusicAuthorization.currentStatus == .authorized else { return }
259
- let subscription = try await MusicSubscription.current
260
- guard subscription.hasCloudLibraryEnabled else { return }
261
-
262
- try await MusicLibrary.shared.add(songs, to: playlist)
195
+ func append(_ tracks: [Song], to list: Playlist) async throws {
196
+ guard await canWriteLibrary() else { return }
197
+ for track in tracks {
198
+ _ = try await MusicLibrary.shared.add(track, to: list)
199
+ }
263
200
  }
264
201
  ```
265
202
 
266
- ## Now Playing Session
203
+ ## Now Playing session
267
204
 
268
- Use `MPNowPlayingSession` when your app manages multiple simultaneous audio
269
- sessions (e.g., picture-in-picture video plus background music).
205
+ When an app runs more than one audio source at once (for example a
206
+ Picture in Picture video next to a music stream), give each its own
207
+ `MPNowPlayingSession`. Each session carries its own command center and info
208
+ center, and the app says which one should be the system's Now Playing entry.
270
209
 
271
210
  ```swift
272
- import MediaPlayer
273
211
  import AVFoundation
212
+ import MediaPlayer
274
213
 
275
- func createNowPlayingSession(for player: AVPlayer) -> MPNowPlayingSession {
276
- let session = MPNowPlayingSession(players: [player])
277
-
278
- // Session-scoped remote command center
279
- session.remoteCommandCenter.playCommand.addTarget { _ in
280
- player.play()
281
- return .success
282
- }
283
-
284
- session.remoteCommandCenter.pauseCommand.addTarget { _ in
285
- player.pause()
214
+ @MainActor
215
+ func makeVideoSession(for videoPlayer: AVPlayer) -> MPNowPlayingSession {
216
+ let clipSession = MPNowPlayingSession(players: [videoPlayer])
217
+ let controls = clipSession.remoteCommandCenter
218
+ controls.playCommand.addTarget { event in
219
+ videoPlayer.play()
286
220
  return .success
287
221
  }
288
-
289
- // Session-scoped now playing info
290
- session.nowPlayingInfoCenter.nowPlayingInfo = [
291
- MPMediaItemPropertyTitle: "Track Title",
292
- MPMediaItemPropertyArtist: "Artist Name"
222
+ clipSession.nowPlayingInfoCenter.nowPlayingInfo = [
223
+ MPMediaItemPropertyTitle: "Match highlights"
293
224
  ]
294
-
295
- // Activate this session to become the "now playing" app
296
- session.becomeActiveIfPossible { success in
297
- print("Now playing session active: \(success)")
225
+ clipSession.becomeActiveIfPossible { didBecomeActive in
226
+ // didBecomeActive is false if another app currently owns Now Playing.
298
227
  }
299
-
300
- return session
228
+ return clipSession
301
229
  }
302
230
  ```
303
231
 
304
- ## Background Audio Configuration
232
+ ## Background audio configuration
305
233
 
306
- ### Audio Session Setup
307
-
308
- Configure the audio session before starting playback to enable background audio.
234
+ Set up the audio session before playback starts:
309
235
 
310
236
  ```swift
311
- import AVFoundation
312
-
313
237
  func configureAudioSession() throws {
314
- let session = AVAudioSession.sharedInstance()
315
- try session.setCategory(.playback, mode: .default)
316
- try session.setActive(true)
238
+ let audio = AVAudioSession.sharedInstance()
239
+ try audio.setCategory(.playback, mode: .default)
240
+ try audio.setActive(true)
317
241
  }
318
242
  ```
319
243
 
320
- ### Info.plist Background Mode
244
+ Then list `audio` under the `UIBackgroundModes` key in Info.plist (in Xcode:
245
+ Signing and Capabilities, Background Modes, "Audio, AirPlay, and Picture in
246
+ Picture").
321
247
 
322
- Add `audio` to `UIBackgroundModes` in Info.plist:
248
+ ### Interruptions
323
249
 
324
- ```xml
325
- <key>UIBackgroundModes</key>
326
- <array>
327
- <string>audio</string>
328
- </array>
329
- ```
330
-
331
- ### Handling Interruptions
250
+ Calls, alarms and Siri interrupt playback. Pause when it starts; resume only if
251
+ the system says so when it ends. The async notification sequence keeps the
252
+ handler on the main actor.
332
253
 
333
254
  ```swift
334
- func observeInterruptions() {
335
- NotificationCenter.default.addObserver(
336
- forName: AVAudioSession.interruptionNotification,
337
- object: AVAudioSession.sharedInstance(),
338
- queue: .main
339
- ) { notification in
340
- guard let typeValue = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
341
- let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
342
- return
343
- }
344
-
345
- switch type {
346
- case .began:
347
- // Pause UI, save state
348
- break
349
- case .ended:
350
- let options = notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? UInt
351
- if let options, AVAudioSession.InterruptionOptions(rawValue: options).contains(.shouldResume) {
352
- // Resume playback
353
- }
354
- @unknown default:
355
- break
255
+ @MainActor
256
+ func followInterruptions(for engine: EpisodeEngine) async {
257
+ let feed = NotificationCenter.default.notifications(named: AVAudioSession.interruptionNotification)
258
+ for await note in feed {
259
+ let info = note.userInfo ?? [:]
260
+ guard let kindValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
261
+ let kind = AVAudioSession.InterruptionType(rawValue: kindValue) else { continue }
262
+
263
+ if kind == .began {
264
+ engine.suspend() // also remember the position
265
+ } else if kind == .ended {
266
+ let flags = AVAudioSession.InterruptionOptions(
267
+ rawValue: info[AVAudioSessionInterruptionOptionKey] as? UInt ?? 0)
268
+ if flags.contains(.shouldResume) { engine.resume() }
356
269
  }
357
270
  }
358
271
  }
359
272
  ```
360
273
 
361
- ### Route Change Handling
274
+ ### Route changes
362
275
 
363
- Pause playback when headphones are unplugged to avoid unexpected speaker output.
276
+ Pulling out headphones should stop the audio instead of moving it to the
277
+ speaker. The system reports that as `.oldDeviceUnavailable`.
364
278
 
365
279
  ```swift
366
- func observeRouteChanges() {
367
- NotificationCenter.default.addObserver(
368
- forName: AVAudioSession.routeChangeNotification,
369
- object: AVAudioSession.sharedInstance(),
370
- queue: .main
371
- ) { notification in
372
- guard let reasonValue = notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt,
373
- let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
374
- return
375
- }
376
-
377
- if reason == .oldDeviceUnavailable {
378
- // Headphones were unplugged -- pause playback
379
- pausePlayback()
280
+ @MainActor
281
+ func followRouteChanges(for engine: EpisodeEngine) async {
282
+ let feed = NotificationCenter.default.notifications(named: AVAudioSession.routeChangeNotification)
283
+ for await note in feed {
284
+ let reasonValue = note.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt ?? 0
285
+ if AVAudioSession.RouteChangeReason(rawValue: reasonValue) == .oldDeviceUnavailable {
286
+ engine.suspend()
380
287
  }
381
288
  }
382
289
  }
383
290
  ```
291
+
292
+ Start both from a long-lived `.task` or from the engine's own setup.
293
+ `EpisodeEngine` stands for whatever `@MainActor` type drives the app's custom
294
+ audio.