MusicKit - Dynamic Queue

Hello,

I'm working with the new MusicKit API, I'm trying to get a list of songs be played from a list IDs.

// List if Songs IDs
let songIds = [MusicItemID("560097694"), MusicItemID("560097715")]

// Defines requests
let requests = songIds.map { songId in
    MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: songId)
 }

 // Responses
let response = try? await requests[0].response()
guard let song = response?.items.first  else {
     return
}

let response1 = try? await requests[1].response()
guard let song1 = response1?.items.first  else {
        return
}

Then if I perform this code :

// Queue Songs
let songs: ApplicationMusicPlayer.Queue = []
try? await songs.insert(song, position: .tail)
try? await songs.insert(song1, position: .tail)
let appPlayer = ApplicationMusicPlayer.shared
appPlayer.queue = songs
try? await appPlayer.play()

I have the following errors :

[SDKPlayback] applicationQueuePlayer _establishConnectionIfNeeded timeout [ping did not pong]

[SDKPlayback] Failed to prepareToPlay error: Error Domain=MPMusicPlayerControllerErrorDomain Code=6 "Failed to prepare to play" UserInfo={NSDebugDescription=Failed to prepare to play}

[SDKPlayback] Failed to prepareToPlay error: Error Domain=MPMusicPlayerControllerErrorDomain Code=6 "Failed to prepare to play" UserInfo={NSDebugDescription=Failed to prepare to play}

But if I do This :


let appPlayer = ApplicationMusicPlayer.shared
appPlayer.queue = [song, song1]
try? await appPlayer.play()

It works perfectly

Is it possible to create a dynamic Queue ? And what is the recommended approach ?


Xcode Version 13.1 (13A1030d)

Answered by Frameworks Engineer in 696426022

Hello @Hikosei,

When using MusicKit's ApplicationMusicPlayer, you should always aim to set the queue initially as completely as you can, using the setter for the queue property.

The correct approach for the specific example you're showing here is:

let request = MusicCatalogResourceRequest<Song>(matching: \.id, memberOf: ["560097694", "560097715"])
let response = try await request.response()

let musicPlayer = ApplicationMusicPlayer.shared
musicPlayer.queue = ApplicationMusicPlayer.Queue(for: response.items)
try await musicPlayer.play()

As you can see, you don't even need to load these songs one at a time. You can setup a single MusicCatalogResourceRequest with init(matching:memberOf:), and pass both of those IDs. This is much more efficient than what you were trying to do.

Once the queue has been set correctly, and the player is already playing (or is at least correctly prepared to play), then you can use the Queue's insertion methods, such as insert(_:position:).

I hope this helps.

Best regards,

Accepted Answer

Hello @Hikosei,

When using MusicKit's ApplicationMusicPlayer, you should always aim to set the queue initially as completely as you can, using the setter for the queue property.

The correct approach for the specific example you're showing here is:

let request = MusicCatalogResourceRequest<Song>(matching: \.id, memberOf: ["560097694", "560097715"])
let response = try await request.response()

let musicPlayer = ApplicationMusicPlayer.shared
musicPlayer.queue = ApplicationMusicPlayer.Queue(for: response.items)
try await musicPlayer.play()

As you can see, you don't even need to load these songs one at a time. You can setup a single MusicCatalogResourceRequest with init(matching:memberOf:), and pass both of those IDs. This is much more efficient than what you were trying to do.

Once the queue has been set correctly, and the player is already playing (or is at least correctly prepared to play), then you can use the Queue's insertion methods, such as insert(_:position:).

I hope this helps.

Best regards,

MusicKit - Dynamic Queue
 
 
Q