@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,499 +1,402 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: avkit
|
|
3
|
-
description: "
|
|
3
|
+
description: "AVKit playback UI: AVPlayerViewController, SwiftUI VideoPlayer, Picture-in-Picture, AirPlay routing, transport controls, subtitles and closed captions, audio session and background mode, AVFoundation playback in system UI. Use when building a video player or adding PiP, AirPlay, transport controls or captions."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# AVKit
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
AVKit is the playback UI layer over AVFoundation. It supplies the standard
|
|
11
|
+
player, Picture-in-Picture (PiP), AirPlay, transport controls and subtitle and
|
|
12
|
+
caption display. Code here is written for iOS 26 with Swift 6.3.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- [Setup](#setup)
|
|
17
|
-
- [AVPlayerViewController](#avplayerviewcontroller)
|
|
18
|
-
- [SwiftUI VideoPlayer](#swiftui-videoplayer)
|
|
19
|
-
- [Picture-in-Picture](#picture-in-picture)
|
|
20
|
-
- [AirPlay](#airplay)
|
|
21
|
-
- [Transport Controls and Playback Speed](#transport-controls-and-playback-speed)
|
|
22
|
-
- [Subtitles and Closed Captions](#subtitles-and-closed-captions)
|
|
23
|
-
- [Common Mistakes](#common-mistakes)
|
|
24
|
-
- [Review Checklist](#review-checklist)
|
|
25
|
-
- [References](#references)
|
|
14
|
+
Covers: PiP for standard and custom players, AirPlay routing with
|
|
15
|
+
`AVRoutePickerView`, playback speeds and interstitial ads.
|
|
26
16
|
|
|
27
17
|
## Setup
|
|
28
18
|
|
|
29
|
-
### Audio
|
|
19
|
+
### Audio session and background mode
|
|
30
20
|
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
Background audio, AirPlay and PiP all depend on two things: an audio session
|
|
22
|
+
in the playback category, and the background mode that goes with it.
|
|
33
23
|
|
|
34
|
-
1.
|
|
35
|
-
`audio`
|
|
36
|
-
2. Set the
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
1. In Signing & Capabilities, add Background Modes and tick "Audio, AirPlay,
|
|
25
|
+
and Picture in Picture". That adds `audio` to `UIBackgroundModes`.
|
|
26
|
+
2. Set the category to `.playback`, with mode `.moviePlayback` for film and
|
|
27
|
+
video content.
|
|
28
|
+
3. Activate the session only when playback actually starts, so the app does not
|
|
29
|
+
silence other audio before the user presses play.
|
|
39
30
|
|
|
40
31
|
```swift
|
|
41
32
|
import AVFoundation
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
enum PlaybackAudio {
|
|
35
|
+
static func configure() {
|
|
36
|
+
do {
|
|
37
|
+
let session = AVAudioSession.sharedInstance()
|
|
38
|
+
try session.setCategory(.playback, mode: .moviePlayback)
|
|
39
|
+
} catch {
|
|
40
|
+
assertionFailure("Audio session category failed: \(error)")
|
|
41
|
+
}
|
|
49
42
|
}
|
|
50
|
-
}
|
|
51
43
|
|
|
52
|
-
func
|
|
53
|
-
do {
|
|
44
|
+
static func activateForPlayback() throws {
|
|
54
45
|
try AVAudioSession.sharedInstance().setActive(true)
|
|
55
|
-
} catch {
|
|
56
|
-
print("Audio session activation failed: \(error)")
|
|
57
46
|
}
|
|
58
47
|
}
|
|
59
48
|
```
|
|
60
49
|
|
|
61
50
|
### Imports
|
|
62
51
|
|
|
63
|
-
|
|
64
|
-
import
|
|
65
|
-
import AVFoundation // AVPlayer, AVPlayerItem, AVAsset
|
|
66
|
-
```
|
|
52
|
+
- `import AVKit` for `AVPlayerViewController`, `VideoPlayer` and PiP.
|
|
53
|
+
- `import AVFoundation` for `AVPlayer`, `AVPlayerItem` and `AVAsset`.
|
|
67
54
|
|
|
68
55
|
## AVPlayerViewController
|
|
69
56
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### Basic Presentation (Full Screen)
|
|
57
|
+
The standard UIKit player. It brings the system controls, PiP, AirPlay,
|
|
58
|
+
subtitles and video frame analysis. Apple does not support subclassing it;
|
|
59
|
+
configure it and use its delegate instead.
|
|
75
60
|
|
|
76
61
|
```swift
|
|
62
|
+
import AVFoundation
|
|
77
63
|
import AVKit
|
|
64
|
+
import UIKit
|
|
65
|
+
|
|
66
|
+
final class LessonViewController: UIViewController {
|
|
67
|
+
private let playerScreen = AVPlayerViewController()
|
|
78
68
|
|
|
79
|
-
func
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
69
|
+
func presentFullScreen(_ url: URL) {
|
|
70
|
+
let viewer = AVPlayerViewController()
|
|
71
|
+
viewer.player = AVPlayer(url: url)
|
|
72
|
+
present(viewer, animated: true) {
|
|
73
|
+
viewer.player?.play()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
83
76
|
|
|
84
|
-
|
|
85
|
-
player
|
|
77
|
+
func embed(_ url: URL, in holder: UIView) {
|
|
78
|
+
playerScreen.player = AVPlayer(url: url)
|
|
79
|
+
addChild(playerScreen)
|
|
80
|
+
holder.addSubview(playerScreen.view)
|
|
81
|
+
playerScreen.view.translatesAutoresizingMaskIntoConstraints = false
|
|
82
|
+
NSLayoutConstraint.activate([
|
|
83
|
+
playerScreen.view.topAnchor.constraint(equalTo: holder.topAnchor),
|
|
84
|
+
playerScreen.view.bottomAnchor.constraint(equalTo: holder.bottomAnchor),
|
|
85
|
+
playerScreen.view.leadingAnchor.constraint(equalTo: holder.leadingAnchor),
|
|
86
|
+
playerScreen.view.trailingAnchor.constraint(equalTo: holder.trailingAnchor)
|
|
87
|
+
])
|
|
88
|
+
playerScreen.didMove(toParent: self)
|
|
86
89
|
}
|
|
87
90
|
}
|
|
88
91
|
```
|
|
89
92
|
|
|
90
|
-
###
|
|
93
|
+
### Key properties
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
| Property | Effect |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `showsPlaybackControls` | shows or hides the system controls |
|
|
98
|
+
| `videoGravity` | `.resizeAspect` fits the frame, `.resizeAspectFill` crops to fill |
|
|
99
|
+
| `entersFullScreenWhenPlaybackBegins` | goes full screen when play starts |
|
|
100
|
+
| `exitsFullScreenWhenPlaybackEnds` | leaves full screen at the end |
|
|
101
|
+
| `updatesNowPlayingInfoCenter` | keeps `MPNowPlayingInfoCenter` in sync automatically |
|
|
102
|
+
| `contentOverlayView` | holds non-interactive views (logos, watermarks) between the video and the controls |
|
|
94
103
|
|
|
95
|
-
|
|
96
|
-
func embedPlayer(in parent: UIViewController, container: UIView, url: URL) {
|
|
97
|
-
let playerVC = AVPlayerViewController()
|
|
98
|
-
playerVC.player = AVPlayer(url: url)
|
|
99
|
-
|
|
100
|
-
parent.addChild(playerVC)
|
|
101
|
-
container.addSubview(playerVC.view)
|
|
102
|
-
playerVC.view.translatesAutoresizingMaskIntoConstraints = false
|
|
103
|
-
NSLayoutConstraint.activate([
|
|
104
|
-
playerVC.view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
|
|
105
|
-
playerVC.view.trailingAnchor.constraint(equalTo: container.trailingAnchor),
|
|
106
|
-
playerVC.view.topAnchor.constraint(equalTo: container.topAnchor),
|
|
107
|
-
playerVC.view.bottomAnchor.constraint(equalTo: container.bottomAnchor)
|
|
108
|
-
])
|
|
109
|
-
playerVC.didMove(toParent: parent)
|
|
110
|
-
}
|
|
111
|
-
```
|
|
104
|
+
### Delegate
|
|
112
105
|
|
|
113
|
-
|
|
106
|
+
`AVPlayerViewControllerDelegate` reports full-screen transitions, the PiP
|
|
107
|
+
lifecycle, ad interstitials and changes to the selected media options. To move your own
|
|
108
|
+
UI in step with the full-screen animation, use the transition coordinator:
|
|
114
109
|
|
|
115
110
|
```swift
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
playerVC.entersFullScreenWhenPlaybackBegins = false
|
|
119
|
-
playerVC.exitsFullScreenWhenPlaybackEnds = true
|
|
120
|
-
playerVC.updatesNowPlayingInfoCenter = true // Auto-updates MPNowPlayingInfoCenter
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Use `contentOverlayView` to add non-interactive views (watermarks, logos)
|
|
124
|
-
between the video and transport controls.
|
|
111
|
+
import AVKit
|
|
112
|
+
import UIKit
|
|
125
113
|
|
|
126
|
-
|
|
114
|
+
@MainActor
|
|
115
|
+
final class FullScreenSync: NSObject, @preconcurrency AVPlayerViewControllerDelegate {
|
|
116
|
+
var chrome: UIView?
|
|
127
117
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
118
|
+
func playerViewController(_ playerViewController: AVPlayerViewController,
|
|
119
|
+
willBeginFullScreenPresentationWithAnimationCoordinator coordinator: any UIViewControllerTransitionCoordinator) {
|
|
120
|
+
coordinator.animate(alongsideTransition: { _ in self.chrome?.alpha = 0 }, completion: nil)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
132
124
|
|
|
133
|
-
###
|
|
125
|
+
### Showing the player only when it has a frame
|
|
134
126
|
|
|
135
|
-
Observe `isReadyForDisplay`
|
|
127
|
+
Observe `isReadyForDisplay` and reveal the player when it turns `true`; this
|
|
128
|
+
avoids a black flash.
|
|
136
129
|
|
|
137
130
|
```swift
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
131
|
+
import AVKit
|
|
132
|
+
|
|
133
|
+
@MainActor
|
|
134
|
+
func revealWhenReady(_ viewer: AVPlayerViewController) -> NSKeyValueObservation {
|
|
135
|
+
viewer.view.alpha = 0
|
|
136
|
+
return viewer.observe(\.isReadyForDisplay, options: [.new]) { observed, _ in
|
|
137
|
+
MainActor.assumeIsolated {
|
|
138
|
+
if observed.isReadyForDisplay { observed.view.alpha = 1 }
|
|
139
|
+
}
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
142
|
```
|
|
144
143
|
|
|
145
144
|
## SwiftUI VideoPlayer
|
|
146
145
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
### Basic Usage
|
|
146
|
+
`VideoPlayer` is the SwiftUI view for AVKit's playback UI.
|
|
150
147
|
|
|
151
148
|
```swift
|
|
152
|
-
import SwiftUI
|
|
153
149
|
import AVKit
|
|
150
|
+
import SwiftUI
|
|
154
151
|
|
|
155
|
-
struct
|
|
152
|
+
struct TrailerView: View {
|
|
153
|
+
let source: URL
|
|
156
154
|
@State private var player: AVPlayer?
|
|
157
155
|
|
|
158
156
|
var body: some View {
|
|
159
157
|
Group {
|
|
160
158
|
if let player {
|
|
161
|
-
VideoPlayer(player: player)
|
|
162
|
-
|
|
159
|
+
VideoPlayer(player: player) {
|
|
160
|
+
VStack {
|
|
161
|
+
Spacer()
|
|
162
|
+
Text("Preview")
|
|
163
|
+
.font(.caption)
|
|
164
|
+
.padding(6)
|
|
165
|
+
.background(.ultraThinMaterial, in: Capsule())
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
.frame(height: 240)
|
|
163
169
|
} else {
|
|
164
170
|
ProgressView()
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
173
|
.task {
|
|
168
|
-
|
|
169
|
-
player = AVPlayer(url: url)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Video Overlay
|
|
176
|
-
|
|
177
|
-
Add a SwiftUI overlay above the video content and below the system playback
|
|
178
|
-
controls. The overlay can be interactive, but it only receives events the system
|
|
179
|
-
controls do not handle.
|
|
180
|
-
|
|
181
|
-
```swift
|
|
182
|
-
VideoPlayer(player: player) {
|
|
183
|
-
VStack {
|
|
184
|
-
Spacer()
|
|
185
|
-
HStack {
|
|
186
|
-
Image("logo")
|
|
187
|
-
.resizable()
|
|
188
|
-
.frame(width: 40, height: 40)
|
|
189
|
-
.padding()
|
|
190
|
-
Spacer()
|
|
174
|
+
player = AVPlayer(url: source)
|
|
191
175
|
}
|
|
192
176
|
}
|
|
193
177
|
}
|
|
194
178
|
```
|
|
195
179
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
180
|
+
- The trailing closure is a video overlay. It sits above the picture and below
|
|
181
|
+
the system controls. It may contain interactive views, but those only receive
|
|
182
|
+
events that the system controls did not handle.
|
|
183
|
+
- `VideoPlayer` exposes only part of what `AVPlayerViewController` offers. For
|
|
184
|
+
PiP settings, delegate callbacks or playback speeds, wrap
|
|
185
|
+
`AVPlayerViewController` in a `UIViewControllerRepresentable` (see
|
|
186
|
+
[the patterns reference](references/avkit-patterns.md#wrapping-avplayerviewcontroller-for-swiftui)).
|
|
202
187
|
|
|
203
188
|
## Picture-in-Picture
|
|
204
189
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
use `AVPictureInPictureController` directly.
|
|
190
|
+
`AVPlayerViewController` provides PiP by itself once three conditions hold:
|
|
191
|
+
setup is done, the hardware can do PiP, and the `AVPlayerItem` now loaded holds
|
|
192
|
+
playable video in a format AVPlayer handles. Audio-only items, unsupported
|
|
193
|
+
containers or codecs, and items not yet ready to display can all leave PiP
|
|
194
|
+
unavailable even when setup is correct. Custom player UIs drive
|
|
195
|
+
`AVPictureInPictureController` themselves.
|
|
212
196
|
|
|
213
197
|
### Prerequisites
|
|
214
198
|
|
|
215
|
-
1. Audio session category
|
|
216
|
-
2. Background
|
|
217
|
-
3.
|
|
218
|
-
4.
|
|
199
|
+
1. Audio session category `.playback`.
|
|
200
|
+
2. Background mode `audio` enabled (shown in Xcode as "Audio, AirPlay, and Picture in Picture").
|
|
201
|
+
3. A ready `AVPlayerItem` containing playable video, not only audio.
|
|
202
|
+
4. A playback context that allows PiP; custom players watch
|
|
203
|
+
`isPictureInPicturePossible`.
|
|
219
204
|
|
|
220
|
-
### Standard
|
|
205
|
+
### Standard player
|
|
221
206
|
|
|
222
|
-
PiP is
|
|
223
|
-
|
|
207
|
+
PiP is on by default. Turn it off with `allowsPictureInPicturePlayback = false`.
|
|
208
|
+
For an inline (not full-screen) player, set
|
|
209
|
+
`canStartPictureInPictureAutomaticallyFromInline = true` to enter PiP
|
|
210
|
+
automatically when the app goes to the background.
|
|
224
211
|
|
|
225
|
-
|
|
226
|
-
let playerVC = AVPlayerViewController()
|
|
227
|
-
playerVC.player = player
|
|
228
|
-
|
|
229
|
-
// PiP enabled by default; set false to disable
|
|
230
|
-
playerVC.allowsPictureInPicturePlayback = true
|
|
212
|
+
### Restoring the app UI when PiP ends
|
|
231
213
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
214
|
+
When the user taps the button to return from PiP, the app must put the player
|
|
215
|
+
back on screen and then pass `true` to the completion handler so the system
|
|
216
|
+
can finish its animation.
|
|
235
217
|
|
|
236
|
-
|
|
218
|
+
```swift
|
|
219
|
+
import AVKit
|
|
220
|
+
import UIKit
|
|
237
221
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
222
|
+
@MainActor
|
|
223
|
+
final class PiPReturn: NSObject, @preconcurrency AVPlayerViewControllerDelegate {
|
|
224
|
+
weak var host: UIViewController?
|
|
241
225
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
226
|
+
func playerViewController(_ playerViewController: AVPlayerViewController,
|
|
227
|
+
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
|
|
228
|
+
guard let host, playerViewController.presentingViewController == nil else {
|
|
229
|
+
completionHandler(true)
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
host.present(playerViewController, animated: false) {
|
|
233
|
+
completionHandler(true)
|
|
234
|
+
}
|
|
250
235
|
}
|
|
251
236
|
}
|
|
252
237
|
```
|
|
253
238
|
|
|
254
|
-
### Custom
|
|
239
|
+
### Custom players
|
|
255
240
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
then check the controller's `isPictureInPicturePossible` before starting PiP in
|
|
259
|
-
the current playback context. See [references/avkit-patterns.md](references/avkit-patterns.md)
|
|
260
|
-
for full custom player and sample buffer PiP patterns.
|
|
241
|
+
Build an `AVPictureInPictureController` from an `AVPlayerLayer` or from a
|
|
242
|
+
sample-buffer content source.
|
|
261
243
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if pipController.isPictureInPicturePossible {
|
|
270
|
-
pipController.startPictureInPicture()
|
|
271
|
-
}
|
|
272
|
-
```
|
|
244
|
+
- Check `AVPictureInPictureController.isPictureInPictureSupported()` before
|
|
245
|
+
showing any PiP control.
|
|
246
|
+
- Check `isPictureInPicturePossible` right before `startPictureInPicture()`.
|
|
247
|
+
- Call `startPictureInPicture()` only from the user's tap on a PiP button,
|
|
248
|
+
never on your own.
|
|
249
|
+
- The controller also has `canStartPictureInPictureAutomaticallyFromInline` and
|
|
250
|
+
a `delegate`.
|
|
273
251
|
|
|
274
|
-
|
|
252
|
+
Full layer-based and sample-buffer examples are in
|
|
253
|
+
[the patterns reference](references/avkit-patterns.md#custom-player-with-avplayerlayer).
|
|
275
254
|
|
|
276
|
-
|
|
277
|
-
exposes through `AVPlayerItem.interstitialTimeRanges`, or from an app-owned
|
|
278
|
-
`AVPlayerInterstitialEventController` schedule. Do not assign
|
|
279
|
-
`interstitialTimeRanges` directly on iOS. Use `requiresLinearPlayback` only to
|
|
280
|
-
prevent seeking during required ad or legal segments:
|
|
255
|
+
### Ads and linear playback
|
|
281
256
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
257
|
+
Interstitial breaks come either from the stream itself (HLS interstitials,
|
|
258
|
+
which the item reports through `interstitialTimeRanges`) or from a schedule the app owns,
|
|
259
|
+
built with `AVPlayerInterstitialEventController`. On iOS,
|
|
260
|
+
`interstitialTimeRanges` is read-only; do not try to assign it.
|
|
285
261
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
```
|
|
262
|
+
Set `requiresLinearPlayback = true` only while a required ad or legal segment
|
|
263
|
+
plays, to block seeking, and set it back to `false` afterwards.
|
|
289
264
|
|
|
290
265
|
## AirPlay
|
|
291
266
|
|
|
292
|
-
`AVPlayerViewController` supports AirPlay
|
|
293
|
-
media, routes
|
|
294
|
-
|
|
295
|
-
in the transport controls when AirPlay-capable devices are available.
|
|
267
|
+
`AVPlayerViewController` supports AirPlay with no extra code when the setup,
|
|
268
|
+
media, available routes and device allow it. The AirPlay button appears in the
|
|
269
|
+
controls whenever a receiver is available.
|
|
296
270
|
|
|
297
271
|
### AVRoutePickerView
|
|
298
272
|
|
|
299
|
-
|
|
273
|
+
For an AirPlay button outside the player, use `AVRoutePickerView`:
|
|
300
274
|
|
|
301
275
|
```swift
|
|
302
276
|
import AVKit
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
277
|
+
import UIKit
|
|
278
|
+
|
|
279
|
+
@MainActor
|
|
280
|
+
func castButton() -> AVRoutePickerView {
|
|
281
|
+
let picker = AVRoutePickerView(frame: CGRect(origin: .zero, size: CGSize(width: 44, height: 44)))
|
|
282
|
+
picker.activeTintColor = .systemTeal
|
|
283
|
+
picker.prioritizesVideoDevices = true
|
|
284
|
+
return picker
|
|
309
285
|
}
|
|
310
286
|
```
|
|
311
287
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
`AVPlayer` allows external playback by default. Leave it enabled for AirPlay,
|
|
315
|
-
or set it explicitly when code elsewhere may disable it:
|
|
316
|
-
|
|
317
|
-
```swift
|
|
318
|
-
player.allowsExternalPlayback = true
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
Set `usesExternalPlaybackWhileExternalScreenIsActive` only when you want the
|
|
322
|
-
player to automatically switch to external playback while an external screen
|
|
323
|
-
mode is active.
|
|
324
|
-
|
|
325
|
-
## Transport Controls and Playback Speed
|
|
326
|
-
|
|
327
|
-
### Custom Playback Speeds
|
|
328
|
-
|
|
329
|
-
Provide user-selectable playback speeds in the player UI:
|
|
330
|
-
|
|
331
|
-
```swift
|
|
332
|
-
let playerVC = AVPlayerViewController()
|
|
333
|
-
playerVC.speeds = [
|
|
334
|
-
AVPlaybackSpeed(rate: 0.5, localizedName: "Half Speed"),
|
|
335
|
-
AVPlaybackSpeed(rate: 1.0, localizedName: "Normal"),
|
|
336
|
-
AVPlaybackSpeed(rate: 1.5, localizedName: "1.5x"),
|
|
337
|
-
AVPlaybackSpeed(rate: 2.0, localizedName: "Double Speed")
|
|
338
|
-
]
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
Use `AVPlaybackSpeed.systemDefaultSpeeds` to restore the default speed options.
|
|
342
|
-
|
|
343
|
-
### Skipping and Seeking
|
|
344
|
-
|
|
345
|
-
On iOS, use the standard transport controls and `AVPlayer.seek(...)` for custom
|
|
346
|
-
app controls. `AVPlayerViewController` skipping behavior APIs such as
|
|
347
|
-
`isSkipForwardEnabled`, `isSkipBackwardEnabled`, and `skippingBehavior` are
|
|
348
|
-
tvOS-focused; keep them out of iOS player implementations.
|
|
349
|
-
|
|
350
|
-
### Now Playing Integration
|
|
351
|
-
|
|
352
|
-
`AVPlayerViewController` updates `MPNowPlayingInfoCenter` automatically by
|
|
353
|
-
default. Disable this if you manage Now Playing info manually:
|
|
288
|
+
`prioritizesVideoDevices = true` lists video-capable receivers first.
|
|
354
289
|
|
|
355
|
-
|
|
356
|
-
playerVC.updatesNowPlayingInfoCenter = false
|
|
357
|
-
```
|
|
290
|
+
### External playback
|
|
358
291
|
|
|
359
|
-
|
|
292
|
+
- `AVPlayer.allowsExternalPlayback` is `true` by default. Leave it on for
|
|
293
|
+
AirPlay, or set it explicitly if other code might have turned it off.
|
|
294
|
+
- `usesExternalPlaybackWhileExternalScreenIsActive` is only for making the
|
|
295
|
+
player switch to external playback automatically while an external screen is
|
|
296
|
+
in use. It is not something AirPlay generally needs.
|
|
360
297
|
|
|
361
|
-
|
|
362
|
-
contains appropriate text tracks. Users control subtitle preferences in
|
|
363
|
-
Settings > Accessibility > Subtitles & Captioning.
|
|
298
|
+
## Transport controls and speed
|
|
364
299
|
|
|
365
|
-
###
|
|
300
|
+
### Custom speeds
|
|
366
301
|
|
|
367
302
|
```swift
|
|
368
|
-
|
|
303
|
+
import AVKit
|
|
369
304
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
305
|
+
@MainActor
|
|
306
|
+
func offerLectureSpeeds(_ viewer: AVPlayerViewController) {
|
|
307
|
+
viewer.speeds = [
|
|
308
|
+
AVPlaybackSpeed(rate: 0.75, localizedName: "0.75x"),
|
|
309
|
+
AVPlaybackSpeed(rate: 1, localizedName: "1x"),
|
|
310
|
+
AVPlaybackSpeed(rate: 1.25, localizedName: "1.25x"),
|
|
311
|
+
AVPlaybackSpeed(rate: 1.75, localizedName: "1.75x")
|
|
312
|
+
]
|
|
375
313
|
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
`allowedSubtitleOptionLanguages`, `requiresFullSubtitles`, and the
|
|
379
|
-
`AVPlayerViewControllerDelegate` media-selection callback are tvOS-only. For iOS,
|
|
380
|
-
load the asset's `.legible` media selection group and select an option on the
|
|
381
|
-
`AVPlayerItem` when the app needs a default.
|
|
382
|
-
|
|
383
|
-
### Providing Subtitle Tracks in HLS
|
|
384
314
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
## Common Mistakes
|
|
391
|
-
|
|
392
|
-
### DON'T: Subclass AVPlayerViewController
|
|
393
|
-
|
|
394
|
-
Apple explicitly states this is unsupported. It may cause undefined behavior or
|
|
395
|
-
crash on future OS versions.
|
|
396
|
-
|
|
397
|
-
```swift
|
|
398
|
-
// WRONG
|
|
399
|
-
class MyPlayerVC: AVPlayerViewController { } // Unsupported
|
|
400
|
-
|
|
401
|
-
// CORRECT: Use composition with delegation
|
|
402
|
-
let playerVC = AVPlayerViewController()
|
|
403
|
-
playerVC.delegate = coordinator
|
|
315
|
+
@MainActor
|
|
316
|
+
func restoreDefaultSpeeds(_ viewer: AVPlayerViewController) {
|
|
317
|
+
viewer.speeds = AVPlaybackSpeed.systemDefaultSpeeds
|
|
318
|
+
}
|
|
404
319
|
```
|
|
405
320
|
|
|
406
|
-
###
|
|
321
|
+
### Skipping and seeking
|
|
407
322
|
|
|
408
|
-
|
|
409
|
-
|
|
323
|
+
On iOS rely on the standard transport controls, and use `AVPlayer.seek(...)`
|
|
324
|
+
behind any custom buttons. `isSkipForwardEnabled`, `isSkipBackwardEnabled` and
|
|
325
|
+
`skippingBehavior` are tvOS APIs; keep them out of iOS code.
|
|
410
326
|
|
|
411
|
-
|
|
412
|
-
// WRONG: Default audio session
|
|
413
|
-
let playerVC = AVPlayerViewController()
|
|
414
|
-
playerVC.player = player // PiP won't work
|
|
415
|
-
|
|
416
|
-
// CORRECT: Configure the category, then activate when playback starts
|
|
417
|
-
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
|
|
418
|
-
try AVAudioSession.sharedInstance().setActive(true)
|
|
419
|
-
let playerVC = AVPlayerViewController()
|
|
420
|
-
playerVC.player = player
|
|
421
|
-
```
|
|
327
|
+
### Now Playing
|
|
422
328
|
|
|
423
|
-
|
|
329
|
+
`AVPlayerViewController` updates `MPNowPlayingInfoCenter` by default. Set
|
|
330
|
+
`updatesNowPlayingInfoCenter = false` if the app manages Now Playing itself.
|
|
424
331
|
|
|
425
|
-
|
|
426
|
-
system cannot return the user to your player. Failing to call
|
|
427
|
-
`completionHandler(true)` leaves the system in an inconsistent state.
|
|
332
|
+
## Subtitles and closed captions
|
|
428
333
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
// CORRECT
|
|
434
|
-
func playerViewController(
|
|
435
|
-
_ playerViewController: AVPlayerViewController,
|
|
436
|
-
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
|
|
437
|
-
) {
|
|
438
|
-
present(playerViewController, animated: false) {
|
|
439
|
-
completionHandler(true)
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### DON'T: Create AVPlayer in a SwiftUI view's init
|
|
445
|
-
|
|
446
|
-
Creating the player eagerly causes performance issues. SwiftUI may recreate the
|
|
447
|
-
view multiple times.
|
|
334
|
+
When the media has text tracks, AVKit shows subtitles and captions
|
|
335
|
+
automatically according to the user's choices in Settings > Accessibility >
|
|
336
|
+
Subtitles & Captioning.
|
|
448
337
|
|
|
449
338
|
```swift
|
|
450
|
-
|
|
451
|
-
struct PlayerView: View {
|
|
452
|
-
let player = AVPlayer(url: videoURL) // Re-created on every view evaluation
|
|
453
|
-
|
|
454
|
-
var body: some View { VideoPlayer(player: player) }
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// CORRECT: Use @State and defer creation
|
|
458
|
-
struct PlayerView: View {
|
|
459
|
-
@State private var player: AVPlayer?
|
|
339
|
+
import AVFoundation
|
|
460
340
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
341
|
+
@MainActor
|
|
342
|
+
func chooseSubtitles(language code: String, for item: AVPlayerItem) async throws {
|
|
343
|
+
let media = item.asset
|
|
344
|
+
guard let legible = try await media.loadMediaSelectionGroup(for: .legible) else { return }
|
|
345
|
+
let match = legible.options.first { $0.locale?.language.languageCode?.identifier == code }
|
|
346
|
+
item.select(match, in: legible)
|
|
465
347
|
}
|
|
466
348
|
```
|
|
467
349
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
-
|
|
472
|
-
|
|
350
|
+
- `allowedSubtitleOptionLanguages`, `requiresFullSubtitles` and the delegate
|
|
351
|
+
callback for media selection exist only on tvOS. On iOS, select within the item's
|
|
352
|
+
`.legible` group as above.
|
|
353
|
+
- HLS carries subtitles and captions in its manifests, and AVKit reads them
|
|
354
|
+
from the `AVAsset`'s `AVMediaSelectionGroup`.
|
|
355
|
+
- Local files need the legible tracks already inside the file, or authored
|
|
356
|
+
into the asset.
|
|
357
|
+
|
|
358
|
+
## Common mistakes
|
|
359
|
+
|
|
360
|
+
- **Subclassing `AVPlayerViewController`.** Apple does not support it and it
|
|
361
|
+
can break on a future OS. Compose it and use the delegate.
|
|
362
|
+
- **Leaving the default audio session.** PiP will not work. Set
|
|
363
|
+
`.playback` with `.moviePlayback`, and activate when playback begins.
|
|
364
|
+
- **No PiP restore handler, or no `completionHandler(true)`.** Without the
|
|
365
|
+
handler the user cannot get back to the player; without the completion call
|
|
366
|
+
the restore does nothing or the animation hangs.
|
|
367
|
+
- **`AVPlayer` as a stored property of a SwiftUI view.** It is recreated every
|
|
368
|
+
time the view value is rebuilt. Hold an optional in `@State` and create it in
|
|
369
|
+
`.task`.
|
|
370
|
+
|
|
371
|
+
## Review checklist
|
|
372
|
+
|
|
373
|
+
- [ ] Audio session is `.playback` with `.moviePlayback`
|
|
374
|
+
- [ ] The session is activated only when playback starts
|
|
375
|
+
- [ ] `UIBackgroundModes` contains `audio`
|
|
473
376
|
- [ ] `AVPlayerViewController` is not subclassed
|
|
474
|
-
- [ ] PiP
|
|
475
|
-
- [ ] PiP restore delegate method
|
|
476
|
-
- [ ] Custom PiP checks
|
|
477
|
-
- [ ] Custom PiP starts only from explicit user
|
|
478
|
-
- [ ] `AVPlayer`
|
|
479
|
-
- [ ] `canStartPictureInPictureAutomaticallyFromInline`
|
|
480
|
-
- [ ] `requiresLinearPlayback`
|
|
481
|
-
- [ ] tvOS-only skipping APIs are
|
|
482
|
-
- [ ] External playback is not
|
|
483
|
-
- [ ] Subtitle selection tested with
|
|
484
|
-
- [ ]
|
|
485
|
-
- [ ] `isReadyForDisplay`
|
|
486
|
-
- [ ]
|
|
377
|
+
- [ ] PiP was tried with real, supported video media
|
|
378
|
+
- [ ] The PiP restore delegate method exists and calls `completionHandler(true)`
|
|
379
|
+
- [ ] Custom PiP checks device support and `isPictureInPicturePossible`
|
|
380
|
+
- [ ] Custom PiP starts only from an explicit user action
|
|
381
|
+
- [ ] SwiftUI creates the `AVPlayer` in `.task`
|
|
382
|
+
- [ ] Inline players set `canStartPictureInPictureAutomaticallyFromInline`
|
|
383
|
+
- [ ] `requiresLinearPlayback` is `true` only while a mandatory ad or legal notice plays
|
|
384
|
+
- [ ] tvOS-only skipping APIs are absent from iOS code
|
|
385
|
+
- [ ] External playback is not switched off where AirPlay is expected
|
|
386
|
+
- [ ] Subtitle selection was tested with media that has real text tracks
|
|
387
|
+
- [ ] `videoGravity` suits the content
|
|
388
|
+
- [ ] `isReadyForDisplay` gates when the player appears
|
|
389
|
+
- [ ] Streaming errors (HLS failures, timeouts) are handled
|
|
487
390
|
|
|
488
391
|
## References
|
|
489
392
|
|
|
490
|
-
-
|
|
491
|
-
- [AVKit
|
|
492
|
-
- [AVPlayerViewController](https://
|
|
493
|
-
- [VideoPlayer
|
|
494
|
-
- [AVPictureInPictureController](https://
|
|
495
|
-
- [AVRoutePickerView](https://
|
|
496
|
-
- [AVPlaybackSpeed](https://
|
|
497
|
-
- [
|
|
498
|
-
- [
|
|
499
|
-
- [
|
|
393
|
+
- [AVKit patterns](references/avkit-patterns.md) - custom AVPlayerLayer player with PiP, sample-buffer PiP, interstitials, background playback, errors, playback observation, frame analysis, HDR, SwiftUI player model, representable wrapper
|
|
394
|
+
- [AVKit](https://developer.apple.com/documentation/avkit)
|
|
395
|
+
- [AVPlayerViewController](https://developer.apple.com/documentation/avkit/avplayerviewcontroller)
|
|
396
|
+
- [VideoPlayer](https://developer.apple.com/documentation/avkit/videoplayer)
|
|
397
|
+
- [AVPictureInPictureController](https://developer.apple.com/documentation/avkit/avpictureinpicturecontroller)
|
|
398
|
+
- [AVRoutePickerView](https://developer.apple.com/documentation/avkit/avroutepickerview)
|
|
399
|
+
- [AVPlaybackSpeed](https://developer.apple.com/documentation/avkit/avplaybackspeed)
|
|
400
|
+
- [Apple guide: media playback configuration](https://developer.apple.com/documentation/avfoundation/configuring-your-app-for-media-playback)
|
|
401
|
+
- [Apple guide: PiP in the standard player](https://developer.apple.com/documentation/avkit/adopting-picture-in-picture-in-a-standard-player)
|
|
402
|
+
- [Apple guide: video in the standard interface](https://developer.apple.com/documentation/avkit/playing-video-content-in-a-standard-user-interface)
|