Background playback

With the background playback feature for iOS, you can adapt the player to play media content while other applications are in the foreground. You need to configure your iOS application to enable this background video playback behavior.

You must disconnect the main AVPlayer instance from the current context and store it in your AppDelegate.swift or SceneDelegate.swift file. When you set the FlowplayerView.avPlayer property using the FlowplayerView class, the player disables certain UI features and other processes that aren't needed in the background.

For information related to listening to events, managing media playback, and handling errors, see the following pages:

Enable background playback

This section describes the general process to enable background playback and provides an example of how to accomplish this task using the AppDelegate.swift or SceneDelegate.swift file. By default, the player's enableBackgroundPlayback property is set to false and the player pauses when receding into the background. For more, see the FlowplayerViewAPI properties.

General process

The general process to enable background video playback in your iOS application is outlined in the following steps. For a better understanding, see our example.

  1. Add a Background Modes capability for your iOS application.
  2. Specify the Background Modes your application requires. Select Audio, AirPlay, and Picture in Picture .
  3. Connect (foreground playback) or disconnect (background playback) the AVPlayer instance in the AppDelegate.swift or SceneDelegate.swift file.
  4. Enable the enableBackgroundPlayback property of the FlowplayerViewAPI protocol.
info

To learn more about enabling certain capabilities to perform background operations, see Configure background modes.

View an example

In this example, we set up a UIViewController subclass, then demonstrate how to manage background playback in the AppDelegate.swift or SceneDelegate.swift files. Pick the method that best suits your iOS application.

Warning

Never implement both strategies simultaneously because this will break the player.

  1. Start by creating a MyControllerView class that's a subclass of UIViewController . Within this new class, leverage the FlowplayerView class to create a flowplayerView instance and enable the enableBackgroundPlayback property. We can use this flowplayerView instance later in the AppDelegate.swift and SceneDelegate.swift files.
    Copy
    Copied
    class MyControllerView: UIViewController {
    
      // Declare view and instance of FlowplayerView class
      public let flowplayerView = FlowplayerView()
    
      // Handle view controller's lifecycle
      override func viewDidLoad() {
        super.viewDidLoad()
    
        view.addSubview(flowplayerView)
    
        let someMedia = MediaExternal(
          url: URL(string: "https://link.to.a.media.file")!
        )
    
        // Enable background playback
        flowplayerView.enableBackgroundPlayback = true
        flowplayerView.load(external: someMedia)
      }
    }
  2. Use the same MyControllerView subclass in the AppDelegate.swift file, enabling playback in the background or foreground. You can similarly handle the same MyControllerView subclass in the SceneDelegate.swift file. Switch between the tabs in the following examples to see each implementation.
    AppDelegate.swiftSceneDelegate.swift
    Copy
    Copied
    // AppDelegate.swift
    private var storedPlayerInstance: AVPlayer?
    
    func applicationDidEnterBackground(_ application: UIApplication) {
        let topController = window?.rootViewController as? MyControllerView
    
        savedPlayerInstance = topController?.flowplayerView.avPlayer
    
        // Disconnect main AVPlayer instance and
        // Continue playback in background
        topController?.videoController.avPlayer = nil
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        guard savedPlayerInstance != nil else { return }
    
        // Continue playback in foreground
        let topController = window?.rootViewController as? MyControllerView
    
        // Reconnect main AVPlayer instance
        topController?.flowplayerView.avPlayer = self.savedPlayerInstance
        self.savedPlayerInstance = nil
    }
    Copy
    Copied
    // SceneDelegate.swift
    var window: UIWindow?
    var storedPlayer: AVPlayer?
    
    func sceneWillEnterForeground(_: UIScene) {
      guard
        let navController = window?.rootViewController as? MyControllerView,
        let flowplayer = viewController.flowplayer
      else {
        return
      }
    
      // Continue playback in foreground
      flowplayer.avPlayer = storedPlayer
      storedPlayer = nil
    }
    
    func sceneDidEnterBackground(_: UIScene) {
      guard
        let navController = window?.rootViewController as? MyControllerView,
        let flowplayer = viewController.flowplayer
      else {
        return
      }
    
      storedPlayer = flowplayer.avPlayer
      // Continue playback in background
      flowplayer.avPlayer = nil
    }

Listen to the background state

By default, the enableBackgroundPlayback property of the player is set to false. This means that when the player moves to the background, it pauses. If it returns to the foreground, it doesn't automatically resume playing your media.

You can modify this behavior and change the video to play when the app containing the player returns to the foreground. To do so, you can observe the background playback state of the player and continue playback.

The following examples capture the background state and use it to resume video playback when the player returns from the background to the foreground. The first tab relies on the FlowplayerDelegate method, while the second tab demonstrates achieving the same goal using notifications.

FlowplayerDelegateNotificationCenter
Copy
Copied
// Use FlowplayerDelegate to observe playback state change
func player(_ player: FlowplayerAPI, didChangePlaybackState state: PlaybackState) {
  print("PlaybackState changed to: \(state)")

  // Check if current state is .playing and previous state is .background
  let wasInBackground = player.playbackStateList.prefix(2) == [
    .playing,
    .background
  ]

  // If was in background and transitioning to playing is true,
  // and the player is paused, continue playback when returning from the background
  if wasInBackground && player.state == .pause {
    player.play()
  }
}
Copy
Copied
// Declare player variable
var player: FlowplayerAPI?

// Register notification observer and method to be called when notification is received
NotificationCenter.default.addObserver(
  self,
  selector: #selector(onReceivedState),
  name: .flowplayerDidChangePlaybackState,
  object: nil
)

// Set up notification selector
// Call onReceivedState method when flowplayerDidChangePlaybackState notification is received
@objc
private func onReceivedState(_ notification: Notification) {
  guard let state = notification.object as? PlaybackState else {
    return
  }

  // Check if current state is .playing and previous state is .background
  let wasInBackground = player.playbackStateList.prefix(2) == [
    .playing,
    .background
  ]

  // If was in background and transitioning to playing is true,
  // and the player is paused, continue playback when returning from the background
  if wasInBackground && player.state == .pause {
    player.play()
  }

  print("myPlayer did change state to: \(state)")
}