AVPlayer Lock Screen updates stop working, only when another AVPlayer instance is played in a subsequent view controller

Refresh

2 weeks ago

Views

5 time

0

I have an appliction with View Controllers A and B. View controller A is the presenter of view controller B (pushes it using a navigation controller). Both A and B have a seperate AVPlayer inside them (A's plays audio. B's plays video. Both local files from docs directory).

VC A configures it's AVPlayer to update the Lock Screen with the Artwork Image, progress status, forward 15s, back 15s buttons. Everything works great if only view controller A's player is used to play audio - no matter how many times I go back and forward to it (I.E. VC B's player is never used to play()). (The whole controller gets deinit correctly) The Lock Screen always works, and always updates all required playback information.

VC A (AVPlayer1) --> PUSH --> VC B (AVPlayer2)

This is the code to register for Lock Screen updates (including button and progress callbacks):

func setupNowPlaying() {
    var nowPlayingInfo = [String : Any]()
    nowPlayingInfo[MPMediaItemPropertyTitle] = title
    nowPlayingInfo[MPMediaItemPropertyArtist] = artist

    let artwork = MPMediaItemArtwork.init(boundsSize: self.artImage.size, requestHandler: { (size) -> UIImage in
        return self.artImage
    })
    nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork

    guard let player = self.player else { return }
    guard let currentItem = self.player?.currentItem else { return }

    nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentItem.asset.duration.seconds
    nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
    nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentItem.currentTime().seconds

    let rcc = MPRemoteCommandCenter.shared()
    let skipBackwardCommand = rcc.skipBackwardCommand
    skipBackwardCommand.isEnabled = true
    skipBackwardCommand.addTarget(handler: skipBackward)
    skipBackwardCommand.preferredIntervals = [15]

    let skipForwardCommand = rcc.skipForwardCommand
    skipForwardCommand.isEnabled = true
    skipForwardCommand.addTarget(handler: skipForward)
    skipForwardCommand.preferredIntervals = [15]

    UIApplication.shared.beginReceivingRemoteControlEvents()

    MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}

Additionally, the following is hooked up in AppDelegate (didFinishLaunchingWithOptions), to enable background playback (standard stuff):

   do {
        try AVAudioSession.sharedInstance().setActive(true, options: [])
        try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback,
                                                        mode: AVAudioSession.Mode.default, options: [])
    } catch {
        print("Error setting up lock screen playback")
    }

The problem of Not showing anything on the lock screen starts to occur when VC B gets presented and plays video using it's own seperate AVPlayer. VC A's AVPlayer is first paused. If I call .play() on VC B's player then when I come back to VC A (by clicking Back < in the nav bar), the progress updates to the Lock Screen will never show up again (the whole playback UI doesn't show up), however the background playback still works.

So as soon as I use another AVPlayer on another View Controller, my original player will never be able to post updates to the Lock Screen UI, ever again, until the application is terminated and re-launched.

  • I tried to do full cleanup and re-init on the player in VC A.
  • I tried to just not call .play() on VC B. (Everything works fine with VC A in that case).
  • VC B's player is not registered for Lock Screen updates, because it just plays video in the app for preview purposes.

Something under-the-hood with Apple's AVPlayer breaks VC A's AVPlayer's Lock Screen updates capability, as soon as another instance of AVPlayer is introduced and used to play in another View Controller.

I know the issue is related to the second AVPlayer on the separate controller, because just not calling .play() on VC B's player, fixes the screen lock update disappearance issue on VC A's player.

How can I work around this? I don't want to remove VC A's player from superview, and pass it to VC B, and reconfigure it, then reconfigure it back again in VC A if the user comes back. (very laborious and error prone). It also currently bricks Lock Screen updates permanently, after the first time the user goes through VC B's playback (which is part of my happy path)

Thanks in advance, Alex

0 answers