Post

Replies

Boosts

Views

Activity

Reply to MusicKit Sample Code errors
Hi @RanLearns!  I was in the same position as you when Apple released beta 4. I was able to fix the issues mainly around the play() being asynchronous now and can throw an error. I don't know if my approach is the standard, one but I'll share it nonetheless.  Dividing the error into two parts -  Trying to make an async call from a non-async function. The call can throw errors, but the function is not handling the error. For the first part, we can suffix the function with the async keyword and prefix the call with the await keyword.  For example, `handlePlayButtonSelected() becomes -   private func handlePlayButtonSelected() async {    if !isPlaying {     if !isPlaybackQueueSet {      player.setQueue(with: album)      isPlaybackQueueSet = true     }     await player.play()    } else {     player.pause()    }   } We can make the function throw an error and prefix the call with the try keyword for the second part. So, handlePlayButtonSelected() finally looks like - private func handlePlayButtonSelected() async throws {    if !isPlaying {     if !isPlaybackQueueSet {      player.setQueue(with: album)      isPlaybackQueueSet = true     }     try await player.play()    } else {     player.pause()    }   } Similarly, `handlePlayButtonSelected() can be changed to -   private func handleTrackSelected(_ track: Track, loadedTracks: MusicItemCollection<Track>) async throws {    player.setQueue(with: loadedTracks, startingAt: track)    isPlaybackQueueSet = true    try await player.play()   } Now, you'll get another error in view where you call these functions. First, in the list where you show the TrackCell, update the action handleTrackSelected(track, loaded tracks: loadedTracks) with the following -  Task {   try? await handleTrackSelected(track, loadedTracks: loadedTracks) } For the second error in the button in playButtonRow, update handlePlayButtonSelected with -  Task {   try? await handlePlayButtonSelected() } The third error is due to a change in the searchable modifier. Now, you've to specify a prompt.  So, replace searchable("Albums", text: $searchTerm) with -  .searchable(text: $searchTerm, prompt: "Albums") There are a few warnings as well, mostly related to the use of detach. You can replace them with Task.detached.  Lastly, you'll find deprecation warnings related to the setQueue(with:startingAt:) method. In Xcode 13, Beta 4 added a new instance property, queue.  We can set this property by using the initializers of the class Queue. In AlbumDetailView.handleTrackSelected(_:loadedTracks:), you can set it as -  private func handleTrackSelected(_ track: Track, loadedTracks: MusicItemCollection<Track>) async throws {    player.queue = .init(for: loadedTracks, startingAt: track)    isPlaybackQueueSet = true    try await player.play()   } In handlePlayButtonSelected(), you can set it as -   private func handlePlayButtonSelected() async throws {    if !isPlaying {     if !isPlaybackQueueSet {      player.queue = .init(arrayLiteral: album)      isPlaybackQueueSet = true     }     try await player.play()    } else {     player.pause()    }   } The last warning is related to the deprecation of the playbackStatus variable. The latest beta offers us with ObservableObject class MusicPlayer.State, and we'll use the playbackState instance property instead. I'm not sure about this one on how to go about observing the value of the playbackStatus, but here's my approach -   .task {     isPlaying = player.state.playbackStatus == .playing  } With this, the project is error and warnings-free!
Aug ’21
Reply to Observing the playbackTime in MusicKit
Hi @JoeKun, Thank you for your detailed answer! Much appreciated. So, if I've to show the current duration in a label, I should probably have a timer running with the initial value of playbackTime? And then, whenever the playbackStatus changes, update the current duration value with the latest playbackTime and refresh the view? I think I can do with this approach for the time being. But having playbackTime as an observable value will make it much much simpler. So, I'll file a ticket for this. Thank you so much!
Aug ’21
Reply to Cannot decode library songs response for a non-Apple Music item
Hi @JoeKun, While creating my own framework for easy access to different Apple Music APIs, I realized I was using it wrong. It's better to decode using MusicItemCollection<> instead of creating my own structure. Then, it safely ignores the item that is not decodable due to missing information. let response = try JSONDecoder().decode(MusicItemCollection<Song>.self, from: dataResponse.data)
Aug ’21
Reply to How to get resources for a Genre?
What should be the best approach to decode the chart data for songs? For example - struct Charts: Decodable {     let results: Songs } struct Songs: Codable {     let songs: [ChartsSong] } struct ChartsSong: Codable {     let data: [Song]     let orderID, next, chart, name: String     let href: String     enum CodingKeys: String, CodingKey {         case data         case orderID = "orderId"         case next, chart, name, href     } } let url = URL(string: "https://api.music.apple.com/v1/catalog/us/charts?types=songs&genre=20&limit=1")! let musicRequest = MusicDataRequest(urlRequest: URLRequest(url: url)) let response = try await musicRequest.response() let data = try JSONDecoder().decode(Charts.self, from: response.data) Right now, I'm using a custom structure to decode the data with the ChartsSong struct. Do you think there's a possibility of a Chart item in the future? Should I file feedback stating my use case?
Aug ’21
Reply to Apple Music/Music Kit: how to get song IDs from regular URLs?
Edit: I didn't see that this question was for MusicKit JS. Apologies. I don't know how to delete the answer, so I'll leave it for someone else to find it helpful. You can extract the song ID from the link by creating a URL component and getting the query items out of the component. Here, the query parameter is "i" with the value 1035048414 as the song ID. You get the first query item and fetch the value out of it: let components = URLComponents(string: "https://music.apple.com/us/album/take-on-me-1985-12-mix-2015-remastered/1035047659?i=1035048414") guard let songID = components?.queryItems?.first?.value else { return } print("SONG ID IS - \(songID)") Now, you can just use a standard MusicCatalogResourceRequest: let request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: MusicItemID(songID)) do { let response = try await request.response() print(response.description) } catch { print(error) } When I print it: SONG ID IS - 1035048414 MusicCatalogResourceResponse<Song>(   items: [     Song(id: "1035048414", title: "Take On Me (1985 12" Mix) [2015 Remastered]", artistName: "a-ha")   ] ) I hope that helps!
Oct ’21
Reply to How to decode the JSON response from MusicKit Search Suggestion
Hi @JoeKun, with the current solution and the response from the endpoint, it only fetches one item at a time. How would I go about creating a MusicItemCollection out of it? The Search for Catalog Resources has a very nice response as it provides an array of albums, songs, etc. But the Get Catalog Search Suggestions has one response model per music item, be it an album, song. Etc. Any suggestion? Edit: Hacky workaround switch topResult.content {             case .album(let album): self.albums += MusicItemCollection(arrayLiteral: album)             case .artist(let artist): self.artists += MusicItemCollection(arrayLiteral: artist)             case .song(let song): self.songs += MusicItemCollection(arrayLiteral: song)             case .curator(let curator): self.curators += MusicItemCollection(arrayLiteral: curator)             default: ()           } Do you think I should use MusicItemCollection in this way?
Apr ’22