Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import AVFoundation
- /// The `PlayerLoader` class provides a callback mechanism for consumers of `AVPlayer` when loading a resource.
- /// AVFoundation works by an inconvenient series of APIs that rely on KVO for asynchronous operations. This class
- /// eliminates the boilerplate that KVO imposes and makes the callsite much more clean.
- public final class PlayerLoader: NSObject {
- public typealias Callback = (AVPlayer) -> ()
- /// The callback will initialize to a non-nil value. The callback is set to nil once it's invoked, at which time the
- /// KVO observation is removed. This is necessary to avoid double-removing the KVO observation.
- private var callback: Callback?
- private let player: AVPlayer
- // This is declared `var` because we have to take its pointer value
- private static var observationContext = NSObject()
- private let observationKeypath = #keyPath(AVPlayer.currentItem.status)
- init(player: AVPlayer, callback: @escaping Callback) {
- self.callback = callback
- self.player = player
- super.init()
- if self.player.currentItem?.status == AVPlayerItemStatus.readyToPlay {
- self.callback?(self.player)
- } else {
- self.player.addObserver(
- self,
- forKeyPath: self.observationKeypath,
- options: [NSKeyValueObservingOptions.new],
- context: &PlayerLoader.observationContext
- )
- }
- }
- public convenience init(url: URL, callback: @escaping Callback) {
- self.init(player: AVPlayer(url: url), callback: callback)
- }
- override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
- guard let context = context, context == &PlayerLoader.observationContext else {
- return
- }
- guard let keyPath = keyPath, keyPath == self.observationKeypath else {
- return
- }
- guard let currentItem = self.player.currentItem else {
- return
- }
- guard !CMTimeGetSeconds(currentItem.duration).isNaN else {
- return
- }
- guard currentItem.status == AVPlayerItemStatus.readyToPlay else {
- return
- }
- self.player.removeObserver(self, forKeyPath: self.observationKeypath)
- self.player.preroll(atRate: 1.0) { _ in
- self.callback?(self.player)
- self.callback = nil
- }
- }
- deinit {
- // This is really awkward but is one of the reasons why this class exists in the first place.
- if self.callback != nil {
- self.player.removeObserver(self, forKeyPath: self.observationKeypath)
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement