Skip to content

Instantly share code, notes, and snippets.

@kshivang
Last active March 30, 2023 19:03
Show Gist options
  • Save kshivang/4c213ec85adf911d30f1305722e7129d to your computer and use it in GitHub Desktop.
Save kshivang/4c213ec85adf911d30f1305722e7129d to your computer and use it in GitHub Desktop.
Combine wrapper over AVPlayer.addPeriodicTimeObserver method. Use AVPlayer.periodicTimePublisher method instead.
import AVFoundation
import Combine
// simply use
// player.periodicTimePublisher()
// .receive(on: RunLoop.main)
// .assign(to: \SomeClass.elapsedTime, on: someInstance)
// .store(in: &cancelBag)
extension AVPlayer {
func periodicTimePublisher(forInterval interval: CMTime = CMTime(seconds: 0.5, preferredTimescale: CMTimeScale(NSEC_PER_SEC))) -> AnyPublisher<CMTime, Never> {
Publisher(self, forInterval: interval)
.eraseToAnyPublisher()
}
}
fileprivate extension AVPlayer {
private struct Publisher: Combine.Publisher {
typealias Output = CMTime
typealias Failure = Never
var player: AVPlayer
var interval: CMTime
init(_ player: AVPlayer, forInterval interval: CMTime) {
self.player = player
self.interval = interval
}
func receive<S>(subscriber: S) where S : Subscriber, Publisher.Failure == S.Failure, Publisher.Output == S.Input {
let subscription = CMTime.Subscription(subscriber: subscriber, player: player, forInterval: interval)
subscriber.receive(subscription: subscription)
}
}
}
fileprivate extension CMTime {
final class Subscription<SubscriberType: Subscriber>: Combine.Subscription where SubscriberType.Input == CMTime, SubscriberType.Failure == Never {
var player: AVPlayer? = nil
var observer: Any? = nil
init(subscriber: SubscriberType, player: AVPlayer, forInterval interval: CMTime) {
self.player = player
observer = player.addPeriodicTimeObserver(forInterval: interval, queue: nil) { time in
_ = subscriber.receive(time)
}
}
func request(_ demand: Subscribers.Demand) {
// We do nothing here as we only want to send events when they occur.
// See, for more info: https://developer.apple.com/documentation/combine/subscribers/demand
}
func cancel() {
if let observer = observer {
player?.removeTimeObserver(observer)
}
observer = nil
player = nil
}
}
}
@okmyself
Copy link

okmyself commented Aug 9, 2022

Is there any way to achieve a similar function for AVAudioPlayer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment