Meet MusicKit for Swift

RSS for tag

Discuss the WWDC21 session Meet MusicKit for Swift.

Posts under wwdc21-10294 tag

54 Posts

Post

Replies

Boosts

Views

Activity

Any way to check if song is in Library with MusicKit? ?relate=library not working for me
For my app that heavily uses both ShazamKit and MusicKit, I need to be able to check if the matched song is in the user's library or not. I couldn't find an easy way to do this with MusicKit so I first then turned to the Apple Music API as they introduced the new parameter ?relate=library during this year's WWDC. When I first tried it, it worked as expected, and was very happy to have gotten it to work. However, it stopped working lately and now I turned to use MPMediaLibrary as that still works but is a lot slower in performance. Anyone has any idea why the ?relate=library stopped working for me or know a better way to check if the Song exists in the user's library? func checkInLibrary(from appleMusicID: String) async { do { let countryCode = try await MusicDataRequest.currentCountryCode let libURL = URL(string: "https://api.music.apple.com/v1/catalog/\(countryCode)/songs/\(appleMusicID)?relate=library")! let request = MusicDataRequest(urlRequest: URLRequest(url: libURL)) let dataResponse = try await request.response() print(dataResponse.debugDescription)               } catch { // I'm handling errors here }}
6
0
3.2k
Feb ’22
ApplicationMusicPlayer automatically turns Shuffle Mode On
Hi all, I am playing songs via ApplicationMusicPlayer with MusicKit for Swift and I am running into an issue where shuffle mode is sometimes automatically turned on. Even though there was no code related to shuffle mode. Here's a snippet //myQueue is an array of Tracks let player = ApplicationMusicPlayer.shared player.queue = ApplicationMusicPlayer.Queue(for: myQueue) // player.state.shuffleMode = .off // I tried explicitly setting shuffle mode off, which has no effect try await player.play() This issue is not easily reproducible, it occurs rarely and at random. But once this issue occurs it persists even after uninstalling and reinstalling the app. When I tried it to reproduce it with another device and everything works properly. Anyone else running into this issue? For Apple Engineers, here is the ticket: FB9816030
2
0
1.6k
Feb ’22
Can you explain canBecomeSubscriber
Based on a twitter thread this morning I want to be clear that before asking this I did run test code... What does MusicKit's MusicSubscription canBecomeSubscriber indicate. It is returning false for me - is that because I'm already a subscriber? The docs say it is "A capability that allows your app to present subscription offers for Apple Music." So will it return false if the user is already a subscriber? Is there a condition in which it returns false if your user is not already a subscriber? (i.e. is there any reason they can't subscribe from my app using the .musicSubscriptionOffer() modifier? Thank you, Daniel
2
0
1.4k
Feb ’22
Play songs from library
Hello, I was trying to use the new ApplicationMusicPlayerController with an album from the music library. In my test code I am searching for the artist "LUWTEN" in my personal library. In my personal library there are two albums that will be found. When I try to play the first album I will get the error MSVEntitlementUtilities - Process .. PID[648] - Group: com.apple.private.tcc.allow - Entitlement: kTCCServiceMediaLibrary - Entitled: NO - Error: (null). Is there a way to play library music with the new ApplicationMusicPlayerController?     func searchLuwten () {         struct MyAlbums : Codable {             let results : Result         }                  struct Result : Codable {             let libraryAlbums : Albums             enum CodingKeys: String, CodingKey {                 case libraryAlbums = "library-albums"             }         }         struct Albums : Codable {             let href : String             let data : [LibraryAlbum]         }                  struct LibraryAlbum : Codable, PlayableMusicItem {             let id : MusicItemID             let attributes : Attributes                          var playParameters: PlayParameters? {                 attributes.playParams             }         }                  struct Attributes : Codable {             let name : String             let playParams : PlayParameters         }                  detach {             let term = "LUWTEN"             var components = URLComponents()             components.scheme = "https"             components.host   = "api.music.apple.com"             components.path   = "/v1/me/library/search"             components.queryItems =  [                 URLQueryItem (name: "term", value: term),                 URLQueryItem (name: "types", value: "library-albums")             ]                          guard let url = components.url else {                 return             }                          let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url))             let dataResponse = try await dataRequest.response()                          let decoder = JSONDecoder()             let albumResponse = try decoder.decode (MyAlbums.self, from: dataResponse.data)             let player = ApplicationMusicPlayer.shared             player.setQueue (with: albumResponse.results.libraryAlbums.data[0])             player.play()                      }     }
5
0
1.8k
Feb ’22
Having trouble getting MusicKit Data into my SwiftUI view
Having trouble getting the data from MusicKit Searches back to my SwiftUI view. Normally I would use a @State variable for the decodable structure, but with MusicKit you dont need to decode anything and I can't figure out how to best get the data back to my view. I call the MusicKit function using .onAppear in my SwiftUI view func getAlbum() async throws -> MusicItemCollection<Album> {         var request = MusicCatalogResourceRequest<Album>(matching: \.id, equalTo: "1440881121")         let response = try await request.response()         print(response.debugDescription)         return response.items        printing debugDescription give these results: MusicCatalogResourceResponse<Album>(   items: [     Album(       id: "1440881121",       title: "Colors",       artistName: "Beck",       contentRating: "explicit",       copyright: Fonograf Records/Capitol Records; ℗ 2017 Fonograf Records, under exclusive license to UMG Recordings, Inc.,       genreNames: [         "Alternative",         "Music"       ],       isCompilation: false,       isComplete: true,       isDigitalMaster: true,       isSingle: false,       releaseDate: "2017-10-13",       trackCount: 11,       upc: "00602557176827" Hoe would you access all of the data in the debugDescription and use it in your SwiftUI view? I can only access the standard name and id. Is anyone using Codable to move the data through a data model? The documentation is not making sense to me even though I am usually ok using it. Also, should I be using `func getAlbum() async throws -> MusicCatalogResourceResponse {' instead of using MusicItemCollection? Thanks
8
0
2.7k
Jan ’22
Using MusicItemID
I have two questions related to the id for Playlist and Song. (1) There are two properties named id in each type. One is defined to be a MusicItemID and one is an ObjectIdentifier. How does one distinguish between them? What is the difference in how we are two use them. (2) Suppose the user has built a custom playlist of Song items in my app. Should I persist the MusicItemID and then retrieve the Song later using a MusicCatalogResourceRequest? It's a fun API to play with but there's little documentation. Thank you, Daniel
1
0
1.3k
Dec ’21
How to get recommendations based on multiple tracks?
Hello, I am trying to get recommendations based on the identifier of multiple tracks. But I am unable to get any results Here is the code I have used to try to get recommendations based on one album let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: URL(string: "https://api.music.apple.com/v1/me/recommendations/1571344275")!)) let dataResponse = try await dataRequest.response() For this I am getting the response: Failed to perform MusicDataRequest.Context(   url: https://api.music.apple.com/v1/me/recommendations/1571344275,   currentRetryCounts: [.other: 1] ) with MusicDataRequest.Error(   status: 404,   code: 40400,   title: "Resource Not Found",   detailText: "Resource with requested id was not found", I have tried used the identifier of songs/albums, to no success. What am I doing wrong?
1
0
2k
Dec ’21
Cannot decode library songs response for a non-Apple Music item
Hi there! I am fetching library songs using the endpoint: https://api.music.apple.com/v1/me/library/songs Here's the code snippet - struct Songs: Decodable { let data: [Song] } public func librarySongs() async throws { let url = URL(string: "https://api.music.apple.com/v1/me/library/songs")   guard let url = url else { return }   let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url)) do { let dataResponse = try await dataRequest.response() print(dataResponse.debugDescription) let response = try JSONDecoder().decode(Songs.self, from: dataResponse.data) } catch { print("**ERROR**") print(error) print(error.localizedDescription)     } } Trying to decode it using a custom struct gives me the following error. - **ERROR** keyNotFound(CodingKeys(stringValue: "artistName", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 20", intValue: 20), CodingKeys(stringValue: "attributes", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"artistName\", intValue: nil) (\"artistName\").", underlyingError: nil)) The data couldn’t be read because it is missing. On digging through the response, I found that one of the songs doesn't have an artist name because it is just a recording of my song I added to the library through GarageBand. In this case, what should be the approach?
6
0
2.6k
Dec ’21
Not getting results when searching MusicKit using MusicDataRequest
I am trying to use MusicDataRequest to search for albums, artists, and tracks using a search term like the album's names. If I use an albums Id, for example, I get results. The following code gets me results. First is the data model, the I have a class with code for authorization and then the function that searches using Music Kit struct MyAlbumResponse: Decodable {     let data: [Album] } class music {     @State var isAuthorizedForMusicKit = false     var searchTerm = ""     func requestMusicAuthorization() {         Task.detached {             let authorizationStatus = await MusicAuthorization.request()             if authorizationStatus == .authorized {                 self.isAuthorizedForMusicKit = true             } else {                 }             }         } func album() async throws {         let auth = requestMusicAuthorization()         let url = URL(string: "https://api.music.apple.com/v1/catalog/US/albums/1440881121")!         let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url))         let dataResponse = try await dataRequest.response()         let decoder = JSONDecoder()         let albumResponse = try decoder.decode(MyAlbumResponse.self, from: dataResponse.data)         print(albumResponse.data)     } The above code returns the results I expect. But If I change the function to use the search endpoint, I do not get any results. I dont get any errors either. The data model is also different. struct AlbumSearchResponse: Decodable {     var albums: Albums }      struct Albums: Codable {     var data: [AlbumsDatum]     var href, next: String } struct AlbumsDatum: Codable {     var attributes: Attributes     var href, id, type: String } struct Attributes: Codable {     var artistName: String     var artwork: Artwork     var copyright: String     var genreNames: [String]     var isComplete, isMasteredForItunes, isSingle: Bool     var name: String     var playParams: PlayParams     var releaseDate: String     var trackCount: Int     var url: String     var editorialNotes: EditorialNotes? } class music {     @State var isAuthorizedForMusicKit = false     var searchTerm = ""          func requestMusicAuthorization() {         Task.detached {             let authorizationStatus = await MusicAuthorization.request()             if authorizationStatus == .authorized {                 self.isAuthorizedForMusicKit = true             } else {                                                       }             }         } func musicSearch() async throws {         var auth = requestMusicAuthorization()         let url = URL(string: "https://api.music.apple.com/v1/catalog/us/search?term=colors&limit=10&types=albums")!         let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url))         let dataResponse = try await dataRequest.response()         let decoder = JSONDecoder()         let albumSearchResponse = try decoder.decode(AlbumSearchResponse.self, from: dataResponse.data)         print(albumSearchResponse.albums)         } I have gone through all of the documentation and example applications but can't find an answer to this. I dont understand why the search endpoint would be not usable, if that is the case. The workaround I have been using is to use MusicCatalogSearchRequest instead, but this does not return enough attributes and I cannot figure out how to have it return more attributes such as totalTracks, copyright, etc. I would be fine using the method if there is a way to add attributes to be included with the results. Here is the code I am using that works. It is also under the same class as the other functions. func Search() async throws {         let auth = requestMusicAuthorization()         let searchTerm = "colors"         Task {                 do {                     // Issue a catalog search request for albums matching the search term.                     var searchRequest = MusicCatalogSearchRequest(term: searchTerm, types: [Album.self])                     searchRequest.limit = 20                     let searchResponse = try await searchRequest.response()                     print(searchResponse)                 }             }         } any and all help is much appreciated. Thanks
2
0
1.5k
Nov ’21
Track vs Song and Disc Numbers
I'm trying to construct what I would consider a tracklist of an Album from MusicKit for Swift, but the steps I've had to take don't feel right to me, so I feel like I am missing something. The thing is that I don't just want a bag of tracks, I want their order (trackNumber) AND what disc they're on (discNumber), if more than one disc. At the moment, I can get to the tracks for the album with album.with([.tracks]) to get a (MusicItemCollection<Track>), but each Track DOES NOT have a discNumber. Which means, in multi disc Albums all the tracks are munged together. To get to the discNumber's, I've had to get an equivalent Song using the same ID, which DOES contain the discNumber, as well as the trackNumber. But this collection is not connected to the Album strongly, so I've had to create a containing struct to hold both the Album (which contains Track's) and some separate Song's, making my data model slightly more complex. (Note that you don't appear to be able to do album.with([.songs]) to skip getting to a Song via a Track.) I can't get my head around this structure - what is the difference between a Track and a Song? I would think that the difference is that a Track is a specific representation on a specific Album, and a Song is a more general representation that might be shared amongst more Album's (like compilations). But therefore, surely discNumber should actually be on the Track as it is specific to the Album, and arguably Song shouldn't even have trackNumber of it's own? In any case, can anyone help with what is the quickest way to get get the trackNumber's and discNumbers' of each actual track on an Album (starting with an MusicItemID) please? var songs = [Song]() let detailedAlbum = try await album.with([.tracks]) if let tracks = detailedAlbum.tracks { for track in tracks { let resourceRequest = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: track.id) async let resourceResponse = resourceRequest.response() guard let song = try await resourceResponse.items.first else { break } songs.append(song) } }
1
0
2.1k
Nov ’21
MusicKit business questions
Hi, Given yesterday's announcement of a new subscription I have some business/permissions questions about apps built on MusicKit. If this is not the place to ask them can you please direct me to where would be more appropriate. (1) When we test to see if someone has a subscription, is there any difference between a Voice subscription and a regular subscription? i.e. will we be able to query and vend any song in Apple Music if the user has a Voice sub the same way we can if they have an individual or family sub? (2) Can our app require that they have some sort of subscription or do we have to provide functionality even if they don't? (3) If I read the agreement correctly, we can't store music for offline listening but we can create and use a playlist in Music. Is there a way to prompt the user to download the music to this playlist? Thank you, Daniel
3
0
1.5k
Oct ’21
Playing Songs and Albums with MusicKit (Bug?)
Hi, I am trying to use MusicKit for playing a song or an album. I am using the following code for it: Tested on iPhone 11 Pro, iOS 15.0.1 @MainActor private func play<I: PlayableMusicItem>(_ item: I) async throws { let systemPlayer = SystemMusicPlayer.shared if !systemPlayer.isPreparedToPlay { try await systemPlayer.prepareToPlay() } let queue = systemPlayer.queue try await queue.insert(item, position: .afterCurrentEntry) try await systemPlayer.play() } Before I was using the "oldschool" way using the MusicPlayer framework as follows: @MainActor private func playOldschool(identifier: String) { let systemPlayer = MPMusicPlayerController.systemMusicPlayer if !systemPlayer.isPreparedToPlay { systemPlayer.prepareToPlay() } systemPlayer.setQueue(with: [identifier]) systemPlayer.play() } Both have been tested under the same conditions (permissions, same MusicSubscription), however the one using MusicKit does not seem to work well as try await systemPlayer.prepareToPlay() fails. If I remove the prepareForPlay code, it fails on play() with the same message and error as prepareForPlay. The logs show this: [SDKPlayback] prepareToPlay failed [no target descriptor] Error Domain=MPMusicPlayerControllerErrorDomain Code=1 "(null)" I could not find anything for that error domain and code 1, however prepareToPlay fails even in playOldschool, if I use the async variant of the function. At the moment I am staying with playOldschool, because that actually plays music. I wonder if I should file a radar for this or if there is any additional requirement for MusicKit that I haven't fulfilled causing it to fail. Any help is appreciated!
2
0
1.7k
Oct ’21
Understanding .musicSubscriptionOffer view modifier
Hi, I am trying to implement the new view modifier to show a subscription view for AppleMusic. In the #wwdc-10294 (Meet MusicKit for Swift) talk, the use case is very clear and it makes sense. We have an album and if the user wants to play it without an active subscription, we show toggle the binding that makes the view appear. However, using a single boolean removes flexibility so I am trying to figure out the following: I have two buttons: "Play" and "Add", which will either play the album as in the talk or add it to the user library using the Apple MusicAPI. In both cases if there is no subscription, I'd like to show the offer. This means I will have two different MusicSubscriptionOffer.Options configuration, one where the messageIdentifier is .playMusic and the other one .addMusic. This also means that I need to use two viewModifiers .musicSubscriptionOffer resulting in two different bindings (otherwise I assume both view controllers will show when the binding changes?). Worst case is if I show a list of songs and want to have options for .playMusic and .addMusic for each song, then I need a dynamic amount of bindings for the view modifier. Would I need to handle this like list bindings? Ideally I'd like an API that allows me to pass in an enum and provide options based on the enum cases like other SwiftUI API (like .sheet). This would allow for dynamic options and only requiring one single .musicSubscriptionOffer. Is there any solid solution for this at the moment? Any insights would be helpful, thanks!
1
0
801
Oct ’21
MusicKit Requests & Localization
Hi 👋 I would like to retrieve Apple Music resources in a specific language. For example, I live in France, but my iPhone language is set to English (UK), unfortunately, it seems like MusicKit is always fetching resources in French (I guess it's the default language associated to my Apple Music account). I know it's possible to specify the language code through the URL (using the l query parameter), but I really would like to use MusicDataRequest as less as possible. Could you please consider adding a way to do this? Regards, Julien
5
0
1.3k
Oct ’21
Getting the album title from the queue's current entry item
I'm trying to get the album title from the current entry in the Queue of ApplicationMusicPlayer. I'm using the following code for this purpose - let queue = ApplicationMusicPlayer.shared.queue let item = queue.currentEntry?.item if case let .song(song) = item { print(song.albumTitle) } But it is returning a nil value. If I print the debug description of the song, I get the other details. Song(   id: "1524813873",   title: "'Til We Die",   artistName: "Slipknot",   discNumber: 1,   hasLyrics: "false",   releaseDate: "2008-08-20",   trackNumber: 15 ) If I do a request to MusicCatalogResourceRequest<Song>, and print the debug description, I get the album title as well. let queue = ApplicationMusicPlayer.shared.queue let item = queue.currentEntry?.item if case let .song(song) = item { do { let musicRequest = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: song.id) let response = try await musicRequest.response() print(response.items.first.debugDescription) } catch { print(error) } } Song(   id: "1524813873",   title: "'Til We Die",   albumTitle: "All Hope Is Gone (Deluxe Edition)",   artistName: "Slipknot",   composerName: Chris Fehn, Corey Taylor, Craig Jones, Jim Root, Joey Jordison, Mick Thomson, Paul Gray, Shawn "Clown" Crahan & Sid Wilson,   discNumber: 1,   duration: 345.693,   genreNames: [     "Metal",     "Music",     "Rock"   ],   hasLyrics: "true",   isrc: "NLA320887352",   releaseDate: "2008-08-20",   trackNumber: 15 ) Is this a bug or intentional? Thanks!
3
0
1.3k
Sep ’21
Setting playbackRate in MusicKit for Swift (beta)
Hi! I'm trying out the new MusicKit for Swift (iOS 15 beta). Really liking it! But I'm seeing a weird issue with setting state.playbackRate to anything other than 1.0 when playing. The issue appears to be that the player keeps trying to reset itself to a playbackRate of 1.0. If I set it to 0.5 or 2.0 it still plays the track back at a playbackRate of 1.0. Strangely, about 5% of attempts to change it are actually successful on the ApplicationMusicPlayer. SystemMusicPlayer changes more reliably at maybe a 20% success rate, in my testing. It seems to be happening during/after preparing the track. If I swap out the player's queue while it's preparing to play, I can get it to reliably throw a prepareToPlay error and, upon doing so, it then accepts the playbackRate change to a non-1.0 value. I've tried setting the playbackRate before playing, after playing, and even both! Through observing the playbackRate property I can see that my alterations seem to stick for maybe 0.5 sec as the track loads, then the rate changes back to 1.0 as the song begins to play. I understand it's still in beta so if the answer is simply "wait for release" then that's fine! But I'd like to know if anyone has found a way to reliably alter the playbackRate, as I'm in the middle of scouting a new project that will depend on it. Some detail into my setup: I'm doing this in the WWDC21 project UsingMusicKitToIntegrateWithAppleMusic (which didn't compile out of the box for me until I reworked some of it). My setup is: iPadOS 15 beta 5 (19a5318f) on an iPad Air 2. Xcode v13.0 beta 5 (13A5212g). MacOS Big Sur 11.4. Thanks!
1
0
1.6k
Sep ’21
Retrieving a list of tracks from a library playlist
I'm trying to do something that I though would be simple; however, I'm tripping up on something... In brief, I want to get all tracks from a playlist in an AppleMusic authorized user's playlist with the following function:     func getTracksFromAppleMusicPlaylist(playlistId: MusicItemID) async throws -> [Track]? {         print("Fetching AppleMusic Playlists...")         print("Playlist ID: \(playlistId)")         var playlistTracks: [Track]? = []         do {             var playlistRequest = MusicCatalogResourceRequest<Playlist>(matching: \.id, equalTo: playlistId )             playlistRequest.properties = [.tracks]             let playlistResponse = try await playlistRequest.response()             print("Playlist Response: \(playlistResponse)")             let playlistWithTracks = playlistResponse.items             let tracks = playlistWithTracks.flatMap { playlist -> MusicItemCollection<Track> in                 playlist.tracks ?? []             }             playlistTracks = tracks.count > 1 ? tracks : nil         } catch {             print("Error", error)             // handle error         }         return playlistTracks     } This function results in the following error: 2021-08-28 04:25:14.335455+0700 App[90763:6707890] [DataRequesting] Failed to perform MusicDataRequest.Context(   url: https://api.music.apple.com/v1/catalog/us/playlists/p.7XxxsXxXXxX?include=tracks&omit%5Bresource%5D=autos,   currentRetryCounts: [.other: 1] ) with MusicDataRequest.Error(   status: 404,   code: 40400,   title: "Resource Not Found",   detailText: "Resource with requested id was not found",   id: "QMK7GH4U7ITPMUTTBKIOMXXXX",   originalResponse: MusicDataResponse(     data: 159 bytes,     urlResponse: <NSHTTPURLResponse: 0x00000002820c0b60>   ) ). The playlistID being used is a value that has been picked from an existing playlist in the user's library, via a function that uses this code snippet: if let url = URL(string: "https://api.music.apple.com/v1/me/library/playlists?limit=100") {                let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url)) ... The only thing I can think of is that the playlistId from the above snippet is converted to a string when decoding into a struct, after which it is changed back to a MusicItemID with an init, like MusicItemID(playlistId). Any thoughts? Because I'm at a loss...
10
0
3.8k
Sep ’21
Any way to check if song is in Library with MusicKit? ?relate=library not working for me
For my app that heavily uses both ShazamKit and MusicKit, I need to be able to check if the matched song is in the user's library or not. I couldn't find an easy way to do this with MusicKit so I first then turned to the Apple Music API as they introduced the new parameter ?relate=library during this year's WWDC. When I first tried it, it worked as expected, and was very happy to have gotten it to work. However, it stopped working lately and now I turned to use MPMediaLibrary as that still works but is a lot slower in performance. Anyone has any idea why the ?relate=library stopped working for me or know a better way to check if the Song exists in the user's library? func checkInLibrary(from appleMusicID: String) async { do { let countryCode = try await MusicDataRequest.currentCountryCode let libURL = URL(string: "https://api.music.apple.com/v1/catalog/\(countryCode)/songs/\(appleMusicID)?relate=library")! let request = MusicDataRequest(urlRequest: URLRequest(url: libURL)) let dataResponse = try await request.response() print(dataResponse.debugDescription)               } catch { // I'm handling errors here }}
Replies
6
Boosts
0
Views
3.2k
Activity
Feb ’22
ApplicationMusicPlayer automatically turns Shuffle Mode On
Hi all, I am playing songs via ApplicationMusicPlayer with MusicKit for Swift and I am running into an issue where shuffle mode is sometimes automatically turned on. Even though there was no code related to shuffle mode. Here's a snippet //myQueue is an array of Tracks let player = ApplicationMusicPlayer.shared player.queue = ApplicationMusicPlayer.Queue(for: myQueue) // player.state.shuffleMode = .off // I tried explicitly setting shuffle mode off, which has no effect try await player.play() This issue is not easily reproducible, it occurs rarely and at random. But once this issue occurs it persists even after uninstalling and reinstalling the app. When I tried it to reproduce it with another device and everything works properly. Anyone else running into this issue? For Apple Engineers, here is the ticket: FB9816030
Replies
2
Boosts
0
Views
1.6k
Activity
Feb ’22
Can you explain canBecomeSubscriber
Based on a twitter thread this morning I want to be clear that before asking this I did run test code... What does MusicKit's MusicSubscription canBecomeSubscriber indicate. It is returning false for me - is that because I'm already a subscriber? The docs say it is "A capability that allows your app to present subscription offers for Apple Music." So will it return false if the user is already a subscriber? Is there a condition in which it returns false if your user is not already a subscriber? (i.e. is there any reason they can't subscribe from my app using the .musicSubscriptionOffer() modifier? Thank you, Daniel
Replies
2
Boosts
0
Views
1.4k
Activity
Feb ’22
Play songs from library
Hello, I was trying to use the new ApplicationMusicPlayerController with an album from the music library. In my test code I am searching for the artist "LUWTEN" in my personal library. In my personal library there are two albums that will be found. When I try to play the first album I will get the error MSVEntitlementUtilities - Process .. PID[648] - Group: com.apple.private.tcc.allow - Entitlement: kTCCServiceMediaLibrary - Entitled: NO - Error: (null). Is there a way to play library music with the new ApplicationMusicPlayerController?     func searchLuwten () {         struct MyAlbums : Codable {             let results : Result         }                  struct Result : Codable {             let libraryAlbums : Albums             enum CodingKeys: String, CodingKey {                 case libraryAlbums = "library-albums"             }         }         struct Albums : Codable {             let href : String             let data : [LibraryAlbum]         }                  struct LibraryAlbum : Codable, PlayableMusicItem {             let id : MusicItemID             let attributes : Attributes                          var playParameters: PlayParameters? {                 attributes.playParams             }         }                  struct Attributes : Codable {             let name : String             let playParams : PlayParameters         }                  detach {             let term = "LUWTEN"             var components = URLComponents()             components.scheme = "https"             components.host   = "api.music.apple.com"             components.path   = "/v1/me/library/search"             components.queryItems =  [                 URLQueryItem (name: "term", value: term),                 URLQueryItem (name: "types", value: "library-albums")             ]                          guard let url = components.url else {                 return             }                          let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url))             let dataResponse = try await dataRequest.response()                          let decoder = JSONDecoder()             let albumResponse = try decoder.decode (MyAlbums.self, from: dataResponse.data)             let player = ApplicationMusicPlayer.shared             player.setQueue (with: albumResponse.results.libraryAlbums.data[0])             player.play()                      }     }
Replies
5
Boosts
0
Views
1.8k
Activity
Feb ’22
Cache SwiftUI ArtworkImage
Hi, is there a way to cache the data from the SwiftUI ArtworkImage across App starts and maybe even for offline use? Best regards Julian
Replies
3
Boosts
0
Views
1.5k
Activity
Jan ’22
Having trouble getting MusicKit Data into my SwiftUI view
Having trouble getting the data from MusicKit Searches back to my SwiftUI view. Normally I would use a @State variable for the decodable structure, but with MusicKit you dont need to decode anything and I can't figure out how to best get the data back to my view. I call the MusicKit function using .onAppear in my SwiftUI view func getAlbum() async throws -> MusicItemCollection<Album> {         var request = MusicCatalogResourceRequest<Album>(matching: \.id, equalTo: "1440881121")         let response = try await request.response()         print(response.debugDescription)         return response.items        printing debugDescription give these results: MusicCatalogResourceResponse<Album>(   items: [     Album(       id: "1440881121",       title: "Colors",       artistName: "Beck",       contentRating: "explicit",       copyright: Fonograf Records/Capitol Records; ℗ 2017 Fonograf Records, under exclusive license to UMG Recordings, Inc.,       genreNames: [         "Alternative",         "Music"       ],       isCompilation: false,       isComplete: true,       isDigitalMaster: true,       isSingle: false,       releaseDate: "2017-10-13",       trackCount: 11,       upc: "00602557176827" Hoe would you access all of the data in the debugDescription and use it in your SwiftUI view? I can only access the standard name and id. Is anyone using Codable to move the data through a data model? The documentation is not making sense to me even though I am usually ok using it. Also, should I be using `func getAlbum() async throws -> MusicCatalogResourceResponse {' instead of using MusicItemCollection? Thanks
Replies
8
Boosts
0
Views
2.7k
Activity
Jan ’22
Using MusicItemID
I have two questions related to the id for Playlist and Song. (1) There are two properties named id in each type. One is defined to be a MusicItemID and one is an ObjectIdentifier. How does one distinguish between them? What is the difference in how we are two use them. (2) Suppose the user has built a custom playlist of Song items in my app. Should I persist the MusicItemID and then retrieve the Song later using a MusicCatalogResourceRequest? It's a fun API to play with but there's little documentation. Thank you, Daniel
Replies
1
Boosts
0
Views
1.3k
Activity
Dec ’21
How to get recommendations based on multiple tracks?
Hello, I am trying to get recommendations based on the identifier of multiple tracks. But I am unable to get any results Here is the code I have used to try to get recommendations based on one album let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: URL(string: "https://api.music.apple.com/v1/me/recommendations/1571344275")!)) let dataResponse = try await dataRequest.response() For this I am getting the response: Failed to perform MusicDataRequest.Context(   url: https://api.music.apple.com/v1/me/recommendations/1571344275,   currentRetryCounts: [.other: 1] ) with MusicDataRequest.Error(   status: 404,   code: 40400,   title: "Resource Not Found",   detailText: "Resource with requested id was not found", I have tried used the identifier of songs/albums, to no success. What am I doing wrong?
Replies
1
Boosts
0
Views
2k
Activity
Dec ’21
Cannot decode library songs response for a non-Apple Music item
Hi there! I am fetching library songs using the endpoint: https://api.music.apple.com/v1/me/library/songs Here's the code snippet - struct Songs: Decodable { let data: [Song] } public func librarySongs() async throws { let url = URL(string: "https://api.music.apple.com/v1/me/library/songs")   guard let url = url else { return }   let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url)) do { let dataResponse = try await dataRequest.response() print(dataResponse.debugDescription) let response = try JSONDecoder().decode(Songs.self, from: dataResponse.data) } catch { print("**ERROR**") print(error) print(error.localizedDescription)     } } Trying to decode it using a custom struct gives me the following error. - **ERROR** keyNotFound(CodingKeys(stringValue: "artistName", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 20", intValue: 20), CodingKeys(stringValue: "attributes", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"artistName\", intValue: nil) (\"artistName\").", underlyingError: nil)) The data couldn’t be read because it is missing. On digging through the response, I found that one of the songs doesn't have an artist name because it is just a recording of my song I added to the library through GarageBand. In this case, what should be the approach?
Replies
6
Boosts
0
Views
2.6k
Activity
Dec ’21
Not getting results when searching MusicKit using MusicDataRequest
I am trying to use MusicDataRequest to search for albums, artists, and tracks using a search term like the album's names. If I use an albums Id, for example, I get results. The following code gets me results. First is the data model, the I have a class with code for authorization and then the function that searches using Music Kit struct MyAlbumResponse: Decodable {     let data: [Album] } class music {     @State var isAuthorizedForMusicKit = false     var searchTerm = ""     func requestMusicAuthorization() {         Task.detached {             let authorizationStatus = await MusicAuthorization.request()             if authorizationStatus == .authorized {                 self.isAuthorizedForMusicKit = true             } else {                 }             }         } func album() async throws {         let auth = requestMusicAuthorization()         let url = URL(string: "https://api.music.apple.com/v1/catalog/US/albums/1440881121")!         let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url))         let dataResponse = try await dataRequest.response()         let decoder = JSONDecoder()         let albumResponse = try decoder.decode(MyAlbumResponse.self, from: dataResponse.data)         print(albumResponse.data)     } The above code returns the results I expect. But If I change the function to use the search endpoint, I do not get any results. I dont get any errors either. The data model is also different. struct AlbumSearchResponse: Decodable {     var albums: Albums }      struct Albums: Codable {     var data: [AlbumsDatum]     var href, next: String } struct AlbumsDatum: Codable {     var attributes: Attributes     var href, id, type: String } struct Attributes: Codable {     var artistName: String     var artwork: Artwork     var copyright: String     var genreNames: [String]     var isComplete, isMasteredForItunes, isSingle: Bool     var name: String     var playParams: PlayParams     var releaseDate: String     var trackCount: Int     var url: String     var editorialNotes: EditorialNotes? } class music {     @State var isAuthorizedForMusicKit = false     var searchTerm = ""          func requestMusicAuthorization() {         Task.detached {             let authorizationStatus = await MusicAuthorization.request()             if authorizationStatus == .authorized {                 self.isAuthorizedForMusicKit = true             } else {                                                       }             }         } func musicSearch() async throws {         var auth = requestMusicAuthorization()         let url = URL(string: "https://api.music.apple.com/v1/catalog/us/search?term=colors&limit=10&types=albums")!         let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url))         let dataResponse = try await dataRequest.response()         let decoder = JSONDecoder()         let albumSearchResponse = try decoder.decode(AlbumSearchResponse.self, from: dataResponse.data)         print(albumSearchResponse.albums)         } I have gone through all of the documentation and example applications but can't find an answer to this. I dont understand why the search endpoint would be not usable, if that is the case. The workaround I have been using is to use MusicCatalogSearchRequest instead, but this does not return enough attributes and I cannot figure out how to have it return more attributes such as totalTracks, copyright, etc. I would be fine using the method if there is a way to add attributes to be included with the results. Here is the code I am using that works. It is also under the same class as the other functions. func Search() async throws {         let auth = requestMusicAuthorization()         let searchTerm = "colors"         Task {                 do {                     // Issue a catalog search request for albums matching the search term.                     var searchRequest = MusicCatalogSearchRequest(term: searchTerm, types: [Album.self])                     searchRequest.limit = 20                     let searchResponse = try await searchRequest.response()                     print(searchResponse)                 }             }         } any and all help is much appreciated. Thanks
Replies
2
Boosts
0
Views
1.5k
Activity
Nov ’21
Track vs Song and Disc Numbers
I'm trying to construct what I would consider a tracklist of an Album from MusicKit for Swift, but the steps I've had to take don't feel right to me, so I feel like I am missing something. The thing is that I don't just want a bag of tracks, I want their order (trackNumber) AND what disc they're on (discNumber), if more than one disc. At the moment, I can get to the tracks for the album with album.with([.tracks]) to get a (MusicItemCollection<Track>), but each Track DOES NOT have a discNumber. Which means, in multi disc Albums all the tracks are munged together. To get to the discNumber's, I've had to get an equivalent Song using the same ID, which DOES contain the discNumber, as well as the trackNumber. But this collection is not connected to the Album strongly, so I've had to create a containing struct to hold both the Album (which contains Track's) and some separate Song's, making my data model slightly more complex. (Note that you don't appear to be able to do album.with([.songs]) to skip getting to a Song via a Track.) I can't get my head around this structure - what is the difference between a Track and a Song? I would think that the difference is that a Track is a specific representation on a specific Album, and a Song is a more general representation that might be shared amongst more Album's (like compilations). But therefore, surely discNumber should actually be on the Track as it is specific to the Album, and arguably Song shouldn't even have trackNumber of it's own? In any case, can anyone help with what is the quickest way to get get the trackNumber's and discNumbers' of each actual track on an Album (starting with an MusicItemID) please? var songs = [Song]() let detailedAlbum = try await album.with([.tracks]) if let tracks = detailedAlbum.tracks { for track in tracks { let resourceRequest = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: track.id) async let resourceResponse = resourceRequest.response() guard let song = try await resourceResponse.items.first else { break } songs.append(song) } }
Replies
1
Boosts
0
Views
2.1k
Activity
Nov ’21
MusicKit search for the new Siri playlists
How do we use MusicKit to search for the new Siri playlists for mood etc? I understand that Voice subscribers can't search for songs using API searches - I'm asking a question in the opposite direction. If full subscribers know the names of the new playlists they can access and play them - are there searches that will surface them? Thank you Daniel
Replies
0
Boosts
0
Views
956
Activity
Nov ’21
MusicKit business questions
Hi, Given yesterday's announcement of a new subscription I have some business/permissions questions about apps built on MusicKit. If this is not the place to ask them can you please direct me to where would be more appropriate. (1) When we test to see if someone has a subscription, is there any difference between a Voice subscription and a regular subscription? i.e. will we be able to query and vend any song in Apple Music if the user has a Voice sub the same way we can if they have an individual or family sub? (2) Can our app require that they have some sort of subscription or do we have to provide functionality even if they don't? (3) If I read the agreement correctly, we can't store music for offline listening but we can create and use a playlist in Music. Is there a way to prompt the user to download the music to this playlist? Thank you, Daniel
Replies
3
Boosts
0
Views
1.5k
Activity
Oct ’21
Playing Songs and Albums with MusicKit (Bug?)
Hi, I am trying to use MusicKit for playing a song or an album. I am using the following code for it: Tested on iPhone 11 Pro, iOS 15.0.1 @MainActor private func play<I: PlayableMusicItem>(_ item: I) async throws { let systemPlayer = SystemMusicPlayer.shared if !systemPlayer.isPreparedToPlay { try await systemPlayer.prepareToPlay() } let queue = systemPlayer.queue try await queue.insert(item, position: .afterCurrentEntry) try await systemPlayer.play() } Before I was using the "oldschool" way using the MusicPlayer framework as follows: @MainActor private func playOldschool(identifier: String) { let systemPlayer = MPMusicPlayerController.systemMusicPlayer if !systemPlayer.isPreparedToPlay { systemPlayer.prepareToPlay() } systemPlayer.setQueue(with: [identifier]) systemPlayer.play() } Both have been tested under the same conditions (permissions, same MusicSubscription), however the one using MusicKit does not seem to work well as try await systemPlayer.prepareToPlay() fails. If I remove the prepareForPlay code, it fails on play() with the same message and error as prepareForPlay. The logs show this: [SDKPlayback] prepareToPlay failed [no target descriptor] Error Domain=MPMusicPlayerControllerErrorDomain Code=1 "(null)" I could not find anything for that error domain and code 1, however prepareToPlay fails even in playOldschool, if I use the async variant of the function. At the moment I am staying with playOldschool, because that actually plays music. I wonder if I should file a radar for this or if there is any additional requirement for MusicKit that I haven't fulfilled causing it to fail. Any help is appreciated!
Replies
2
Boosts
0
Views
1.7k
Activity
Oct ’21
Understanding .musicSubscriptionOffer view modifier
Hi, I am trying to implement the new view modifier to show a subscription view for AppleMusic. In the #wwdc-10294 (Meet MusicKit for Swift) talk, the use case is very clear and it makes sense. We have an album and if the user wants to play it without an active subscription, we show toggle the binding that makes the view appear. However, using a single boolean removes flexibility so I am trying to figure out the following: I have two buttons: "Play" and "Add", which will either play the album as in the talk or add it to the user library using the Apple MusicAPI. In both cases if there is no subscription, I'd like to show the offer. This means I will have two different MusicSubscriptionOffer.Options configuration, one where the messageIdentifier is .playMusic and the other one .addMusic. This also means that I need to use two viewModifiers .musicSubscriptionOffer resulting in two different bindings (otherwise I assume both view controllers will show when the binding changes?). Worst case is if I show a list of songs and want to have options for .playMusic and .addMusic for each song, then I need a dynamic amount of bindings for the view modifier. Would I need to handle this like list bindings? Ideally I'd like an API that allows me to pass in an enum and provide options based on the enum cases like other SwiftUI API (like .sheet). This would allow for dynamic options and only requiring one single .musicSubscriptionOffer. Is there any solid solution for this at the moment? Any insights would be helpful, thanks!
Replies
1
Boosts
0
Views
801
Activity
Oct ’21
MusicKit Requests & Localization
Hi 👋 I would like to retrieve Apple Music resources in a specific language. For example, I live in France, but my iPhone language is set to English (UK), unfortunately, it seems like MusicKit is always fetching resources in French (I guess it's the default language associated to my Apple Music account). I know it's possible to specify the language code through the URL (using the l query parameter), but I really would like to use MusicDataRequest as less as possible. Could you please consider adding a way to do this? Regards, Julien
Replies
5
Boosts
0
Views
1.3k
Activity
Oct ’21
Getting the album title from the queue's current entry item
I'm trying to get the album title from the current entry in the Queue of ApplicationMusicPlayer. I'm using the following code for this purpose - let queue = ApplicationMusicPlayer.shared.queue let item = queue.currentEntry?.item if case let .song(song) = item { print(song.albumTitle) } But it is returning a nil value. If I print the debug description of the song, I get the other details. Song(   id: "1524813873",   title: "'Til We Die",   artistName: "Slipknot",   discNumber: 1,   hasLyrics: "false",   releaseDate: "2008-08-20",   trackNumber: 15 ) If I do a request to MusicCatalogResourceRequest<Song>, and print the debug description, I get the album title as well. let queue = ApplicationMusicPlayer.shared.queue let item = queue.currentEntry?.item if case let .song(song) = item { do { let musicRequest = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: song.id) let response = try await musicRequest.response() print(response.items.first.debugDescription) } catch { print(error) } } Song(   id: "1524813873",   title: "'Til We Die",   albumTitle: "All Hope Is Gone (Deluxe Edition)",   artistName: "Slipknot",   composerName: Chris Fehn, Corey Taylor, Craig Jones, Jim Root, Joey Jordison, Mick Thomson, Paul Gray, Shawn "Clown" Crahan & Sid Wilson,   discNumber: 1,   duration: 345.693,   genreNames: [     "Metal",     "Music",     "Rock"   ],   hasLyrics: "true",   isrc: "NLA320887352",   releaseDate: "2008-08-20",   trackNumber: 15 ) Is this a bug or intentional? Thanks!
Replies
3
Boosts
0
Views
1.3k
Activity
Sep ’21
How to fetch content like the Browse tab in Apple Music
Hello, Is there any way to get content like what is displayed in the Browse tab of Apple Music using MusicKit or Apple Music API? I checked the contents of the default recommendations in the Apple Music API, and these were my personal recommendations. Is it possible to get common recommendations?
Replies
1
Boosts
0
Views
1.1k
Activity
Sep ’21
Setting playbackRate in MusicKit for Swift (beta)
Hi! I'm trying out the new MusicKit for Swift (iOS 15 beta). Really liking it! But I'm seeing a weird issue with setting state.playbackRate to anything other than 1.0 when playing. The issue appears to be that the player keeps trying to reset itself to a playbackRate of 1.0. If I set it to 0.5 or 2.0 it still plays the track back at a playbackRate of 1.0. Strangely, about 5% of attempts to change it are actually successful on the ApplicationMusicPlayer. SystemMusicPlayer changes more reliably at maybe a 20% success rate, in my testing. It seems to be happening during/after preparing the track. If I swap out the player's queue while it's preparing to play, I can get it to reliably throw a prepareToPlay error and, upon doing so, it then accepts the playbackRate change to a non-1.0 value. I've tried setting the playbackRate before playing, after playing, and even both! Through observing the playbackRate property I can see that my alterations seem to stick for maybe 0.5 sec as the track loads, then the rate changes back to 1.0 as the song begins to play. I understand it's still in beta so if the answer is simply "wait for release" then that's fine! But I'd like to know if anyone has found a way to reliably alter the playbackRate, as I'm in the middle of scouting a new project that will depend on it. Some detail into my setup: I'm doing this in the WWDC21 project UsingMusicKitToIntegrateWithAppleMusic (which didn't compile out of the box for me until I reworked some of it). My setup is: iPadOS 15 beta 5 (19a5318f) on an iPad Air 2. Xcode v13.0 beta 5 (13A5212g). MacOS Big Sur 11.4. Thanks!
Replies
1
Boosts
0
Views
1.6k
Activity
Sep ’21
Retrieving a list of tracks from a library playlist
I'm trying to do something that I though would be simple; however, I'm tripping up on something... In brief, I want to get all tracks from a playlist in an AppleMusic authorized user's playlist with the following function:     func getTracksFromAppleMusicPlaylist(playlistId: MusicItemID) async throws -> [Track]? {         print("Fetching AppleMusic Playlists...")         print("Playlist ID: \(playlistId)")         var playlistTracks: [Track]? = []         do {             var playlistRequest = MusicCatalogResourceRequest<Playlist>(matching: \.id, equalTo: playlistId )             playlistRequest.properties = [.tracks]             let playlistResponse = try await playlistRequest.response()             print("Playlist Response: \(playlistResponse)")             let playlistWithTracks = playlistResponse.items             let tracks = playlistWithTracks.flatMap { playlist -> MusicItemCollection<Track> in                 playlist.tracks ?? []             }             playlistTracks = tracks.count > 1 ? tracks : nil         } catch {             print("Error", error)             // handle error         }         return playlistTracks     } This function results in the following error: 2021-08-28 04:25:14.335455+0700 App[90763:6707890] [DataRequesting] Failed to perform MusicDataRequest.Context(   url: https://api.music.apple.com/v1/catalog/us/playlists/p.7XxxsXxXXxX?include=tracks&omit%5Bresource%5D=autos,   currentRetryCounts: [.other: 1] ) with MusicDataRequest.Error(   status: 404,   code: 40400,   title: "Resource Not Found",   detailText: "Resource with requested id was not found",   id: "QMK7GH4U7ITPMUTTBKIOMXXXX",   originalResponse: MusicDataResponse(     data: 159 bytes,     urlResponse: <NSHTTPURLResponse: 0x00000002820c0b60>   ) ). The playlistID being used is a value that has been picked from an existing playlist in the user's library, via a function that uses this code snippet: if let url = URL(string: "https://api.music.apple.com/v1/me/library/playlists?limit=100") {                let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url)) ... The only thing I can think of is that the playlistId from the above snippet is converted to a string when decoding into a struct, after which it is changed back to a MusicItemID with an init, like MusicItemID(playlistId). Any thoughts? Because I'm at a loss...
Replies
10
Boosts
0
Views
3.8k
Activity
Sep ’21