Hello,
In MusicKit I would like to know how I can detect the end of hasNextBatch for a MusicItemCollection.
Let's say, I have a playlist with 100+ songs in my library.
Initially, I get 100 songs from the response and hasNextBatch is true.
If I use a while loop, every response includes the hasNextBatch = true. So I do not know when to stop or how many limited loop to make.
Is there way I can get the playlist loaded completely with all the songs loaded using nextBatch and stop when I have hit the end?
Hello @ashinthetray,
I cannot reproduce any issue with the playlist you suggested.
Here's some sample code that iteratively loads all the tracks of that playlist, one batch at a time.
var playlistRequest = MusicCatalogResourceRequest<Playlist>(matching: \.id, equalTo: "pl.u-76oN0LeIve30Yy")
playlistRequest.properties = [.tracks]
let playlistResponse = try await playlistRequest.response()
let playlist = playlistResponse.items[0]
print("loading all tracks for playlist \(playlist)")
var batchIndex = 0
var tracks = playlist.tracks ?? []
print("batch number \(batchIndex + 1) => \(tracks.count) tracks, hasNextBatch: \(tracks.hasNextBatch)")
repeat {
if let nextBatchOfTracks = try await tracks.nextBatch() {
tracks = nextBatchOfTracks
batchIndex += 1
print("batch number \(batchIndex + 1) => \(tracks.count) tracks, hasNextBatch: \(tracks.hasNextBatch)")
} else {
print("no next batch could be loaded")
break
}
} while tracks.hasNextBatch
print("done after \(batchIndex + 1) batches")
Here's the console output for this code:
loading all tracks for playlist Playlist(id: "pl.u-76oN0LeIve30Yy", name: "Insecure Official Playlist (Seasons 1-5)", curatorName: "Jordan")
batch number 1 => 100 tracks, hasNextBatch: true
batch number 2 => 100 tracks, hasNextBatch: true
batch number 3 => 100 tracks, hasNextBatch: true
batch number 4 => 100 tracks, hasNextBatch: true
batch number 5 => 4 tracks, hasNextBatch: false
done after 5 batches
Once you have the first batch of tracks, retrieved from Playlist's tracks property, you can call nextBatch() to get the second batch.
If you call nextBatch() again on the first batch, you'll get the exact same results. And that's to be expected, because MusicItemCollection is a value type (i.e. a struct
), and won't mutate from under you when you call nextBatch(), because that method is not a mutating
one. So just because you call nextBatch() won't change its internal state so that a second call would return a different result.
Instead, if you want to get the third batch, you need to call nextBatch() on the second batch, and so on and so forth.
So I think the main thing that makes this sample code work, which I'm guessing you didn't realize, is this:
if let nextBatchOfTracks = try await tracks.nextBatch() {
tracks = nextBatchOfTracks
i.e. the fact that we replace the value of the tracks
local variable with the nextBatchOfTracks
that was just loaded.
Nevertheless, while this is technically possible, as I just showed you, let me reiterate that we would generally hope that you never need to eagerly fetch all the possible next batches of a MusicItemCollection in this way.
What we would hope you do in your app is precisely what you said, to fetch the next batch as the users scroll further down. I'll leave it up to you to implement this behavior in your UI code using MusicItemCollection's nextBatch().
I will just give you one last tip: please read the documentation for MusicItemCollection's += operator.
I hope this helps.
Best regards,