How can I check if my AVPlayer is buffering?

The accepted answer didn't work for me, I used the code below to show the loader efficiently.

Swift 3

//properties 
var observer:Any!
var player:AVPlayer!


self.observer = self.player.addPeriodicTimeObserver(forInterval: CMTimeMake(1, 600), queue: DispatchQueue.main) {
    [weak self] time in

    if self?.player.currentItem?.status == AVPlayerItemStatus.readyToPlay {

        if let isPlaybackLikelyToKeepUp = self?.player.currentItem?.isPlaybackLikelyToKeepUp {
            //do what ever you want with isPlaybackLikelyToKeepUp value, for example, show or hide a activity indicator.
        }
    }
}

You can observe the values of your player.currentItem:

playerItem.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .New, context: nil)
playerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .New, context: nil)
playerItem.addObserver(self, forKeyPath: "playbackBufferFull", options: .New, context: nil)

then

override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if object is AVPlayerItem {
        switch keyPath {
            case "playbackBufferEmpty":
               // Show loader

            case "playbackLikelyToKeepUp":
                // Hide loader

            case "playbackBufferFull":
                // Hide loader
        }
    }
}

For me above accepted answer didn't worked but this method does.You can use timeControlStatus but it is available only above iOS 10.

According to apple's official documentation

A status that indicates whether playback is currently in progress, paused indefinitely, or suspended while waiting for appropriate network conditions

Add this observer to the player.

player.addObserver(self, forKeyPath: “timeControlStatus”, options: [.old, .new], context: nil)

Then,Observe the changes in

func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)

method.Use below code inside above method

override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "timeControlStatus", let change = change, let newValue = change[NSKeyValueChangeKey.newKey] as? Int, let oldValue = change[NSKeyValueChangeKey.oldKey] as? Int {
        let oldStatus = AVPlayer.TimeControlStatus(rawValue: oldValue)
        let newStatus = AVPlayer.TimeControlStatus(rawValue: newValue)
        if newStatus != oldStatus {
            DispatchQueue.main.async {[weak self] in
                if newStatus == .playing || newStatus == .paused {
                    self?.loaderView.isHidden = true
                } else {
                    self?.loaderView.isHidden = false
                }
            }
        }
    }
}

This is tested on iOS 11 above with swift 4 and It is working.