.onRecieve is one step behind

64 Views Asked by At

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?

1

There are 1 best solutions below

0
Dávid Pásztor On

This is unfortunately caused by NotificationCenter.Publisher. Using the async variant 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 NotificationCenter method, addObserver if you want to be able to receive notifications in the same main thread runloop in which they were posted.