xcodemacoscocoampremotecommandcenter

MacOS not responding to MPRemoteCommandCenter commands in the background


I am writing an application for my own purposes that aims to get play pause events no matter what is going on in the system. I have gotten this much working

let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.togglePlayPauseCommand.isEnabled = true
commandCenter.togglePlayPauseCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("Play Pause Command")
    return .success
}

commandCenter.nextTrackCommand.isEnabled = true
commandCenter.nextTrackCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("NextTrackCommand")
    return .success
}
commandCenter.previousTrackCommand.isEnabled = true
commandCenter.previousTrackCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("previousTrackCommand")
    return .success
}
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("playCommand")
    return .success
}

MPNowPlayingInfoCenter.default().playbackState = .playing

Most of those methods are there because apparently you will not get any notifications without having nextTrackCommand or previousTrackCommand or playCommand implemented.

Anyways my one issue is that as soon as you open another application that uses audio these event handlers stop getting called and I cant find a way to detect and fix this.

I would normally try doing AVAudioSession things to state this as a background application however that does not seem to work. Any ideas on how I can get playpause events no matter what state the system is in?

I would like to be able to always listen for these events OR get an indication of when someone else has taken control of the audio? Perhaps even be able to re-subscribe to these play pause events.


Solution

  • There's an internal queue in the system which contains all the audio event subscribers. Other applications get on top of it when you start using them.

    I would like to be able to always listen for these events

    There's no API for that but there's a dirty workaround. If I understand your issue correctly, this snippet:

        MPNowPlayingInfoCenter.default().playbackState = .paused
        MPNowPlayingInfoCenter.default().playbackState = .playing
    

    must do the trick for you if you run it in a loop somewhere in your application.

    Note that this is not 100% reliable because:

    1. If an event is generated before two subsequent playbackState state changes right after you've switched to a different application, it would still be catched by the application in the active window;
    2. If another application is doing the same thing, there would be a constant race condition in the queue, with unpredictable outcome.

    References:

    OR get an indication of when someone else has taken control of the audio

    As far as I know there's no public API for this in macOS.