@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,239 +1,199 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: musickit-audio
|
|
3
|
-
description: "
|
|
3
|
+
description: "MusicKit and MediaPlayer on iOS: Apple Music authorization, catalog search, subscription checks and offers, ApplicationMusicPlayer playback controls and queues, Now Playing info, remote commands. Use when adding Apple Music playback or Now Playing controls."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# MusicKit
|
|
8
|
+
# MusicKit and Now Playing
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
Two frameworks cover music in an iOS app. MusicKit talks to Apple Music:
|
|
11
|
+
permission, catalog, subscription state, library and an app-owned player.
|
|
12
|
+
MediaPlayer talks to the system: the Now Playing card on the Lock Screen, in
|
|
13
|
+
Control Center and in CarPlay, plus the remote commands that headphones and
|
|
14
|
+
those surfaces send. Code here targets Swift 6.3 and iOS 26.
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
- [Catalog Search](#catalog-search)
|
|
19
|
-
- [Subscription Checks](#subscription-checks)
|
|
20
|
-
- [Playback with ApplicationMusicPlayer](#playback-with-applicationmusicplayer)
|
|
21
|
-
- [Queue Management](#queue-management)
|
|
22
|
-
- [Now Playing Info](#now-playing-info)
|
|
23
|
-
- [Remote Command Center](#remote-command-center)
|
|
24
|
-
- [Common Mistakes](#common-mistakes)
|
|
25
|
-
- [Review Checklist](#review-checklist)
|
|
26
|
-
- [References](#references)
|
|
16
|
+
Longer building blocks (an observable player model, mini player, search screen,
|
|
17
|
+
charts, library and playlist work, `MPNowPlayingSession`, audio session setup,
|
|
18
|
+
interruptions and route changes) live in
|
|
19
|
+
[references/musickit-patterns.md](references/musickit-patterns.md).
|
|
27
20
|
|
|
28
21
|
## Setup
|
|
29
22
|
|
|
30
|
-
###
|
|
23
|
+
### Portal and Info.plist
|
|
24
|
+
|
|
25
|
+
1. In the developer portal, open the app's explicit App ID and turn on the
|
|
26
|
+
**MusicKit** App Service. That is what lets the system mint developer tokens
|
|
27
|
+
for the app automatically; there is no token code to write.
|
|
28
|
+
2. Add `NSAppleMusicUsageDescription` to Info.plist. Without it, access to the
|
|
29
|
+
user's media library fails wherever the system requires the string.
|
|
30
|
+
3. Only if audio must keep playing in the background: add `audio` to the
|
|
31
|
+
`UIBackgroundModes` array.
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
There is no `com.apple.developer.musickit` entitlement to add on iOS, and a
|
|
34
|
+
missing entitlement is not what makes authorization fail. The App Service and
|
|
35
|
+
the usage string are the two requirements.
|
|
35
36
|
|
|
36
37
|
### Imports
|
|
37
38
|
|
|
38
39
|
```swift
|
|
39
|
-
import MusicKit
|
|
40
|
-
import MediaPlayer
|
|
40
|
+
import MusicKit // authorization, catalog, subscription, ApplicationMusicPlayer
|
|
41
|
+
import MediaPlayer // MPNowPlayingInfoCenter, MPRemoteCommandCenter
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
## Authorization
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
content. `request()` presents Apple's consent dialog when necessary; use
|
|
47
|
-
`currentStatus` to read the current setting without prompting.
|
|
46
|
+
Ask before touching the user's music data or starting Apple Music playback.
|
|
48
47
|
|
|
49
48
|
```swift
|
|
50
|
-
func
|
|
51
|
-
|
|
52
|
-
switch status {
|
|
49
|
+
func ensureMusicAccess() async -> Bool {
|
|
50
|
+
switch await MusicAuthorization.request() {
|
|
53
51
|
case .authorized:
|
|
54
|
-
|
|
55
|
-
break
|
|
52
|
+
return true
|
|
56
53
|
case .denied, .restricted:
|
|
57
|
-
//
|
|
58
|
-
|
|
54
|
+
// Explain the limitation and point the user at Settings.
|
|
55
|
+
return false
|
|
59
56
|
case .notDetermined:
|
|
60
|
-
|
|
57
|
+
return false
|
|
61
58
|
@unknown default:
|
|
62
|
-
|
|
59
|
+
return false
|
|
63
60
|
}
|
|
64
|
-
return status
|
|
65
61
|
}
|
|
66
|
-
|
|
67
|
-
// Check current status without prompting
|
|
68
|
-
let current = MusicAuthorization.currentStatus
|
|
69
62
|
```
|
|
70
63
|
|
|
71
|
-
|
|
64
|
+
`request()` shows the consent prompt only when the user has not answered yet;
|
|
65
|
+
otherwise it returns the stored answer. To read the state without any chance of
|
|
66
|
+
a prompt, use `MusicAuthorization.currentStatus`.
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
can fetch Apple Music resources, but playback of subscription catalog content
|
|
75
|
-
must still be gated on `MusicSubscription.current.canPlayCatalogContent`.
|
|
68
|
+
## Catalog search
|
|
76
69
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
var request = MusicCatalogSearchRequest(term: term, types: [Song.self])
|
|
80
|
-
request.limit = 25
|
|
70
|
+
Searching the catalog works for anyone who granted access. Playing what you
|
|
71
|
+
found is a separate question, answered by the subscription check below.
|
|
81
72
|
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
```swift
|
|
74
|
+
func findTracks(matching phrase: String) async throws -> MusicItemCollection<Song> {
|
|
75
|
+
var search = MusicCatalogSearchRequest(term: phrase, types: [Song.self])
|
|
76
|
+
search.limit = 25
|
|
77
|
+
let result = try await search.response()
|
|
78
|
+
return result.songs
|
|
84
79
|
}
|
|
85
80
|
```
|
|
86
81
|
|
|
87
|
-
|
|
82
|
+
For a row, read `song.title`, `song.artistName` and
|
|
83
|
+
`song.artwork?.url(width: 120, height: 120)`.
|
|
84
|
+
|
|
85
|
+
## Subscription state
|
|
88
86
|
|
|
89
87
|
```swift
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if let artwork = song.artwork {
|
|
93
|
-
let url = artwork.url(width: 300, height: 300)
|
|
94
|
-
// Load artwork from url
|
|
95
|
-
}
|
|
96
|
-
}
|
|
88
|
+
let membership = try await MusicSubscription.current
|
|
89
|
+
let mayPlay = membership.canPlayCatalogContent
|
|
97
90
|
```
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
Check whether the user has an active Apple Music subscription before offering playback features.
|
|
92
|
+
Subscription state changes while the app runs. Keep the UI in step:
|
|
102
93
|
|
|
103
94
|
```swift
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// Observe subscription changes
|
|
110
|
-
func observeSubscription() async {
|
|
111
|
-
for await subscription in MusicSubscription.subscriptionUpdates {
|
|
112
|
-
if subscription.canPlayCatalogContent {
|
|
113
|
-
// Enable full playback UI
|
|
114
|
-
} else {
|
|
115
|
-
// Show subscription offer
|
|
116
|
-
}
|
|
95
|
+
.task {
|
|
96
|
+
for await membership in MusicSubscription.subscriptionUpdates {
|
|
97
|
+
showsPlayControls = membership.canPlayCatalogContent
|
|
98
|
+
showsJoinButton = !membership.canPlayCatalogContent && membership.canBecomeSubscriber
|
|
117
99
|
}
|
|
118
100
|
}
|
|
119
101
|
```
|
|
120
102
|
|
|
121
|
-
###
|
|
103
|
+
### Presenting the Apple Music offer
|
|
122
104
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`onLoadCompletion` when the sheet needs contextual metadata or load-error handling.
|
|
105
|
+
Only offer when `canBecomeSubscriber` is true. The sheet comes from a view
|
|
106
|
+
modifier:
|
|
126
107
|
|
|
127
108
|
```swift
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
@State private var showOffer = false
|
|
109
|
+
struct JoinAppleMusicButton: View {
|
|
110
|
+
@State private var offerVisible = false
|
|
111
|
+
@State private var offerOptions = MusicSubscriptionOffer.Options.default
|
|
112
|
+
@State private var offerError: (any Error)?
|
|
133
113
|
|
|
134
114
|
var body: some View {
|
|
135
|
-
Button("
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
.musicSubscriptionOffer(
|
|
142
|
-
isPresented: $showOffer,
|
|
143
|
-
options: .default,
|
|
144
|
-
onLoadCompletion: { error in
|
|
145
|
-
if let error {
|
|
146
|
-
// Surface loading errors in app UI or diagnostics.
|
|
147
|
-
print(error)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
)
|
|
115
|
+
Button("Try Apple Music") { offerVisible = true }
|
|
116
|
+
.musicSubscriptionOffer(
|
|
117
|
+
isPresented: $offerVisible,
|
|
118
|
+
options: offerOptions,
|
|
119
|
+
onLoadCompletion: { failure in offerError = failure }
|
|
120
|
+
)
|
|
151
121
|
}
|
|
152
122
|
}
|
|
153
123
|
```
|
|
154
124
|
|
|
155
|
-
|
|
125
|
+
When `offerError` is non-nil the sheet did not load; show a fallback message.
|
|
156
126
|
|
|
157
|
-
|
|
127
|
+
## ApplicationMusicPlayer
|
|
158
128
|
|
|
159
|
-
|
|
160
|
-
|
|
129
|
+
`ApplicationMusicPlayer.shared` is the app's own player. It plays alongside the
|
|
130
|
+
Music app without touching the Music app's queue or state.
|
|
161
131
|
|
|
162
|
-
|
|
163
|
-
|
|
132
|
+
```swift
|
|
133
|
+
func start(_ track: Song) async throws {
|
|
134
|
+
let player = ApplicationMusicPlayer.shared
|
|
135
|
+
player.queue = [track]
|
|
164
136
|
try await player.play()
|
|
165
137
|
}
|
|
166
138
|
|
|
167
|
-
func pause()
|
|
168
|
-
player.pause()
|
|
169
|
-
}
|
|
139
|
+
func halt() { ApplicationMusicPlayer.shared.pause() }
|
|
170
140
|
|
|
171
|
-
func
|
|
172
|
-
try await player.skipToNextEntry()
|
|
173
|
-
}
|
|
141
|
+
func forward() async throws { try await ApplicationMusicPlayer.shared.skipToNextEntry() }
|
|
174
142
|
```
|
|
175
143
|
|
|
176
|
-
|
|
144
|
+
The player is not `Sendable`. Read `.shared` where it is used; a global
|
|
145
|
+
`let player`, or a property on a main-actor type, fails Swift 6 checking once
|
|
146
|
+
an async method is called on it. The snippets below use `player` for
|
|
147
|
+
`ApplicationMusicPlayer.shared`.
|
|
148
|
+
|
|
149
|
+
`player.state` is observable, so SwiftUI redraws when it changes. Branch on
|
|
150
|
+
`playbackStatus`:
|
|
177
151
|
|
|
178
152
|
```swift
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
case .stopped, .interrupted, .seekingForward, .seekingBackward:
|
|
188
|
-
break
|
|
189
|
-
@unknown default:
|
|
190
|
-
break
|
|
191
|
-
}
|
|
153
|
+
switch player.state.playbackStatus {
|
|
154
|
+
case .playing: label = "Playing"
|
|
155
|
+
case .paused: label = "Paused"
|
|
156
|
+
case .stopped: label = "Stopped"
|
|
157
|
+
case .interrupted: label = "Interrupted"
|
|
158
|
+
case .seekingForward,
|
|
159
|
+
.seekingBackward: label = "Seeking"
|
|
160
|
+
@unknown default: label = ""
|
|
192
161
|
}
|
|
193
162
|
```
|
|
194
163
|
|
|
195
|
-
## Queue
|
|
164
|
+
## Queue management
|
|
196
165
|
|
|
197
|
-
|
|
166
|
+
`player.queue` is an `ApplicationMusicPlayer.Queue`. Assigning a collection
|
|
167
|
+
replaces it, and an album or playlist works as well as songs:
|
|
198
168
|
|
|
199
169
|
```swift
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
player.queue = [album]
|
|
203
|
-
try await player.play()
|
|
204
|
-
}
|
|
170
|
+
player.queue = [albumToPlay]
|
|
171
|
+
```
|
|
205
172
|
|
|
206
|
-
|
|
207
|
-
func appendToQueue(_ songs: [Song]) async throws {
|
|
208
|
-
try await player.queue.insert(songs, position: .tail)
|
|
209
|
-
}
|
|
173
|
+
To add without replacing:
|
|
210
174
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
175
|
+
```swift
|
|
176
|
+
try await player.queue.insert(extraTracks, position: .tail) // at the end
|
|
177
|
+
try await player.queue.insert(nextUp, position: .afterCurrentEntry) // play next
|
|
215
178
|
```
|
|
216
179
|
|
|
217
|
-
## Now Playing
|
|
180
|
+
## Now Playing info
|
|
218
181
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
182
|
+
`MPNowPlayingInfoCenter` feeds the Lock Screen, Control Center and CarPlay.
|
|
183
|
+
When `ApplicationMusicPlayer` plays Apple Music content, the system fills this
|
|
184
|
+
in for you. Write it yourself only for audio the app plays some other way, such
|
|
185
|
+
as an `AVPlayer` streaming podcasts.
|
|
223
186
|
|
|
224
187
|
```swift
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
info[MPNowPlayingInfoPropertyMediaType] = MPNowPlayingInfoMediaType.audio.rawValue
|
|
235
|
-
|
|
236
|
-
MPNowPlayingInfoCenter.default().nowPlayingInfo = info
|
|
188
|
+
func publishNowPlaying(for episode: Episode, position: TimeInterval) {
|
|
189
|
+
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
|
|
190
|
+
MPMediaItemPropertyTitle: episode.title,
|
|
191
|
+
MPMediaItemPropertyArtist: episode.showName,
|
|
192
|
+
MPMediaItemPropertyPlaybackDuration: episode.length,
|
|
193
|
+
MPNowPlayingInfoPropertyElapsedPlaybackTime: position,
|
|
194
|
+
MPNowPlayingInfoPropertyPlaybackRate: 1.0,
|
|
195
|
+
MPNowPlayingInfoPropertyMediaType: MPNowPlayingInfoMediaType.audio.rawValue
|
|
196
|
+
]
|
|
237
197
|
}
|
|
238
198
|
|
|
239
199
|
func clearNowPlaying() {
|
|
@@ -241,184 +201,98 @@ func clearNowPlaying() {
|
|
|
241
201
|
}
|
|
242
202
|
```
|
|
243
203
|
|
|
244
|
-
|
|
204
|
+
Artwork goes in under `MPMediaItemPropertyArtwork`. Merge it into the existing
|
|
205
|
+
dictionary rather than replacing the whole thing:
|
|
245
206
|
|
|
246
207
|
```swift
|
|
247
|
-
func
|
|
248
|
-
let
|
|
208
|
+
func attachCover(_ cover: UIImage) {
|
|
209
|
+
let art = MPMediaItemArtwork(boundsSize: cover.size) { _ in cover }
|
|
249
210
|
var info = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [:]
|
|
250
|
-
info[MPMediaItemPropertyArtwork] =
|
|
211
|
+
info[MPMediaItemPropertyArtwork] = art
|
|
251
212
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = info
|
|
252
213
|
}
|
|
253
214
|
```
|
|
254
215
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
Register handlers for `MPRemoteCommandCenter` to respond to Lock Screen controls,
|
|
258
|
-
AirPods tap gestures, and CarPlay buttons.
|
|
216
|
+
Refresh the elapsed time on a timer or at each seek, so the Lock Screen
|
|
217
|
+
scrubber stays accurate.
|
|
259
218
|
|
|
260
|
-
|
|
261
|
-
func setupRemoteCommands() {
|
|
262
|
-
let center = MPRemoteCommandCenter.shared()
|
|
263
|
-
|
|
264
|
-
center.playCommand.addTarget { _ in
|
|
265
|
-
resumePlayback()
|
|
266
|
-
return .success
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
center.pauseCommand.addTarget { _ in
|
|
270
|
-
pausePlayback()
|
|
271
|
-
return .success
|
|
272
|
-
}
|
|
219
|
+
## Remote commands
|
|
273
220
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
221
|
+
`MPRemoteCommandCenter.shared()` receives Lock Screen buttons, AirPods taps and
|
|
222
|
+
CarPlay controls. Register the handlers from the main actor so they can call a
|
|
223
|
+
main-actor engine directly; `EpisodeEngine` stands for that type.
|
|
278
224
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
225
|
+
```swift
|
|
226
|
+
@MainActor
|
|
227
|
+
func wireRemoteControls(to engine: EpisodeEngine) {
|
|
228
|
+
let commands = MPRemoteCommandCenter.shared()
|
|
283
229
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
```
|
|
230
|
+
commands.playCommand.addTarget { _ in engine.resume(); return .success }
|
|
231
|
+
commands.pauseCommand.addTarget { _ in engine.suspend(); return .success }
|
|
232
|
+
commands.nextTrackCommand.addTarget { _ in engine.advance(); return .success }
|
|
233
|
+
commands.previousTrackCommand.addTarget { _ in engine.rewind(); return .success }
|
|
289
234
|
|
|
290
|
-
|
|
235
|
+
commands.seekForwardCommand.isEnabled = false
|
|
236
|
+
commands.seekBackwardCommand.isEnabled = false
|
|
291
237
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
let center = MPRemoteCommandCenter.shared()
|
|
295
|
-
center.changePlaybackPositionCommand.addTarget { event in
|
|
296
|
-
guard let positionEvent = event as? MPChangePlaybackPositionCommandEvent else {
|
|
238
|
+
commands.changePlaybackPositionCommand.addTarget { event in
|
|
239
|
+
guard let jump = event as? MPChangePlaybackPositionCommandEvent else {
|
|
297
240
|
return .commandFailed
|
|
298
241
|
}
|
|
299
|
-
|
|
242
|
+
engine.move(to: jump.positionTime)
|
|
300
243
|
return .success
|
|
301
244
|
}
|
|
302
245
|
}
|
|
303
246
|
```
|
|
304
247
|
|
|
305
|
-
## Common
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// CORRECT
|
|
334
|
-
func play(_ song: Song) async throws {
|
|
335
|
-
let sub = try await MusicSubscription.current
|
|
336
|
-
guard sub.canPlayCatalogContent else {
|
|
337
|
-
showSubscriptionOffer()
|
|
338
|
-
return
|
|
339
|
-
}
|
|
340
|
-
player.queue = [song]
|
|
341
|
-
try await player.play()
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### DON'T: Use SystemMusicPlayer when you mean ApplicationMusicPlayer
|
|
346
|
-
|
|
347
|
-
`SystemMusicPlayer` controls the global Music app queue. Changes affect the user's
|
|
348
|
-
Music app state. Use `ApplicationMusicPlayer` for app-scoped playback.
|
|
349
|
-
|
|
350
|
-
```swift
|
|
351
|
-
// WRONG: Modifies the user's Music app queue
|
|
352
|
-
let player = SystemMusicPlayer.shared
|
|
353
|
-
|
|
354
|
-
// CORRECT: App-scoped playback
|
|
355
|
-
let player = ApplicationMusicPlayer.shared
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
### DON'T: Forget to update Now Playing info when track changes
|
|
359
|
-
|
|
360
|
-
Stale metadata on the Lock Screen confuses users. Update Now Playing info
|
|
361
|
-
every time the current track changes.
|
|
362
|
-
|
|
363
|
-
```swift
|
|
364
|
-
// WRONG: Set once and forget
|
|
365
|
-
updateNowPlaying(title: firstSong.title, ...)
|
|
366
|
-
|
|
367
|
-
// CORRECT: Update on every track change
|
|
368
|
-
func onTrackChanged(_ song: Song) {
|
|
369
|
-
updateNowPlaying(
|
|
370
|
-
title: song.title,
|
|
371
|
-
artist: song.artistName,
|
|
372
|
-
duration: song.duration ?? 0,
|
|
373
|
-
elapsed: 0
|
|
374
|
-
)
|
|
375
|
-
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
### DON'T: Register remote commands without handling them
|
|
379
|
-
|
|
380
|
-
Registering a command but returning `.commandFailed` breaks Lock Screen controls.
|
|
381
|
-
Disable commands you do not support instead.
|
|
382
|
-
|
|
383
|
-
```swift
|
|
384
|
-
// WRONG
|
|
385
|
-
center.skipForwardCommand.addTarget { _ in .commandFailed }
|
|
386
|
-
|
|
387
|
-
// CORRECT
|
|
388
|
-
center.skipForwardCommand.isEnabled = false
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
## Review Checklist
|
|
392
|
-
|
|
393
|
-
- [ ] MusicKit App Service enabled for the app's explicit bundle ID
|
|
394
|
-
- [ ] `NSAppleMusicUsageDescription` added to Info.plist
|
|
395
|
-
- [ ] `MusicAuthorization.request()` called before any MusicKit access
|
|
396
|
-
- [ ] Subscription checked before attempting catalog playback
|
|
397
|
-
- [ ] `canBecomeSubscriber` checked before presenting a subscription offer
|
|
248
|
+
## Common mistakes
|
|
249
|
+
|
|
250
|
+
- **Skipping portal or plist setup.** Without the MusicKit App Service there is
|
|
251
|
+
no automatic developer token; without `NSAppleMusicUsageDescription`, library
|
|
252
|
+
access fails where it is required.
|
|
253
|
+
- **Playing catalog items without a subscription.** Playback fails quietly or
|
|
254
|
+
throws. Check `canPlayCatalogContent` first and show the offer when it is
|
|
255
|
+
false.
|
|
256
|
+
- **Reaching for `SystemMusicPlayer`.** It drives the Music app's global queue
|
|
257
|
+
and changes the user's own listening state. App-scoped playback belongs in
|
|
258
|
+
`ApplicationMusicPlayer`.
|
|
259
|
+
- **Stale Now Playing data.** For custom audio, rewrite the dictionary on every
|
|
260
|
+
track change: title, artist, duration (use `track.duration ?? 0` when it is
|
|
261
|
+
optional) and an elapsed time reset to 0.
|
|
262
|
+
- **Registering commands you cannot honor.** A handler that returns
|
|
263
|
+
`.commandFailed` leaves a dead button on the Lock Screen. Turn the command off
|
|
264
|
+
instead, for example `MPRemoteCommandCenter.shared().skipForwardCommand.isEnabled = false`.
|
|
265
|
+
|
|
266
|
+
## Review checklist
|
|
267
|
+
|
|
268
|
+
- [ ] MusicKit App Service enabled on the explicit App ID
|
|
269
|
+
- [ ] `NSAppleMusicUsageDescription` present
|
|
270
|
+
- [ ] `MusicAuthorization.request()` runs before any MusicKit access
|
|
271
|
+
- [ ] Subscription checked before catalog playback
|
|
272
|
+
- [ ] `canBecomeSubscriber` checked before the offer is shown
|
|
273
|
+
- [ ] Offer shown when the user is not subscribed
|
|
398
274
|
- [ ] `hasCloudLibraryEnabled` checked before library writes
|
|
399
|
-
- [ ] `ApplicationMusicPlayer` used
|
|
400
|
-
- [ ]
|
|
401
|
-
- [ ] Now Playing
|
|
402
|
-
- [ ]
|
|
403
|
-
- [ ]
|
|
404
|
-
- [ ]
|
|
405
|
-
- [ ] Elapsed playback time updated periodically for scrubber accuracy
|
|
406
|
-
- [ ] Subscription offer presented when user lacks Apple Music subscription
|
|
275
|
+
- [ ] `ApplicationMusicPlayer` used, not `SystemMusicPlayer`
|
|
276
|
+
- [ ] `audio` background mode set if playback continues in the background
|
|
277
|
+
- [ ] Now Playing updated on each track change (custom audio)
|
|
278
|
+
- [ ] Artwork supplied for the Lock Screen
|
|
279
|
+
- [ ] Elapsed time refreshed so the scrubber is accurate
|
|
280
|
+
- [ ] Supported remote commands return `.success`; unsupported ones are disabled
|
|
407
281
|
|
|
408
282
|
## References
|
|
409
283
|
|
|
410
|
-
-
|
|
411
|
-
- [MusicKit
|
|
412
|
-
- [Using automatic developer token generation
|
|
413
|
-
- [MusicAuthorization](https://
|
|
414
|
-
- [ApplicationMusicPlayer](https://
|
|
415
|
-
- [MusicCatalogSearchRequest](https://
|
|
416
|
-
- [MusicSubscription](https://
|
|
417
|
-
- [canPlayCatalogContent](https://
|
|
418
|
-
- [canBecomeSubscriber](https://
|
|
419
|
-
- [hasCloudLibraryEnabled](https://
|
|
420
|
-
- [MusicCatalogChartsRequest
|
|
421
|
-
- [musicSubscriptionOffer(isPresented:options:onLoadCompletion:)](https://
|
|
422
|
-
- [MPRemoteCommandCenter](https://
|
|
423
|
-
- [MPNowPlayingInfoCenter](https://
|
|
424
|
-
- [NSAppleMusicUsageDescription](https://
|
|
284
|
+
- [references/musickit-patterns.md](references/musickit-patterns.md)
|
|
285
|
+
- [MusicKit](https://developer.apple.com/documentation/musickit)
|
|
286
|
+
- [Using automatic developer token generation](https://developer.apple.com/documentation/musickit/using-automatic-developer-token-generation-for-apple-music-api)
|
|
287
|
+
- [MusicAuthorization](https://developer.apple.com/documentation/musickit/musicauthorization)
|
|
288
|
+
- [ApplicationMusicPlayer](https://developer.apple.com/documentation/musickit/applicationmusicplayer)
|
|
289
|
+
- [MusicCatalogSearchRequest](https://developer.apple.com/documentation/musickit/musiccatalogsearchrequest)
|
|
290
|
+
- [MusicSubscription](https://developer.apple.com/documentation/musickit/musicsubscription)
|
|
291
|
+
- [canPlayCatalogContent](https://developer.apple.com/documentation/musickit/musicsubscription/canplaycatalogcontent)
|
|
292
|
+
- [canBecomeSubscriber](https://developer.apple.com/documentation/musickit/musicsubscription/canbecomesubscriber)
|
|
293
|
+
- [hasCloudLibraryEnabled](https://developer.apple.com/documentation/musickit/musicsubscription/hascloudlibraryenabled)
|
|
294
|
+
- [MusicCatalogChartsRequest init(genre:kinds:types:)](https://developer.apple.com/documentation/musickit/musiccatalogchartsrequest/init(genre:kinds:types:))
|
|
295
|
+
- [musicSubscriptionOffer(isPresented:options:onLoadCompletion:)](https://developer.apple.com/documentation/swiftui/view/musicsubscriptionoffer(ispresented:options:onloadcompletion:))
|
|
296
|
+
- [MPRemoteCommandCenter](https://developer.apple.com/documentation/mediaplayer/mpremotecommandcenter)
|
|
297
|
+
- [MPNowPlayingInfoCenter](https://developer.apple.com/documentation/mediaplayer/mpnowplayinginfocenter)
|
|
298
|
+
- [NSAppleMusicUsageDescription](https://developer.apple.com/documentation/bundleresources/information-property-list/nsapplemusicusagedescription)
|