Sorry that the question is too sketchy, I will provide more details.
First, I created a process to retrieve playlist information using MusicLibraryRequest, then retrieve the song information in each playlist and keep it as a model: MusicPlaylist defined in my program, as shown below.
@Published var libraryPlaylists = [MusicPlaylist]()
@MainActor
init() {
loadLibraryPlaylist()
}
@MainActor
private func loadLibraryPlaylist(){
Task {
let status = await MusicAuthorization.request()
switch status {
case .authorized:
do {
if #available(iOS 16.0, *) {
var request = MusicLibraryRequest<Playlist>()
request.sort(by: \.lastPlayedDate, ascending: false)
let response = try await request.response()
let playlists = response.items
fetchMusicTracksInPlaylist(playlists: playlists)
} else {
// Fallback on earlier versions
}
} catch {
print(String(describing: error))
}
default:
break
}
}
}
@MainActor
private func fetchMusicTracksInPlaylist(playlists: MusicItemCollection<Playlist>) {
Task {
do {
for playlist in playlists {
let detailedPlaylist = try await playlist.with(.tracks, preferredSource: .library)
print("*** Detailed Plaulist ***")
print("\(detailedPlaylist)")
let libraryPlaylist = MusicPlaylist(
id: detailedPlaylist.id.rawValue,
name: detailedPlaylist.name,
imageUrl: detailedPlaylist.artwork?.url(width: 75, height: 75),
lastPlayedDate: detailedPlaylist.lastPlayedDate,
musicTracks: getMusicTracksInPlaylist(playlist: detailedPlaylist))
self.libraryPlaylists.append(libraryPlaylist)
}
}
}
}
private func getMusicTracksInPlaylist(playlist: Playlist) -> [MusicTrack] {
var musicTracks = [MusicTrack]()
playlist.tracks?.forEach { track in
musicTracks.append(MusicTrack(
id: track.id.rawValue,
name: track.title,
artist: track.artistName,
imageUrl: track.artwork?.url(width: 75, height: 75)
))
}
print("*** music tracks in playlist ***")
print("\(playlist.tracks)")
return musicTracks
}
import MusicKit
import Foundation
import MediaPlayer
struct MusicPlaylist: Identifiable, Hashable {
var id: String
let name: String
let imageUrl: URL?
let lastPlayedDate: Date?
let musicTracks: [MusicTrack]?
}
Using this MusicPlaylist instance to display a list of playlists and a list of songs for each playlist worked well.
However, when I try to select one of the songs in the list and play that song, it fails.
import SwiftUI
import MusicKit
struct PlaylistView: View {
@EnvironmentObject var modelData: ModelData
let playlist: MusicPlaylist
@State var musicTracks = [MusicTrack]()
var body: some View {
NavigationView {
List {
Section(header: Text(playlist.name)) {
if let musicTracks = playlist.musicTracks {
ForEach(musicTracks, id: \.id) {
musicTrack in
MusicTrackRow(musicTrack: musicTrack)
}
}
}
}
.listStyle(InsetListStyle())
}
}
}
import MusicKit
import SwiftUI
import MediaPlayer
struct MusicTrackRow: View {
@EnvironmentObject var modelData: ModelData
let musicTrack: MusicTrack
var body: some View {
HStack {
AsyncImage(url: musicTrack.imageUrl)
.frame(width: 80, height: 80, alignment: .center)
.cornerRadius(8)
VStack(alignment: .leading) {
Text(musicTrack.name)
.font(.callout)
.bold()
Spacer()
Text(musicTrack.artist)
.font(.subheadline)
}
.padding()
}
.onTapGesture {
print("*** Play Music ***")
print("\(musicTrack)")
modelData.currentTrack = musicTrack
modelData.musicPlayer.setQueue(with: [musicTrack.id])
print("*** Setted music track id ***")
print("now playing item: \(modelData.musicPlayer.indexOfNowPlayingItem)")
modelData.isSelected = true
modelData.musicPlayer.play()
modelData.isPlaying = true
}
}
(Below is debug message)
*** Setted music track id ***
2022-09-29 11:39:10.909463+0900 MusicDiscovery[15402:3213994] [SDKPlayback] applicationQueuePlayer _establishConnectionIfNeeded timeout [ping did not pong]
now playing item: 0
2022-09-29 11:39:11.036584+0900 MusicDiscovery[15402:3213994] [SDKPlayback] Failed to prepareToPlay error: Error Domain=MPMusicPlayerControllerErrorDomain Code=6 "Failed to prepare to play" UserInfo={NSDebugDescription=Failed to prepare to play}
2022-09-29 11:39:11.100059+0900 MusicDiscovery[15402:3213994] Metal API Validation Enabled
On the other hand, I have also implemented a separate process that searches for and plays songs using MusicCatalogSearchRequest, which can be played without problems.
I have no idea why songs retrieved using playlist information cannot be played, and my current hypothesis is that it is because the songs in the playlist are all purchased from itunes or Traxcource.
I'd like to know if the above hypothesis is correct, and based on that, the proper coding to play the songs in the playlist...!
Thanks for reading.