MusicKit Custom Data Request

Hi,

I am trying to use MusicDataRequest from MusicKit to get the user's library artists (https://api.music.apple.com/v1/me/library/artists). This works well, until I am trying to use pagination with it. The data returned by this endpoint contains a "next" property, which reads /v1/me/library/artists?offset=25. Replacing the URL as such resulting in https://api.music.apple.com/v1/me/library/artists?offset=25 no longer works, as the request returns an error:

MusicDataRequest.Error(
   status: 400,
   code: 40008,
   title: "Invalid Path Value",
   detailText: "Unknown library resource type 'artists?offset=25'",
   ...
)

It seems like the request cannot handle query parameters and considers them as part of the request's path.

Is there any way to use query parameters with MusicDataRequest?

Thanks for any response.

  • Actually it seems the "next" property contains an invalid path, it returns "artists?offset=25" and should return "artists/?offset=25"

Add a Comment

Replies

Hello @sharedRoutine,

The value of the "next" property is actually just fine. You just need to be very careful when manipulating URLs. That's why in general, you're always better off using URLComponents when manipulating URLs.

But more importantly, you can do this much more simply using MusicKit, so you won't even need to use the "next" field directly. Indeed, since the response from the Get All Library Artists endpoint is a resource collection of artists, you can just decode it using MusicItemCollection.

Once you have your collection of artists, you can just check for the presence of a next batch using MusicItemCollection's hasNextBatch property, and then load it using the nextBatch(limit:) function:

let url = URL(string: "https://api.music.apple.com/v1/me/library/artists")!
let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url))
let dataResponse = try await dataRequest.response()

let decoder = JSONDecoder()
let artists = try decoder.decode(MusicItemCollection<Artist>.self, from: dataResponse.data)

print("artists = \(artists)")

if artists.hasNextBatch {
    if let nextArtists = try await artists.nextBatch() {
        print("nextArtists = \(nextArtists)")
    }
}

Here's the abbreviated output I see for this code:

artists = MusicItemCollection<Artist>(
  items: [
    Artist(id: "r.JQh8U77", name: "Ana Tijoux"),
    [...]
  ],
  hasNextBatch: true
)

nextArtists = MusicItemCollection<Artist>(
  items: [
    Artist(id: "r.58OQvyt", name: "Berry"),
    [...]
  ],
  hasNextBatch: true
)

I hope this helps.

Best regards,

  • Thanks! That is very cool. However with this approach I am missing two functions. First, I am using the catalog relationship using the "include" parameter to get the catalog identifier of the library artists. Is this possible with this approach, given that the Artists in the MusicItemCollection have the library identifier? I am using the catalog identifier to make a custom request to get the latest "X" amount of albums (sorted by releaseDate) directly from the API Endpoint. If I can load a collection of latest albums (instead of the lastestAlbums property), then I might not need the catalog identifier in the first place.

  • Hello @sharedRoutine, MusicKit for Swift doesn't currently offer public APIs to solve this specific problem when using builtin item types like Artist. However, we are actively investigating viable solutions that would unblock such use-cases. Stay tuned. Best regards,

Add a Comment

Hello @sharedRoutine,

I just wanted to let you know you no longer need to use MusicDataRequest on iOS 16 beta 1 to fetch artists from the user's library.

Instead, you can use the brand new MusicLibraryRequest in MusicKit.

Please check our new WWDC22 session video, Explore more content with MusicKit, which goes into this, and much more!

I hope you'll like it!

Best regards,