Skip to content

Commit

Permalink
Remove delegate pattern from RemoteCommandsHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamede1945 committed Sep 28, 2024
1 parent 3446956 commit 5253d31
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 58 deletions.
1 change: 0 additions & 1 deletion Features/AudioBannerFeature/AudioBannerBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public struct AudioBannerBuilder {
baseURL: container.filesAppHost,
downloader: container.downloadManager
),
remoteCommandsHandler: RemoteCommandsHandler(center: .shared()),
reciterListBuilder: ReciterListBuilder(),
advancedAudioOptionsBuilder: AdvancedAudioOptionsBuilder()
)
Expand Down
63 changes: 34 additions & 29 deletions Features/AudioBannerFeature/AudioBannerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public final class AudioBannerViewModel: ObservableObject {
recentRecitersService: RecentRecitersService,
audioPlayer: QuranAudioPlayer,
downloader: QuranAudioDownloader,
remoteCommandsHandler: RemoteCommandsHandler,
reciterListBuilder: ReciterListBuilder,
advancedAudioOptionsBuilder: AdvancedAudioOptionsBuilder
) {
Expand All @@ -59,24 +58,11 @@ public final class AudioBannerViewModel: ObservableObject {
self.recentRecitersService = recentRecitersService
self.audioPlayer = audioPlayer
self.downloader = downloader
self.remoteCommandsHandler = remoteCommandsHandler
self.reciterListBuilder = reciterListBuilder
self.advancedAudioOptionsBuilder = advancedAudioOptionsBuilder

let actions = QuranAudioPlayerActions(
playbackEnded: { [weak self] in self?.playbackEnded() },
playbackPaused: { [weak self] in self?.playbackPaused() },
playbackResumed: { [weak self] in self?.playbackResumed() },
playing: { [weak self] in self?.playing(ayah: $0) }
)
audioPlayer.setActions(actions)

remoteCommandsHandler.delegate = self
}

deinit {
remoteCommandsHandler.stopListening()
NotificationCenter.default.removeObserver(self)
setUpAudioPlayerActions()
setUpRemoteCommandHandler()
}

// MARK: Public
Expand Down Expand Up @@ -106,7 +92,7 @@ public final class AudioBannerViewModel: ObservableObject {
}

func start() async {
remoteCommandsHandler.startListeningToPlayCommand()
remoteCommandsHandler?.startListeningToPlayCommand()
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationDidBecomeActive),
Expand Down Expand Up @@ -142,7 +128,7 @@ public final class AudioBannerViewModel: ObservableObject {
private let lastAyahFinder: LastAyahFinder = PreferencesLastAyahFinder.shared
private let audioPlayer: QuranAudioPlayer
private let downloader: QuranAudioDownloader
private let remoteCommandsHandler: RemoteCommandsHandler
private var remoteCommandsHandler: RemoteCommandsHandler?
private let reciterListBuilder: ReciterListBuilder
private let advancedAudioOptionsBuilder: AdvancedAudioOptionsBuilder

Expand Down Expand Up @@ -177,8 +163,8 @@ public final class AudioBannerViewModel: ObservableObject {
}
listener?.highlightReadingAyah(nil)

remoteCommandsHandler.stopListening()
remoteCommandsHandler.startListeningToPlayCommand()
remoteCommandsHandler?.stopListening()
remoteCommandsHandler?.startListeningToPlayCommand()
}

@objc
Expand All @@ -188,8 +174,6 @@ public final class AudioBannerViewModel: ObservableObject {
playingState = tempPlayingState
}

// MARK: - Reciter

private func selectReciter(_ reciter: Reciter) {
preferences.lastSelectedReciterId = reciter.id
}
Expand Down Expand Up @@ -311,6 +295,16 @@ public final class AudioBannerViewModel: ObservableObject {

// MARK: - Audio Interactor Delegate

private func setUpAudioPlayerActions() {
let actions = QuranAudioPlayerActions(
playbackEnded: { [weak self] in self?.playbackEnded() },
playbackPaused: { [weak self] in self?.playbackPaused() },
playbackResumed: { [weak self] in self?.playbackResumed() },
playing: { [weak self] in self?.playing(ayah: $0) }
)
audioPlayer.setActions(actions)
}

private func playbackPaused() {
logger.info("AudioBanner: playback paused")
updatePlayingState(to: .paused)
Expand Down Expand Up @@ -363,7 +357,7 @@ public final class AudioBannerViewModel: ObservableObject {
logger.info("AudioBanner: playing started")
cancellableTasks = []
crasher.setValue(false, forKey: .downloadingQuran)
remoteCommandsHandler.startListening()
remoteCommandsHandler?.startListening()
playingState = .playing

guard let audioRange else {
Expand Down Expand Up @@ -425,8 +419,19 @@ extension AudioBannerViewModel {
}
}

extension AudioBannerViewModel: RemoteCommandsHandlerDelegate {
func onPlayCommandFired() {
extension AudioBannerViewModel {
private func setUpRemoteCommandHandler() {
let remoteActions = RemoteCommandActions(
play: { [weak self] in self?.handlePlayCommand() },
pause: { [weak self] in self?.handlePauseCommand() },
togglePlayPause: { [weak self] in self?.handleTogglePlayPauseCommand() },
nextTrack: { [weak self] in self?.handleNextTrackCommand() },
previousTrack: { [weak self] in self?.handlePreviousTrackCommand() }
)
remoteCommandsHandler = RemoteCommandsHandler(center: .shared(), actions: remoteActions)
}

private func handlePlayCommand() {
logger.info("AudioBanner: play command fired. State: \(playingState)")
switch playingState {
case .stopped: playStartingCurrentPage()
Expand All @@ -435,22 +440,22 @@ extension AudioBannerViewModel: RemoteCommandsHandlerDelegate {
}
}

func onPauseCommandFired() {
private func handlePauseCommand() {
logger.info("AudioBanner: pause command fired. State: \(playingState)")
pause()
}

func onTogglePlayPauseCommandFired() {
private func handleTogglePlayPauseCommand() {
logger.info("AudioBanner: toggle play/pause command fired. State: \(playingState)")
togglePlayPause()
}

func onStepForwardCommandFired() {
private func handleNextTrackCommand() {
logger.info("AudioBanner: step forward command fired. State: \(playingState)")
stepForward()
}

func onStepBackwardCommandFire() {
private func handlePreviousTrackCommand() {
logger.info("AudioBanner: step backward command fired. State: \(playingState)")
stepBackward()
}
Expand Down
81 changes: 53 additions & 28 deletions Features/AudioBannerFeature/RemoteCommandsHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,38 @@
import MediaPlayer

@MainActor
protocol RemoteCommandsHandlerDelegate: AnyObject {
func onPlayCommandFired()
func onPauseCommandFired()
func onTogglePlayPauseCommandFired()
func onStepForwardCommandFired()
func onStepBackwardCommandFire()
struct RemoteCommandActions {
var play: () -> Void
var pause: () -> Void
var togglePlayPause: () -> Void
var nextTrack: () -> Void
var previousTrack: () -> Void
}

final class RemoteCommandsHandler: Sendable {
@MainActor
final class RemoteCommandsHandler {
// MARK: Lifecycle

init(center: MPRemoteCommandCenter) {
init(center: MPRemoteCommandCenter, actions: RemoteCommandActions) {
self.center = center
self.actions = actions
setUpRemoteControlEvents()
}

deinit {
stopListening()
Task { [center] in
await center.setCommandsEnabled(false)
}
}

// MARK: Internal

weak var delegate: RemoteCommandsHandlerDelegate?

func startListening() {
setCommandsEnabled(true)
center.setCommandsEnabled(true)
}

func stopListening() {
setCommandsEnabled(false)
center.setCommandsEnabled(false)
}

func startListeningToPlayCommand() {
Expand All @@ -48,56 +50,79 @@ final class RemoteCommandsHandler: Sendable {
// MARK: Private

private let center: MPRemoteCommandCenter
private let actions: RemoteCommandActions

private func setUpRemoteControlEvents() {
center.playCommand.addTarget { [weak self] _ in
guard let self else { return .success }
Task { @MainActor in
self.delegate?.onPlayCommandFired()
self.actions.play()
}
return .success
}
center.pauseCommand.addTarget { [weak self] _ in
guard let self else { return .success }
Task { @MainActor in
self.delegate?.onPauseCommandFired()
self.actions.pause()
}
return .success
}
center.togglePlayPauseCommand.addTarget { [weak self] _ in
guard let self else { return .success }
Task { @MainActor in
self.delegate?.onTogglePlayPauseCommandFired()
self.actions.togglePlayPause()
}
return .success
}
center.nextTrackCommand.addTarget { [weak self] _ in
guard let self else { return .success }
Task { @MainActor in
self.delegate?.onStepForwardCommandFired()
self.actions.nextTrack()
}
return .success
}
center.previousTrackCommand.addTarget { [weak self] _ in
guard let self else { return .success }
Task { @MainActor in
self.delegate?.onStepBackwardCommandFire()
self.actions.previousTrack()
}
return .success
}

// disable all of them initially
setCommandsEnabled(false)

// disabled unused command
[center.seekForwardCommand, center.seekBackwardCommand, center.skipForwardCommand,
center.skipBackwardCommand, center.ratingCommand, center.changePlaybackRateCommand,
center.likeCommand, center.dislikeCommand, center.bookmarkCommand, center.changePlaybackPositionCommand].forEach { $0.isEnabled = false }
center.setCommandsEnabled(false)

// disabled unused commands
let unusedCommands = [
center.seekForwardCommand,
center.seekBackwardCommand,
center.skipForwardCommand,
center.skipBackwardCommand,
center.ratingCommand,
center.changePlaybackRateCommand,
center.likeCommand,
center.dislikeCommand,
center.bookmarkCommand,
center.changePlaybackPositionCommand,
]
for command in unusedCommands {
command.isEnabled = false
}
}
}

private func setCommandsEnabled(_ enabled: Bool) {
let center = MPRemoteCommandCenter.shared()
[center.playCommand, center.pauseCommand, center.togglePlayPauseCommand,
center.nextTrackCommand, center.previousTrackCommand].forEach { $0.isEnabled = enabled }
extension MPRemoteCommandCenter {
@MainActor
func setCommandsEnabled(_ enabled: Bool) {
let commands = [
playCommand,
pauseCommand,
togglePlayPauseCommand,
nextTrackCommand,
previousTrackCommand,
]
for command in commands {
command.isEnabled = enabled
}
}
}

0 comments on commit 5253d31

Please sign in to comment.