Note: I posted this on Stackoverflow but haven't gotten any answers. Please let me know if the question is unclear or the code sample is insufficient.
I have a method that changes the audio track played by my app's
AVPlayer
and also sets MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
for the new track:func setTrackNumber(trackNum: Int) {
self.trackNum = trackNum
player.replaceCurrentItemWithPlayerItem(tracks[trackNum])
var nowPlayingInfo: [String: AnyObject] = [ : ]
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = tracks[trackNum].albumTitle
nowPlayingInfo[MPMediaItemPropertyTitle] = "Track \(trackNum)"
...
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = nowPlayingInfo
print("Now playing local: \(nowPlayingInfo)")
print("Now playing lock screen: \(MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo)")
}
I call this method when the user explicitly selects an album or track and when a track ends and the next one automatically starts. The lock screen correctly shows the track metadata when the user sets an album or track but NOT when a track ends and the next one is automatically set.
I added print statements to make sure I was correctly populating the
nowPlayingInfo
dictionary. As expected, the two print statements print the same dictionary content when this method is called for a user-initiated change of album or track. However, in the case when the method is called after an automatic track change, the local nowPlayingInfo
variable shows the new trackNum
whereas MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
shows the previous trackNum
:Now playing local: ["title": Track 9, "albumTitle": Test Album, ...]
Now playing set: Optional(["title": Track 8, "albumTitle": Test Album, ...]
I discovered that when I set a breakpoint on the line that sets
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
to nowPlayingInfo
, then the track number is correctly updated on the lock screen. Adding sleep(1)
right after that line also ensures that the track on the lock screen is correctly updated.I have verified that
nowPlayingInfo
is always set from the main queue. I've tried explicitly running this code in the main queue or in a different queue with no change in behavior.What is preventing my change to
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
? How can I make sure that setting MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
always updates the lock screen info?
One thing to keep in mind is that dispatch_async causes the block of code to be executed on the desired thread, scheduled at some point in the future. And for a serial queue, it is FIFO, but it would be on a subsequent run loop.
I'm looking into whether or not the setter is asynchronous, but I think maintaining your own dictionary and setting all of the values correctly will always ensure that you are setting the data you intend to, as opposed to fetching the current dictionary, changing one value, and setting it, which is effectively what is going on currently in your code. That way, you control the "source of truth" and are always ensuring that anytime your class is asked for or provides the current state, that it is doing so based on being the master of the domain.