Many modern iOS applications include audio and video streaming capabilities. Users expect media playback to continue when they lock their device, switch to another application, or use Picture in Picture (PiP) mode.
Developers often discover that while audio continues playing in the background, video playback stops when the application moves to the background. This behavior is expected unless the application is properly configured to support background audio and Picture in Picture.
This guide explains how to configure AVPlayer for background playback and Picture in Picture using Swift 5.
Understanding Background Playback
By default, iOS pauses most application activities when an app enters the background.
Media applications can continue playback if:
- The application enables Background Modes.
- The audio session is configured correctly.
- The media type supports background playback.
- Picture in Picture is properly configured for video content.
Configuring the Audio Session
The first step is configuring the audio session.
import AVFoundation
let audioSession = AVAudioSession.sharedInstance()
try? audioSession.setCategory(
.playback,
mode: .default,
options: [
.defaultToSpeaker,
.allowBluetooth,
.allowAirPlay
]
)
try? audioSession.setActive(true)
What This Does
The .playback category tells iOS that media playback should continue when the application enters the background.
Without this configuration:
- Audio stops when the app is minimized.
- Video playback is interrupted.
- Control Center media controls may not appear.
Enabling Background Audio Capability
Open the project settings in Xcode:
- Select the project.
- Select the application target.
- Open Signing & Capabilities.
- Add Background Modes.
- Enable:
Audio, AirPlay, and Picture in Picture
Without this capability, background playback will not work even if the audio session is configured correctly.
Detecting When the Application Enters Background
Applications often need to react when the user presses the Home button or switches to another application.
Register for background notifications:
NotificationCenter.default.addObserver(
self,
selector: #selector(appDidEnterBackground),
name: UIApplication.didEnterBackgroundNotification,
object: nil
)
Handle the notification:
@objc func appDidEnterBackground() {
print("Application entered background")
}
Why Video Stops but Audio Continues
A common scenario is:
AVPlayer
+
AVPlayerLayer
When the application enters the background:
- Audio can continue.
- Video rendering stops.
- The AVPlayerLayer is no longer displayed.
This is normal iOS behavior.
If video should remain visible outside the application, Picture in Picture must be used.
Introduction to Picture in Picture
Picture in Picture allows video playback to continue in a floating window while users interact with other applications.
Common examples include:
- Video streaming apps
- Educational platforms
- Podcast applications with video content
- Live television applications
Checking PiP Availability
Before enabling PiP, verify support:
if AVPictureInPictureController.isPictureInPictureSupported() {
print("PiP supported")
}
Creating a Picture in Picture Controller
Picture in Picture works with an existing AVPlayerLayer.
let playerLayer = AVPlayerLayer(player: player)
let pipController =
AVPictureInPictureController(playerLayer: playerLayer)
Common Mistake
Many developers attempt something similar:
let playerLayer = AVPlayerLayer(player: player)
let pipController =
AVPictureInPictureController(playerLayer: playerLayer)
pipController?.startPictureInPicture()
This often fails because:
- The playerLayer was never displayed.
- The playerLayer is not attached to the view hierarchy.
- The PiP controller is not retained.
- The video is not actively playing.
Correct PiP Setup
Store the controller as a property:
var pipController: AVPictureInPictureController?
Create it using the visible player layer:
playerLayer = AVPlayerLayer(player: player)
view.layer.addSublayer(playerLayer)
if AVPictureInPictureController.isPictureInPictureSupported() {
pipController = AVPictureInPictureController(
playerLayer: playerLayer
)
pipController?.delegate = self
}
Starting Picture in Picture
pipController?.startPictureInPicture()
The player must already be playing:
player.play()
PiP Delegate Methods
Implement delegate callbacks:
extension ViewController:
AVPictureInPictureControllerDelegate {
func pictureInPictureControllerDidStartPictureInPicture(
_ pictureInPictureController: AVPictureInPictureController
) {
print("PiP started")
}
func pictureInPictureControllerDidStopPictureInPicture(
_ pictureInPictureController: AVPictureInPictureController
) {
print("PiP stopped")
}
}
Automatically Entering PiP When App Goes Background
A common requirement is starting PiP automatically when the user leaves the application.
@objc func appDidEnterBackground() {
if pipController?.isPictureInPicturePossible == true {
pipController?.startPictureInPicture()
}
}
AVPlayer and Audio-Only Background Playback
Some applications stream video but only need audio in the background.
In this case:
player.play()
combined with:
AVAudioSession.Category.playback
is usually sufficient.
PiP is not required when the application behaves like a radio or podcast player.
Disabling Video Tracks
For file-based content, video tracks can be disabled.
Example:
let asset = playerItem.asset
for track in asset.tracks(withMediaType: .video) {
track.isEnabled = false
}
This approach is primarily useful for local assets.
Maintaining Playback Across Screen Lock
With:
AVAudioSession.Category.playback
and:
Background Modes
enabled, playback can continue when:
- Device screen locks
- User switches applications
- Control Center appears
Troubleshooting Checklist
If PiP does not start:
Verify PiP Support
AVPictureInPictureController
.isPictureInPictureSupported()
Verify Capability
Enable:
Audio, AirPlay, and Picture in Picture
Verify Player Layer
Ensure:
view.layer.addSublayer(playerLayer)
has been executed.
Verify Playback
player.play()
must already be active.
Retain the Controller
Wrong:
let pipController = ...
Correct:
self.pipController = ...
Complete Example
import UIKit
import AVFoundation
import AVKit
class ViewController: UIViewController {
var player: AVPlayer!
var playerLayer: AVPlayerLayer!
var pipController: AVPictureInPictureController?
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "VIDEO_URL")!
player = AVPlayer(url: url)
playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = view.bounds
view.layer.addSublayer(playerLayer)
if AVPictureInPictureController
.isPictureInPictureSupported() {
pipController =
AVPictureInPictureController(
playerLayer: playerLayer
)
}
player.play()
}
}
Conclusion
Background media playback and Picture in Picture are essential features for modern iOS media applications. While audio playback can continue in the background using AVAudioSession and Background Modes, video playback requires proper Picture in Picture configuration.
By correctly configuring the audio session, enabling the required capabilities, managing application lifecycle events, and properly initializing AVPictureInPictureController, developers can provide a seamless media experience that continues even when users leave the application.


