I have a function that returns all songs in the Music library with the same year as the currently playing one. It looks like this:
private func loadSongsByYear() {
let query = MPMediaQuery.songs()
query.addFilterPredicate(MPMediaPropertyPredicate(value: false, forProperty: MPMediaItemPropertyIsCloudItem))
if let items = query.items {
if let yearInt = Int(year) {
songsByYear = items.filter { item in
if let itemYearNumber = item.value(forProperty: "year") as? NSNumber {
return itemYearNumber.intValue == yearInt
}
return false
}
} else {
songsByYear = items.filter { item in
return item.value(forProperty: "year") == nil
}
}
}
}
I call it in a ScrollView with .onAppear:
ScrollView {
//My ScrollView
}
.onAppear(perform: loadSongsByYear)
I want it to update when the currently playing song changes, so I added this .onRecieve
ScrollView {
//My ScrollView
}
.onAppear(perform: loadSongsByYear)
.onReceive(
NotificationCenter.default.publisher(for: .MPMusicPlayerControllerNowPlayingItemDidChange),
perform: { _ in
loadSongsByYear()
}
)
It almost works, except it is one step behind. If the currently playing song has the year 2000, and then I switch to a song from the year 2001, nothing changes, but when I switch a second time to a song from 2002, the songs from the year 2001 loads.
Have I misunderstood how .onRecieve works or what am I doing wrong?
This is unfortunately caused by
NotificationCenter.Publisher. Using theasyncvariant also results in the same issue, where you only receive the latest update in the next main thread runloop after the notification was posted, instead of the same runloop.You need to use the old
NotificationCentermethod,addObserverif you want to be able to receive notifications in the same main thread runloop in which they were posted.