AVKit
High-level media playback UI built on AVFoundation. Provides system-standard
video players, Picture-in-Picture, AirPlay routing, transport controls, and
subtitle/caption display. Targets Swift 6.3 / iOS 26+.
Contents
Workflow
- Define playback ownership, media source, audio-session policy, background behavior, and supported system controls.
- Choose
AVPlayerViewController for full system playback UI or SwiftUI VideoPlayer for simpler embedding.
- Keep
AVPlayer in stable state outside transient view initialization and observe readiness/errors deliberately.
- Configure Picture in Picture, AirPlay, Now Playing, subtitles, and seeking only when the product needs them.
- Verify interruptions, route changes, foreground/background transitions, PiP restoration, captions, and teardown.
Route by Task
- Read core implementation details for audio session, player controllers,
VideoPlayer, PiP, AirPlay, controls, and subtitles.
- Read extended AVKit patterns for custom player UI, interstitials, background playback, error handling, and advanced hosting.
Core Decisions
- Prefer system playback UI and delegation over subclassing
AVPlayerViewController.
- Configure the audio session for the intended playback/background contract.
- Complete every PiP restoration callback and retain objects required by PiP.
- Keep player identity and observation lifetime stable across SwiftUI body updates.
Common Mistakes
DON'T: Subclass AVPlayerViewController
Apple explicitly states this is unsupported. It may cause undefined behavior or
crash on future OS versions.
// WRONG
class MyPlayerVC: AVPlayerViewController { } // Unsupported
// CORRECT: Use composition with delegation
let playerVC = AVPlayerViewController()
playerVC.delegate = coordinator
DON'T: Skip audio session configuration for PiP
PiP and background playback depend on the playback audio session category and
the Audio, AirPlay, and Picture in Picture background mode.
// WRONG: Default audio session
let playerVC = AVPlayerViewController()
playerVC.player = player // PiP won't work
// CORRECT: Configure the category, then activate when playback starts
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
try AVAudioSession.sharedInstance().setActive(true)
let playerVC = AVPlayerViewController()
playerVC.player = player
DON'T: Forget the PiP restore delegate or its completion handler
Without restoreUserInterfaceForPictureInPictureStopWithCompletionHandler, the
system cannot return the user to your player. Failing to call
completionHandler(true) leaves the system in an inconsistent state.
// WRONG: No delegate method or missing completionHandler call
// User taps restore in PiP -> nothing happens or animation hangs
// CORRECT
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
present(playerViewController, animated: false) {
completionHandler(true)
}
}
DON'T: Create AVPlayer in a SwiftUI view's init
Creating the player eagerly causes performance issues. SwiftUI may recreate the
view multiple times.
// WRONG: Created on every view init
struct PlayerView: View {
let player = AVPlayer(url: videoURL) // Re-created on every view evaluation
var body: some View { VideoPlayer(player: player) }
}
// CORRECT: Use @State and defer creation
struct PlayerView: View {
@State private var player: AVPlayer?
var body: some View {
VideoPlayer(player: player)
.task { player = AVPlayer(url: videoURL) }
}
}
Review Checklist
References
1---2name: avkit3description: Create media playback experiences using AVKit. Use when adding video players with AVPlayerViewController, enabling Picture-in-Picture, routing media with AirPlay, using SwiftUI VideoPlayer views, configuring transport controls, displaying subtitles and closed captions, or integrating AVFoundation playback with system UI.4---56# AVKit78High-level media playback UI built on AVFoundation. Provides system-standard9video players, Picture-in-Picture, AirPlay routing, transport controls, and10subtitle/caption display. Targets Swift 6.3 / iOS 26+.1112## Contents1314- [Workflow](#workflow)15- [Route by Task](#route-by-task)16- [Core Decisions](#core-decisions)17- [Common Mistakes](#common-mistakes)18- [Review Checklist](#review-checklist)19- [References](#references)2021## Workflow22231. Define playback ownership, media source, audio-session policy, background behavior, and supported system controls.242. Choose `AVPlayerViewController` for full system playback UI or SwiftUI `VideoPlayer` for simpler embedding.253. Keep `AVPlayer` in stable state outside transient view initialization and observe readiness/errors deliberately.264. Configure Picture in Picture, AirPlay, Now Playing, subtitles, and seeking only when the product needs them.275. Verify interruptions, route changes, foreground/background transitions, PiP restoration, captions, and teardown.2829## Route by Task3031- Read [core implementation details](references/core-implementation.md) for audio session, player controllers, `VideoPlayer`, PiP, AirPlay, controls, and subtitles.32- Read [extended AVKit patterns](references/avkit-patterns.md) for custom player UI, interstitials, background playback, error handling, and advanced hosting.3334## Core Decisions3536- Prefer system playback UI and delegation over subclassing `AVPlayerViewController`.37- Configure the audio session for the intended playback/background contract.38- Complete every PiP restoration callback and retain objects required by PiP.39- Keep player identity and observation lifetime stable across SwiftUI body updates.4041## Common Mistakes4243### DON'T: Subclass AVPlayerViewController4445Apple explicitly states this is unsupported. It may cause undefined behavior or46crash on future OS versions.4748```swift49// WRONG50class MyPlayerVC: AVPlayerViewController { } // Unsupported5152// CORRECT: Use composition with delegation53let playerVC = AVPlayerViewController()54playerVC.delegate = coordinator55```5657### DON'T: Skip audio session configuration for PiP5859PiP and background playback depend on the playback audio session category and60the Audio, AirPlay, and Picture in Picture background mode.6162```swift63// WRONG: Default audio session64let playerVC = AVPlayerViewController()65playerVC.player = player // PiP won't work6667// CORRECT: Configure the category, then activate when playback starts68try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)69try AVAudioSession.sharedInstance().setActive(true)70let playerVC = AVPlayerViewController()71playerVC.player = player72```7374### DON'T: Forget the PiP restore delegate or its completion handler7576Without `restoreUserInterfaceForPictureInPictureStopWithCompletionHandler`, the77system cannot return the user to your player. Failing to call78`completionHandler(true)` leaves the system in an inconsistent state.7980```swift81// WRONG: No delegate method or missing completionHandler call82// User taps restore in PiP -> nothing happens or animation hangs8384// CORRECT85func playerViewController(86 _ playerViewController: AVPlayerViewController,87 restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void88) {89 present(playerViewController, animated: false) {90 completionHandler(true)91 }92}93```9495### DON'T: Create AVPlayer in a SwiftUI view's init9697Creating the player eagerly causes performance issues. SwiftUI may recreate the98view multiple times.99100```swift101// WRONG: Created on every view init102struct PlayerView: View {103 let player = AVPlayer(url: videoURL) // Re-created on every view evaluation104105 var body: some View { VideoPlayer(player: player) }106}107108// CORRECT: Use @State and defer creation109struct PlayerView: View {110 @State private var player: AVPlayer?111112 var body: some View {113 VideoPlayer(player: player)114 .task { player = AVPlayer(url: videoURL) }115 }116}117```118119## Review Checklist120121- [ ] Audio session category set to `.playback` with `mode: .moviePlayback`122- [ ] Audio session activation deferred until playback begins123- [ ] Audio, AirPlay, and Picture in Picture background mode added to `UIBackgroundModes`124- [ ] `AVPlayerViewController` is not subclassed125- [ ] PiP tested with supported video media, not only app/device setup126- [ ] PiP restore delegate method implemented and calls `completionHandler(true)`127- [ ] Custom PiP checks both device support and current `isPictureInPicturePossible`128- [ ] Custom PiP starts only from explicit user interaction129- [ ] `AVPlayer` deferred to `.task` in SwiftUI (not created eagerly)130- [ ] `canStartPictureInPictureAutomaticallyFromInline` set for inline players131- [ ] `requiresLinearPlayback` toggled only during required ad/legal segments132- [ ] tvOS-only skipping APIs are not used for iOS transport controls133- [ ] External playback is not disabled accidentally when AirPlay is required134- [ ] Subtitle selection tested with actual media tracks135- [ ] Video gravity set appropriately (`.resizeAspect` vs `.resizeAspectFill`)136- [ ] `isReadyForDisplay` observed before showing the player view137- [ ] Error handling for network-streamed content (HLS failures, timeouts)138139## References140141- Advanced patterns (custom player UI, interstitials, background playback, error handling): [references/avkit-patterns.md](references/avkit-patterns.md)142- [AVKit framework](https://sosumi.ai/documentation/avkit)143- [AVPlayerViewController](https://sosumi.ai/documentation/avkit/avplayerviewcontroller)144- [VideoPlayer (SwiftUI)](https://sosumi.ai/documentation/avkit/videoplayer)145- [AVPictureInPictureController](https://sosumi.ai/documentation/avkit/avpictureinpicturecontroller)146- [AVRoutePickerView](https://sosumi.ai/documentation/avkit/avroutepickerview)147- [AVPlaybackSpeed](https://sosumi.ai/documentation/avkit/avplaybackspeed)148- [Configuring your app for media playback](https://sosumi.ai/documentation/avfoundation/configuring-your-app-for-media-playback)149- [Adopting Picture in Picture in a Standard Player](https://sosumi.ai/documentation/avkit/adopting-picture-in-picture-in-a-standard-player)150- [Playing video content in a standard user interface](https://sosumi.ai/documentation/avkit/playing-video-content-in-a-standard-user-interface)151- [Core implementation details](references/core-implementation.md) -- setup, API wiring, and focused implementation recipes moved out of the entrypoint.