rodskagg
rodskagg

Reputation: 3927

AVPlayer status is updated when app comes to foreground

I'm building a music player in my iOS app, using AVPlayer. I listen to changes for the AVPlayer.status property like this, to know when audio is ready to be played:

player.currentItem!.addObserver(self, forKeyPath: "status", options: .New, context: nil)

And when the status is .ReadyToPlay I automatically start playback:

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if (keyPath == "status") {
            if let currentItem = self.player?.currentItem {
                let status = currentItem.status
                if (status == .ReadyToPlay) {                    
                    self.play()
                }
            }
        }        
    }
}

Works great. The problem, however, is that if I start playing music in my app, pause the music and then leave the app and start playing music in, for example, Spotify, the AVPlayer's status property seems to be changed to .ReadyToPlay again the next time my app comes to the foreground, which causes the observer to fire, which in turn causes the music to start playing again.

I assume that something happens with the AVPlayer instance when the app gets focus again, which causes the status property to change/refresh.

How do I prevent this behaviour?

Upvotes: 3

Views: 1998

Answers (1)

ChrisH
ChrisH

Reputation: 4558

This seems like expected behavior. If you want to ensure that you only begin playback the first time the AVPlayerItem status changes, remove the observer after calling play().

One caveat here is that you should already be removing the observer when the currentItem is changed on the player, so you will need to use an additional flag to track whether you are observing the existing currentItem.

The owner of the player would keep track of state

var isObservingCurrentItem = false

And update/check that state when you add the observer

if currentItem = player.currentItem where isObservingCurrentItem {
    currentItem.removeObserver(self, forKeyPath:"status")
}

player.currentItem!.addObserver(self, forKeyPath: "status", options: .New, context: nil)
isObservingCurrentItem = true

Then you can safely remove the observer once the player is ready to play

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {

    if let object = object,
        keyPath = keyPath,
        currentItem = self.player?.currentItem,
        status = currentItem.status
        where status == .ReadyToPlay {
            self.play()
            object.removeObserver(self, forKeyPath:keyPath)
            isObservingCurrentItem = false
    }
}

Upvotes: 4

Related Questions