How to Keep AVPlayer Playing in the Background and Enable Picture in Picture (PiP) in Swift 5

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:

  1. Select the project.
  2. Select the application target.
  3. Open Signing & Capabilities.
  4. Add Background Modes.
  5. 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.

This article is inspired by real-world challenges we tackle in our projects. If you're looking for expert solutions or need a team to bring your idea to life,

Let's talk!

    Please fill your details, and we will contact you back

      Please fill your details, and we will contact you back