diff --git a/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift b/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift index 510df76bc..cb1603404 100644 --- a/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift +++ b/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift @@ -20,7 +20,6 @@ class AudioContainerViewModel { audioView.playButton.publisher(for: .touchUpInside) .sink { _ in - if audioAttachment === AudioPlayer.shared.attachment { if AudioPlayer.shared.isPlaying() { AudioPlayer.shared.pause() @@ -53,16 +52,26 @@ class AudioContainerViewModel { audioAttachment: Attachment ) { let audioView = cell.statusView.audioView + var lastCurrentTimeSubject: TimeInterval? AudioPlayer.shared.currentTimeSubject - .receive(on: DispatchQueue.main) - .filter { _ in - audioAttachment === AudioPlayer.shared.attachment - } - .sink(receiveValue: { time in - audioView.timeLabel.text = time.asString(style: .positional) - if let duration = audioAttachment.meta?.original?.duration, !audioView.slider.isTracking { - audioView.slider.setValue(Float(time / duration), animated: true) + .throttle(for: 0.33, scheduler: DispatchQueue.main, latest: true) + .compactMap { time -> (TimeInterval, Float)? in + defer { + lastCurrentTimeSubject = time } + guard audioAttachment === AudioPlayer.shared.attachment else { return nil } + guard let duration = audioAttachment.meta?.original?.duration else { return nil } + + if let lastCurrentTimeSubject = lastCurrentTimeSubject, time != 0.0 { + guard abs(time - lastCurrentTimeSubject) < 0.5 else { return nil } // debounce + } + + guard !audioView.slider.isTracking else { return nil } + return (time, Float(time / duration)) + } + .sink(receiveValue: { time, progress in + audioView.timeLabel.text = time.asString(style: .positional) + audioView.slider.setValue(progress, animated: true) }) .store(in: &cell.disposeBag) AudioPlayer.shared.playbackState diff --git a/Mastodon/Service/AudioPlayer.swift b/Mastodon/Service/AudioPlayer.swift index 458ca7614..13479f0af 100644 --- a/Mastodon/Service/AudioPlayer.swift +++ b/Mastodon/Service/AudioPlayer.swift @@ -18,14 +18,16 @@ final class AudioPlayer: NSObject { var timeObserver: Any? var statusObserver: Any? var attachment: Attachment? - var currentURL: URL? + let session = AVAudioSession.sharedInstance() let playbackState = CurrentValueSubject(PlaybackState.unknown) + + // MARK: - singleton public static let shared = AudioPlayer() let currentTimeSubject = CurrentValueSubject(0) - override init() { + private override init() { super.init() addObserver() } @@ -45,7 +47,7 @@ extension AudioPlayer { if audioAttachment == attachment { if self.playbackState.value == .stopped { - self.seekToTime(time: 0) + self.seekToTime(time: .zero) } player.play() self.playbackState.value = .playing @@ -97,12 +99,14 @@ extension AudioPlayer { case .unknown: self.playbackState.value = .unknown @unknown default: - fatalError() + assertionFailure() } }) .store(in: &disposeBag) NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: nil) - .sink { _ in + .sink { [weak self] _ in + guard let self = self else { return } + self.player.seek(to: .zero) self.playbackState.value = .stopped self.currentTimeSubject.value = 0 }