View difference between Paste ID: 57Q4BSj8 and ue3emfxu
SHOW: | | - or go back to the newest paste.
1-
import AVFoundation
1+
import AVFoundation
2-
2+
3-
/// The `PlayerLoader` class provides a callback mechanism for consumers of `AVPlayer` when loading a resource.
3+
/// The `PlayerLoader` class provides a callback mechanism for consumers of `AVPlayer` when loading a resource.
4-
/// AVFoundation works by an inconvenient series of APIs that rely on KVO for asynchronous operations. This class
4+
/// AVFoundation works by an inconvenient series of APIs that rely on KVO for asynchronous operations. This class
5-
/// eliminates the boilerplate that KVO imposes and makes the callsite much more clean.
5+
/// eliminates the boilerplate that KVO imposes and makes the callsite much more clean.
6-
public final class PlayerLoader: NSObject {
6+
public final class PlayerLoader: NSObject {
7-
    public typealias Callback = (AVPlayer) -> ()
7+
    public typealias Callback = (AVPlayer) -> ()
8-
8+
9-
    /// The callback will initialize to a non-nil value. The callback is set to nil once it's invoked, at which time the
9+
    /// The callback will initialize to a non-nil value. The callback is set to nil once it's invoked, at which time the
10-
    /// KVO observation is removed. This is necessary to avoid double-removing the KVO observation.
10+
    /// KVO observation is removed. This is necessary to avoid double-removing the KVO observation.
11-
    private var callback: Callback?
11+
    private var callback: Callback?
12-
12+
13-
    private let player: AVPlayer
13+
    private let player: AVPlayer
14-
14+
15-
    // This is declared `var` because we have to take its pointer value
15+
    // This is declared `var` because we have to take its pointer value
16-
    private static var observationContext = NSObject()
16+
    private static var observationContext = NSObject()
17-
    private let observationKeypath = #keyPath(AVPlayer.currentItem.status)
17+
    private let observationKeypath = #keyPath(AVPlayer.currentItem.status)
18-
18+
19-
    init(player: AVPlayer, callback: @escaping Callback) {
19+
    init(player: AVPlayer, callback: @escaping Callback) {
20-
        self.callback = callback
20+
        self.callback = callback
21-
        self.player = player
21+
        self.player = player
22-
22+
23-
        super.init()
23+
        super.init()
24-
24+
25-
        if self.player.currentItem?.status == AVPlayerItemStatus.readyToPlay {
25+
        if self.player.currentItem?.status == AVPlayerItemStatus.readyToPlay {
26-
            self.callback?(self.player)
26+
            self.callback?(self.player)
27-
        } else {
27+
        } else {
28-
            self.player.addObserver(
28+
            self.player.addObserver(
29-
                self,
29+
                self,
30-
                forKeyPath: self.observationKeypath,
30+
                forKeyPath: self.observationKeypath,
31-
                options: [NSKeyValueObservingOptions.new],
31+
                options: [NSKeyValueObservingOptions.new],
32-
                context: &PlayerLoader.observationContext
32+
                context: &PlayerLoader.observationContext
33-
            )
33+
            )
34-
        }
34+
        }
35-
    }
35+
    }
36-
36+
37-
    public convenience init(url: URL, callback: @escaping Callback) {
37+
    public convenience init(url: URL, callback: @escaping Callback) {
38-
        self.init(player: AVPlayer(url: url), callback: callback)
38+
        self.init(player: AVPlayer(url: url), callback: callback)
39-
    }
39+
    }
40-
40+
41-
    override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
41+
    override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
42-
        guard let context = context, context == &PlayerLoader.observationContext else {
42+
        guard let context = context, context == &PlayerLoader.observationContext else {
43-
            return
43+
            return
44-
        }
44+
        }
45-
45+
46-
        guard let keyPath = keyPath, keyPath == self.observationKeypath else {
46+
        guard let keyPath = keyPath, keyPath == self.observationKeypath else {
47-
            return
47+
            return
48-
        }
48+
        }
49-
49+
50-
        guard let currentItem = self.player.currentItem else {
50+
        guard let currentItem = self.player.currentItem else {
51-
            return
51+
            return
52-
        }
52+
        }
53-
53+
54-
        guard !CMTimeGetSeconds(currentItem.duration).isNaN else {
54+
        guard !CMTimeGetSeconds(currentItem.duration).isNaN else {
55-
            return
55+
            return
56-
        }
56+
        }
57-
57+
58-
        guard currentItem.status == AVPlayerItemStatus.readyToPlay else {
58+
        guard currentItem.status == AVPlayerItemStatus.readyToPlay else {
59-
            return
59+
            return
60-
        }
60+
        }
61-
61+
62-
        self.player.removeObserver(self, forKeyPath: self.observationKeypath)
62+
        self.player.removeObserver(self, forKeyPath: self.observationKeypath)
63-
63+
64-
        self.player.preroll(atRate: 1.0) { _ in
64+
        self.player.preroll(atRate: 1.0) { _ in
65-
            self.callback?(self.player)
65+
            self.callback?(self.player)
66-
            self.callback = nil
66+
            self.callback = nil
67-
        }
67+
        }
68-
    }
68+
    }
69-
69+
70-
    deinit {
70+
    deinit {
71-
        // This is really awkward but is one of the reasons why this class exists in the first place.
71+
        // This is really awkward but is one of the reasons why this class exists in the first place.
72-
        if self.callback != nil {
72+
        if self.callback != nil {
73-
            self.player.removeObserver(self, forKeyPath: self.observationKeypath)
73+
            self.player.removeObserver(self, forKeyPath: self.observationKeypath)
74-
        }
74+
        }
75-
    }
75+
    }
76
}