Skip to content

Instantly share code, notes, and snippets.

@kshivang
Last active April 11, 2025 15:13
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.

Revisions

  1. kshivang revised this gist Mar 11, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion AVPlayer+Combine.swift
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ import Combine
    // simply use
    // player.periodicTimePublisher()
    // .receive(on: RunLoop.main)
    // .assign(to: elapsedSec)
    // .assign(to: \SomeClass.elapsedTime, on: someInstance)
    // .store(in: &cancelBag)


  2. kshivang revised this gist Mar 11, 2020. No changes.
  3. kshivang revised this gist Mar 11, 2020. No changes.
  4. kshivang created this gist Mar 11, 2020.
    65 changes: 65 additions & 0 deletions AVPlayer+Combine.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,65 @@
    import AVFoundation
    import Combine

    // simply use
    // player.periodicTimePublisher()
    // .receive(on: RunLoop.main)
    // .assign(to: elapsedSec)
    // .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
    }
    }
    }