Dealing with expiring streams while the app is in the background

📘

Assumptions

Background playback is disabled (See PlaybackConfig.isBackgroundPlaybackEnabled)

Q: What happens when the app goes into the background while playing a stream?

When your app is in the background, AVPlayer continues to send segment requests (and manifest requests if the source is live) by default. If the app is resumed after being in the background for a significant time, and the stream source has since become invalid (e.g., expired CDN token or resource removal), the player will receive HTTP 4XX responses. This causes a playback error (SourceErrorEvent).

Q: How does this issue typically occur?

Here is a typical use-case for this problem:

  1. The user watches a stream successfully, receiving HTTP 200 status responses for segments.
  2. The user suspends the app by putting it into the background.
  3. After an extended period (e.g. 3 hours), the user resumes the app.
  4. The stream fails to resume playback due to HTTP 4XX responses for segments, likely caused by issues such as an expired CDN URL token.

Q: Why does playback fail when the app is resumed?

AVPlayer tries to continue fetching segments, but the source may no longer be valid due to:

  • Expired CDN URL tokens.
  • Stream resources being unavailable or removed.
  • A significant gap between the last playback request and the current time.

Since the player doesn’t receive valid HTTP 200 responses, it throws an error.

Q: How can I prevent playback errors when the app moves to the foreground?

To handle this seamlessly, you can reload a valid source when the app is resumed.

Solution

Use willEnterForegroundNotification to detect when the app is moving to the foreground. At this point, check if the stream source is still valid (e.g., verify token expiration). If the source is invalid, load a new, updated source to avoid errors.

Here’s how you can implement the solution:

    override func viewDidLoad() {
        super.viewDidLoad()
        // ...

        NotificationCenter.default.addObserver(
            forName: UIApplication.willEnterForegroundNotification,
            object: UIApplication.shared,
            queue: .main
        ) { [weak self] _ in
            let isTokenExpired = // Calculate if the URL token has expired
            guard isTokenExpired else { return }
            player.load(sourceConfig: sourceWithUpdatedURL)
        }
    }

Benefits of this approach

  • Prevents playback errors: avoid errors due to invalid HTTP responses.
  • Improves user experience: automatically refreshes the stream, ensuring seamless resumption of playback.