I have a simple MIDI player (Swift 5, SwiftUI, iOS14) and would like to toggle a play/pause command with MPRemoteCommandCenter. The control from the lock screen works fine with the following setup, but I'm struggling to find how to notify MPRemoteCommandCenter of my player's state when play/pause is toggled from inside the app and not the lock screen. Playing or pausing from within my app doesn't change the play/pause icon on the lock screen, however I can stop the displayed playback position and progress on the lock screen by setting the playback rate to 0. In other words, the only issue is that the play/pause icon on the lock screen is out of sync.
I feel like I should be emitting a notification here, but I'm not finding the documentation. I have a similar audio media player that uses AVAudioPlayer and this just magically works, and I'm guessing AVAudioPlayer is spitting out the required notifications. Does anyone have a tutorial or advice on how to do this with my own player that doesn't use AVAudioPlayer? Thanks.
// ContentView.swift
import SwiftUI
import AudioKit
import MediaPlayer
struct ContentView: View {
let engine: AudioEngine
let sampler: MIDISampler
let sequencer: AppleSequencer
@State var playing: Bool = false
init() {
self.engine = AudioEngine()
self.sampler = MIDISampler()
self.sequencer = AppleSequencer()
let url = Bundle.main.url(forResource: "mytrack", withExtension: "mid")!
sequencer.loadMIDIFile(fromURL: url)
sequencer.setGlobalMIDIOutput(sampler.midiIn)
engine.output = sampler
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
try AVAudioSession.sharedInstance().setActive(true)
try engine.start()
} catch let error {
print(error)
}
}
var body: some View {
Text("Play a MIDI file")
.padding()
Button(action: {
self.togglePlayPause()
}) {
if playing {
Text("Pause")
} else {
Text("Play")
}
}.onAppear {
self.setupRemoteTransportControls()
}
}
func togglePlayPause() {
if playing {
sequencer.stop()
} else {
sequencer.play()
}
self.playing = playing ? false : true
self.setupNowPlayingInfo()
}
func setupNowPlayingInfo() {
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "My track"
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = sequencer.currentPosition.seconds
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = sequencer.length.seconds
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = NSNumber(value: self.playing ? 1 : 0)
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
func setupRemoteTransportControls() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.togglePlayPauseCommand.addTarget { [self] _ in
self.togglePlayPause()
return .success
}
commandCenter.togglePlayPauseCommand.isEnabled = true
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}