Explore more content with MusicKit

RSS for tag

Discuss the WWDC22 Session Explore more content with MusicKit

Posts under wwdc2022-110347 tag

22 Posts

Post

Replies

Boosts

Views

Activity

Editing a Library Playlist (MusicKit: iOS 16 beta)
I've just begun to dip my toes into the iOS16 waters. One of the first things that I've attempted is to edit a library playlist using: try await MusicLibrary.shared.edit(targetPlaylist, items: tracksToAdd) Where targetPlaylist is of type MusicItemCollection<MusicKit.Playlist>.Element and tracksToAdd is of type [Track] The targetPlaylist was created, using new iOS16 way, here: let newPlaylist = try await MusicLibrary.shared.createPlaylist(name: name, description: description) tracksToAdd is derived by performing a MusicLibraryRequest on a specific playlist ID, and then doing something like this: if let tracksToAdd = try await playlist.with(.tracks).tracks {    // add tracks to target playlist } My problem is that when I perform attempt the edit, I am faced with a rather sad looking crash. libdispatch.dylib`dispatch_group_leave.cold.1:     0x10b43d62c <+0>:  mov    x8, #0x0     0x10b43d630 <+4>:  stp    x20, x21, [sp, #-0x10]!     0x10b43d634 <+8>:  adrp   x20, 6     0x10b43d638 <+12>: add    x20, x20, #0xfbf          ; "BUG IN CLIENT OF LIBDISPATCH: Unbalanced call to dispatch_group_leave()"     0x10b43d63c <+16>: adrp   x21, 40     0x10b43d640 <+20>: add    x21, x21, #0x260          ; gCRAnnotations     0x10b43d644 <+24>: str    x20, [x21, #0x8]     0x10b43d648 <+28>: str    x8, [x21, #0x38]     0x10b43d64c <+32>: ldp    x20, x21, [sp], #0x10 ->  0x10b43d650 <+36>: brk    #0x1 I assume that I must be doing something wrong, but I frankly have no idea how to troubleshoot this. Any help would be most appreciated. Thanks. @david-apple?
11
0
3.5k
May ’25
Thoughts on MusicLibraryRequest as a replacement for MPMediaQuery
I'm very excited about the new MusicLibrary API, but after a couple of days of playing around with it, I have to say that I find the implementation of filtering MusicLibraryRequests a little confusing. MPMediaQuery has a fairly extensive list of predicates that can be applied, including string and persistentID comparisons for artist, album artist genre, and more. It also lets you filter on an item’s title. MusicLibraryRequests let you filter on the item’s ID, or on its MusicKit Artist and Genre relationships. To me, this seems like it adds an extra step.  With an MPMediaQuery, if I wanted to fetch every album by a given artist, I’d apply an MPMediaPropertyPredicate looking at MPMediaItemPropertyAlbumArtist and compare the string. It was also easy to change the MPMediaPredicateComparison to .contains to match more widely. If I wanted to surface albums by “Aesop Rock” or “Aesop Rock & Blockhead,” I could use that. In the MusicLibraryRequest implementation, it looks like I need to perform a MusicLibraryRequest<Artist> first in order to get the Artist objects. There’s no filter for the name property, so if I don’t have their IDs, I’ve got to use filter(text:). From there, I can take the results of that request and apply them to my MusicLibraryRequest<Album> using the filter(matching:memberOf) function.  I could use filter(text:) on the MusicLibraryRequest<Album>, but that filters across multiple properties (title and artistName?) and is less precise than defining the actual property I want to match against. I think my ideal version of the MusicLibraryRequest API would offer something like filter(matching:equalTo:) or filter(matching:contains:) that worked off of KeyPaths rather than relationships. That seems more intuitive to me. I’m not saying we need every property from every filterable MPMediaItemProperty key, but I’d love to be able to do it on title, artistName, and other common metadata. That might look something like: filter(matching: \.title, contains: “Abbey Road”) filter(matching: \.artistName, equalTo: “Between The Buried And Me”) I noticed that filter(text:) is case insensitive, which is awesome, and something I’ve wanted for a long time in MPMediaPropertyPredicate. As a bonus, it would be great if a KeyPath based filter API supported a case sensitivity flag. This is less of a problem when dealing with Apple Music catalog content, but users’ libraries are a harsh environment, and you might have an artist “Between The Buried And Me” and one called “Between the Buried and Me.” It would be great to get albums from both with something like: filter(matching: \.artistName, equalTo: “Between The Buried And Me”, caseSensitive: false)  I've submitted the above as FB10185685. I also submitted another feedback this morning regarding filter(text:) and repeating text as FB10184823. My last wishlist item for this API (for the time being!) is exposing the MPMediaItemPropertyAlbumPersistentID as an available filter attribute. I know, I know… hear me out. If you take a look at the other thread I made today, you’ll see that due to missing metadata in MusicKit, I still have some use cases where I need to be able to reference an MPMediaItem and might need to fetch its containing MPMediaItemCollection to get at other tracks on the album. It would be nice to seamlessly be able to fetch the MPMediaItemCollection or the library Album using a shared identifier, especially when it comes to being able to play the album in MusicKit’s player rather than Media Player’s.  I've submitted that list bit as FB10185789 Thanks for bearing with my walls of text today. Keep up the great work!
11
1
4.1k
Nov ’24
iOS 16 Music Kit MusicLibrary methods have stopped working
Hi there, I have a related forum thread here and a Feedback Assistant ticket open, but this issue seems different. Sometime within the last 2-3 weeks, code related to MusicLibrary has stopped working. None of my code has changed. For example, the below two snippets used to work fine:  for track in newTracks {    try await MusicLibrary.shared.add(track, to: targetPlaylist)  } try await MusicLibrary.shared.edit(targetPlaylist, items: items) newTracks and items are both fetched using: try await targetPlaylist.with(.tracks, preferredSource: .catalog).tracks Using preferredSource: .catalog was a workaround used to address the issue in the aforementioned post above. All iOS 16 capable functions are decorated with: @available(iOS 16, *) or in an if block: if #available(iOS 16, *) {... What's happening is that the following is showing up in the console: 2022-11-28 23:31:11.279648+0700 MyApp[38653:6736450] [core] Attempted to register account monitor for types client is not authorized to access: {(     "com.apple.account.iTunesStore" )} 2022-11-28 23:31:11.279718+0700 MyApp[38653:6736450] [Default] <ICUserIdentityStoreACAccountBackend: 0x282adb520> Failed to register for account monitoring. err=Error Domain=com.apple.accounts Code=7 "(null)" 2022-11-28 23:31:11.279758+0700 MyApp[38653:6736450] [Default] ICUserIdentity - Unable to retrieve DSID for userIdentity=<ICUserIdentity 0x2806eb120: [Active Account: <unresolved>]> - error=Error Domain=com.apple.accounts Code=7 "(null)" These errors are not caught by a do/catch block, but I assume they are related to the issue, and I believe they have to do with MyApp trying to access things that Music Kit thinks it's not supposed to. For example, if MyApp attempts to work with a playlist that it did not create, errors would be expected, thrown errors. The thing is that I know I'm working with resources that are created by MyApp. In fact, in trying to test this, I just tried to create a playlist with the below, and the same behavior is occurring: @available(iOS 16, *) func createPlaylist2(name: String, description: String) async -> MusicKit.Playlist? {     do {         Logger.log(.info, "Creating Playlist: \(name)")         Logger.log(.shrug, "Does this work?")         let newPlaylist = try await MusicLibrary.shared.createPlaylist(name: name, description: description) // <= Things stop here!         Logger.log(.success, "New playlist created: \(newPlaylist)") // <= this isn't logged.         return newPlaylist // <= nothing is returned     } catch {         Logger.log(.error, "Could not create new playlist: \(error)") // <= no error logged.     }     return nil } The result is: 2022-11-29 00:15:01.875064+0700 MyApp[38794:6760471] [core] Attempted to register account monitor for types client is not authorized to access: {(     "com.apple.account.iTunesStore" )} 2022-11-29 00:15:01.875372+0700 MyApp[38794:6760471] [Default] <ICUserIdentityStoreACAccountBackend: 0x283005720> Failed to register for account monitoring. err=Error Domain=com.apple.accounts Code=7 "(null)" 2022-11-29 00:15:01.876677+0700 MyApp[38794:6760323] [EntityQuery] Finished executing query in 0.000999928s 2022-11-29 00:15:01.889055+0700 MyApp[38794:6760323] [EntityQuery] Finished fetching results in 0.0120001s 2022-11-29 00:15:01.891235+0700 MyApp[38794:6760329] [core] Attempted to register account monitor for types client is not authorized to access: {(     "com.apple.account.iTunesStore" )} 2022-11-29 00:15:01.891684+0700 MyApp[38794:6760329] [Default] <ICUserIdentityStoreACAccountBackend: 0x283005720> Failed to register for account monitoring. err=Error Domain=com.apple.accounts Code=7 "(null)" 📘 Creating Playlist: TEST PLAYLIST 🤷🏻‍♀️ Does this work? 2022-11-29 00:15:06.697374+0700 MyApp[38794:6760329] [] nw_path_necp_check_for_updates Failed to copy updated result (22) What's really nasty is that errors are not thrown, so they can't be caught and handled in a catch block. I know that iOS 16.1 got released around the end of October, but I really don't know what's going on here. The behavior is showing up in both prod and when testing locally. Any help would be most appreciated. @JoeKhun: Did I miss the memo?
10
1
5.5k
May ’24
MusicLibraryRequest to get all tracks from a playlist (iOS 16 beta)
Hi there, tl;dr: What's the best way to get all tracks (with catalog IDs) from a playlist that has more than 100 tracks, using MusicLibraryRequest. I'm doing something dumb, not understanding something, and possibly both. I've got an existing, kinda okay function that uses the MusicDataRequest and the Apple Music API to fetch all tracks from a playlist, with pagination like this: func getTracksFromAppleMusicLibraryPlaylist(playlist: AppleMusicPlaylist) async throws -> [MusicKit.Track]? {     var tracksToReturn: [MusicKit.Track] = []     var libraryTracks: [AppleMusicLibraryTrack] = []     @Sendable     func fetchTracks(playlist: AppleMusicPlaylist, offset: Int) async -> AppleMusicPlaylistFetchResponse? {         do {             let playlistId = playlist.id             var playlistRequestURLComponents = URLComponents()             playlistRequestURLComponents.scheme = "https"             playlistRequestURLComponents.host = "api.music.apple.com"             playlistRequestURLComponents.path = "/v1/me/library/playlists/\(playlistId)/tracks"             playlistRequestURLComponents.queryItems = [                 URLQueryItem(name: "include", value: "catalog"),                 URLQueryItem(name: "limit", value: "100"),                 URLQueryItem(name: "offset", value: String(offset)),             ]             if let playlistRequestURL = playlistRequestURLComponents.url {                 let playlistRequest = MusicDataRequest(urlRequest: URLRequest(url: playlistRequestURL))                 let playlistResponse = try await playlistRequest.response()                 let decoder = JSONDecoder()                 // print("Get Tracks Dump")                 // print(String(data: playlistResponse.data, encoding: .utf8)!)                            let response = try decoder.decode(AppleMusicPlaylistFetchResponse.self, from: playlistResponse.data)                 return response             } else {                 print("Bad URL!")             }         } catch {             print(error)         }         return nil     }     Logger.log(.info, "Fetching inital tracks from \(playlist.attributes.name)")     if let response = await fetchTracks(playlist: playlist, offset: 0) {         if let items = response.data {             libraryTracks = items         }         if let totalItemCount = response.meta?.total {             Logger.log(.info, "There are \(totalItemCount) track(s) in \(playlist.attributes.name)")             if totalItemCount > 100 {                 let remainingItems = (totalItemCount - 100)                 let calls = remainingItems <= 100 ? 1 : (totalItemCount - 100) / 100                 Logger.log(.info, "Total items: \(totalItemCount)")                 Logger.log(.info, "Remaining items: \(remainingItems)")                 Logger.log(.info, "Calls: \(calls)")                 await withTaskGroup(of: [AppleMusicLibraryTrack]?.self) { group in                     for offset in stride(from: 100, to: calls * 100, by: 100) {                         Logger.log(.info, "Fetching additional tracks from \(playlist.attributes.name) with offset of \(offset)")                         group.addTask {                             if let response = await fetchTracks(playlist: playlist, offset: offset) {                                 if let items = response.data {                                     return items                                 }                             }                             return nil                         }                     }                     for await (fetchedTracks) in group {                         if let tracks = fetchedTracks {                             libraryTracks.append(contentsOf: tracks)                         }                     }                 }             }         }     } // props to @JoeKun for this bit of magic     Logger.log(.info, "Matching library playlist tracks with catalog tracks...")     for (i, track) in libraryTracks.enumerated() {         if let correspondingCatalogTrack = track.relationships?.catalog?.first {             tracksToReturn.append(correspondingCatalogTrack)             print("\(i) => \(track.id) corresponds to catalog track with ID: \(correspondingCatalogTrack.id).")         } else {             Logger.log(.warning, "\(i) => \(track.id) doesn't have any corresponding catalog track.")         }     }     if tracksToReturn.count == 0 {         return nil     }     return tracksToReturn } While not the most elegant, it gets the job done, and it's kinda quick due to the use of withTaskGroup .esp with playlists containing more than 100 songs/tracks. Regardless, I'm kinda stuck, trying to do something similar with the new MusicLibraryReqeust in iOS 16. The only way I can think of to get tracks from a playlist, using MusicLibraryRequest, having read the new docs, is like this: @available(iOS 16.0, *) func getAllTracksFromHugePlaylist(id: MusicItemID) async throws -> [MusicKit.Track]? {     do {         var request = MusicLibraryRequest<MusicKit.Playlist>()         request.filter(matching: \.id, equalTo: id)         let response = try await request.response()         if response.items.count > 0 {             if let tracks = try await response.items[0].with(.tracks, preferredSource: .catalog).tracks {                 Logger.log(.info, "Playlist track count: \(tracks.count)")                 return tracks.compactMap{$0}             }         }     } catch {         Logger.log(.error, "Could not: \(error)")     }     return nil } The problem with this is that .with seems to be capped at 100 songs/tracks, and I can't see any way to change that. Knowing that, I can't seem to tell MusicLibraryRequest that I want the tracks of the playlist with the initial request, where before I could use request.properties = .tracks, which I could then paginate if available. Any help setting me on the right course would be greatly appreciated.
8
0
3.6k
Sep ’23
Usage of Dolby Atmos Badge
Since iOS 16, MusicKit exposes audio variants, such as Dolby Atmos. In the session video at 9:09, the code is configured to show the Atmos badge. Considering it is a third party logo and not provided as an SF symbol, are developers allowed to use it in their own apps when using MusicKit? Is permission from Dolby required?
1
1
1.5k
Jan ’23
Artwork of Track is not displayed correctly when used within Button in iOS 16.1 & 16.2
Hi Apple Engineers, I am reporting a issue that has surfaced in iOS 16.1 and iOS 16.2 beta, where when you have a long list of Tracks that are in the label of a Buttons, they are are displayed correctly. They appear intermittently, after you scroll they disappear (placeholder is shown) and it required a state change to display the final artwork correctly Here is code: let tracks = [Track] //With Issue List(tracks) { track in Button { //ACTION BLOCL } label: { if let artwork = track.artwork { ArtworkImage(artwork: artwork, width: 50, height: 50) } else { PlaceholderView() } } } //Without Issue List(tracks) { track in if let artwork = track.artwork { ArtworkImage(artwork: artwork, width: 50, height: 50) } else { PlaceholderView() } } Same code works correctly on iOS 15 and also when ArtworkImage is not used inside a button. Here is the related ticket: FB11840336
0
0
998
Dec ’22
How to develop for impending beta features while supporting older iOS versions
Hi there, This is more of a "how-to," "am I doing this right?" question, as I've never done this before, and I couldn't find any definitive write up for how to do this, so I'm asking here. With the release of iOS16 beta, I've been implementing some new MusicKit features in an iOS app I'm working on that was initially built for iOS 15. Setup: dev machine: masOS Monterey 12.4 test device 1: iOS 15.4 test device 2: iOS 16 beta 2 Xcode versions: 14.0 beta 2 (14A5229c) 13.4.1 (13F100) The original app was written, using Xcode 13 and has an iOS Development Target of 15.0 Steps: create new branch off main called beta16 open Xcode beta2 and switch to new branch set the iOS Development Target for the project to 15.0 make code changes in the new branch, using ifavailable to handle both iOS 16 and fallback version code. Results: When I test the new code using Xcode 14 beta 2 on an iOS 16 device, things work as expected. When I test the new code using Xcode 14 beta on an iOS 15 device, the app builds and then crashes immediately upon open with: dyld[38909]: Symbol not found: _$s8MusicKit0A20CatalogSearchRequestV17includeTopResultsSbvs   Referenced from: /private/var/containers/Bundle/Application/4BB8F74F-FDA6-4DF1-8B04-010EA87BA80C/MyApp.app/MyApp   Expected in: /System/Library/Frameworks/MusicKit.framework/MusicKit Symbol not found: _$s8MusicKit0A20CatalogSearchRequestV17includeTopResultsSbvs   Referenced from: /private/var/containers/Bundle/Application/4BB8F74F-FDA6-4DF1-8B04-010EA87BA80C/MyApp.app/MyApp   Expected in: /System/Library/Frameworks/MusicKit.framework/MusicKit When coding, I followed all of Xcodes prompting that says when a potentially unsupported new feature is being used. When I look to where .includeTopResults is being used, I can see that it was not wrapped with ifavailable: var request = MusicCatalogSearchRequest(term: searchString, types: [Song.self])     request.includeTopResults = true let response = try await request.response() If I comment out the line with .includeTopResults, the app runs on the iOS 15 device w/o issue. If I wrap it in ifavailable, like this, it crashes as before: var request = MusicCatalogSearchRequest(term: searchString, types: [Song.self]) if #available(iOS 16, *) {     request.includeTopResults = true } let response = try await request.response() If I check the docs here, I think I'm using .includeTopResults correctly. Question: My main question here is if I'm correctly developing the app toward the goal of supporting new iOS 16 features, while also supporting devices using iOS 15, via fallback. Ultimately, I plan to merge the beta16 branch into a branch from which I'll deploy the app. The app crashing when using .includeTopResults has me hesitant because I don't know if that's a bug or if I'm the bug. Usually, it's the latter. It seems like the steps outlined above are the correct way to develop toward supporting upcoming features and "legacy iOS versions", but I'm not sure. Any clarity on this would be most appreciated. Thanks!
3
0
1.6k
Nov ’22
MusicLibrary.shared.createPlaylist adds wrong track to playlist
I’ve been trying to debug an issue where tracks in playlists created using the new MusicLibrary.shared.createPlaylist function seem to be missing their artwork. For example: I’ve found that when I look at the track which MusicKit added in the above example, it’s not the one which I specified - it’s a different version (catalogId) of the same song. Interestingly it also seems to be marked as a library track (the track is definitely not in my library). Here is the code: let request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: MusicItemID("1440800296")) let response = try await request.response() let initialCatalogId = response.items.first?.id.rawValue ?? "?" let playlist = try await MusicLibrary.shared.createPlaylist(name: "My Playlist", description: nil, authorDisplayName: "My App", items: response.items) while true {     guard let entry = try? await playlist.with([.entries], preferredSource: .library).entries?.first else {         continue     }     // This is just for debug purposes to work out what track actually got added:     let data = try JSONEncoder().encode(entry.playParameters)     guard let root = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {         continue     }     let newCatalogId = root["catalogId"] as? String ?? "?"     let isLibrary = root["isLibrary"] as? Bool ?? false     print(initialCatalogId, newCatalogId, isLibrary)     // Prints: 1440800296 309186793 true     break } If I’m doing something wrong either when saving the playlist or retrieving the catalogId of the track which was saved please let me know: I confess I’m finding it quite hard to navigate MusicKit Swift at the moment as examples & documentation seem quite thin on the ground. However if the above behaviour is the best I can expect then it’s a serious issue for me - the catalogId of the track I want to add, above, was carefully selected so if MusicKit is going to randomly swap this out then I can’t use it. I’ve created FB11589088 for this issue. @JoeKun, @david-apple: I’ve raised a number of issues with MusicKit on here recently and have received no responses at all. Appreciate you guys are really busy but I think a little help here would go a long way to making MusicKit the success it deserves to be. Please let me know if I should be doing this differently. Thanks!
0
0
2.0k
Sep ’22
Exposing library-related metadata on library Songs
I have been excitedly testing out MusicKit's new features to interact with a user's music library over the last couple of days. Albums and Songs are a much nicer paradigm than MPMediaItemCollection and MPMediaItem, but I'm afraid that there are some library-focused metadata options present on MPMediatem that are seemingly not currently exposed on Songs fetched from a MusicLibraryRequest that force me back into the world of MPMediaItems. A big component of my app is being able to sort and filter your library across properties like play count and date added. Am I right that these properties are not available when interacting with library Songs in MusicKit? dateAdded - The date the item was added to the library lastPlayTime - The date on which the item was last played playCount - The number of times the item has been played For as long as those properties are not available, I will need to dip back into MPMediaItems in order to continue offering important pieces of functionality in my app.  Those three are my highest priority for this area of MusicKit at this time, and are my biggest blockers on jumping into the new world as opposed to staying the old. I've submitted the above as FB10185523 While I'm at it, I also have a wishlist of some additional properties, only one of which is currently exposed on MPMediaItem. The rest of which correspond to metadata a user can set in Music.app on the desktop but have not historically been publicly exposed on MPMediaItem: isCloudItem - This is currently exposed on MPMediaItem and tells you whether an item is locally downloaded on the device. I understand that the includeOnlyDownloadedContent option can be set on a MusicLibraryRequest to return locally downloaded devices, but it would also be nice to have on a per-song basis to be able to display an indicator to the user.  startTime and stopTime - These properties correspond to the custom “start” and “stop” times a user can define for a song on the Options tab of Music.app on the desktop. These have not historically been exposed publicly. It would be nice to have access to these in order to be able to display an accurate play time if a user has customized it. sortAlbumTitle, sortAlbumArtist, sortArtist - These properties correspond to the “sort as” options a user can define for a song on the Sorting tab of Music.app on the desktop. These have not historically been exposed publicly. It would be nice to have access to these in order to display library items to a user in the order they’ve defined and expect, rather than ignore their preference.  I've submitted the above as FB10185575 Thanks!
6
0
2.4k
Sep ’22
Musickit Top Results only return 3 items
I've been trying to implement MusicCatalogSearchRequest with includeTopResults. It works. But topResults only returns 3 items. I tried with many different search terms and even compared the same search term with Apple Music app where it gives many more top results. Below is my code var searchRequest = MusicCatalogSearchRequest(term: searchTerm,                             types: [                              Song.self,                              MusicVideo.self,                              Album.self,                              Playlist.self,                              Curator.self,                              RadioShow.self,                              Artist.self                             ])      searchRequest.includeTopResults = true      searchResponse = try await searchRequest.response()      print(searchResponse?.topResults) What should I do to get more relevant top results? Could this be a bug in iOS 16 Beta or is topResults restricted to 3 items in Musickit?
2
1
2.1k
Sep ’22
PlayParameters in iOS 16 breaks backwards compatibility with iOS 15
Hi Apple Engineers, I noticed in iOS 16, PlayParameters which part of every Track that has been added/downloaded to the library, now includes two new properties isLibrary & musicKit_persistentID . Unfortunately, these create backward compatibility error when trying to decode the Track object on iOS 15. I make collaborative playlist app, which stores the Track added by User A which now includes the unique identifier for the locally downloaded file, when attempted to be played by User B won't play any music due to mismatching identifiers (even when everyone is on iOS 16). This essentially breaks the entire functionality of my app due to these new properties. Steps: Store a Track (which has been added to library) as a JSON object from iOS 16 Decode the JSON on iOS 15 Expected Results: New PlayParameters variables should be optional, to support backwards compatibility with iOS 15 and to support collaborative uses cases. Actual Results: New PlayParameters automatically include two new values that cause decoding errors. I request that at least for an option to ignore the new properties in the PlayParameters in iOS 16. Here is my feedback: FB11362589
1
0
1.8k
Aug ’22
Can we remove tracks from a user's Library Playlist yet?
Hi there, I just watched the WWDC22 vids on MusicKit enhancements. In the Explore More Content with MusicKit video, there is a brief mention of "edit playlists" at 24:33. I asked about this a couple years ago here in this forum. The ever-helpful @JoeKun suggested that I submit a Feedback Assistant ticket on the need/justification for this feature, which I did some time ago: FB9798928. Sadly, there's been no traction on that. I'm super hopeful that the ability to remove tracks from a library playlist is now, or at least will soon be, possible; however, I can't find anything in the docs yet to support this notion. Am I being overly optimistic, or are we finally going to get this much requested feature? Thanks.
7
0
3.7k
Aug ’22
Get Stations of a Curator/RadioShow using MusicKit
Hi, Is there a way to get all the episodes (Stations) of a Curator or RadioShow, particularly using the MusicKit enhancements announced at WW22? For example, if I have find an AppleMusic Curator such as "After School Radio" (Curator(id: "1496846020", name: "After School Radio", kind: .editorial)) or a RadioShow (RadioShow(id: "1496846020", name: "After School Radio", hostName: "Mark Hoppus")), is there a way to get all the Stations associated with this curator? There don't seem to be any relationships between Curator, RadioShow, and Station in MusicKit for Swift, and only a relationship from Station to RadioShow in the Apple Music API, but no reverse relationship from RadioShow to Stations. Thanks
1
0
1.2k
Jul ’22
Fetch all properties of the music item in MusicCatalogResourceRequest
I am working on an app that requires to fetch all properties of a music item. Instead of adding all the items, is there a way to just say .all that adds all the properties applicable for the music item? Example: var request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: song.id) request.properties = [.all] // Instead of request.properties = [.audioVariants, .albums, .artists, .composers, .genres, .musicVideos, .artistURL, .station] Thank you!
1
0
1.3k
Jul ’22
Is there any way to exclude curated and smart playlists?
The new MusicLibraryRequest<Playlist>() is great to get all the user's playlists but I have not found a way to exclude a specific type of playlist. For example; I wish to only get playlists that the user owns and can add content to (so this would exclude Smart and Curated playlists) I did find the Playlist.Kind enum, but I can't seem to apply this to the request, and it doesn't seem to have an option for smart playlists. So in short: Is there any way I can exclude Smart- and Curated Playlists from a MusicLibraryRequest
0
0
1.1k
Jul ’22
MusicCatalogChartsRequest and kinds
Hi there! I was experimenting with the MusicCatalogChartsRequest. I have one doubt, and I do not know if it is the intended behavior. If I have the kinds as [.cityTop] and the types as [Song.self], why does it print the city playlists and the top played playlists? Ideally, it should print an empty array, right? As I have only Song as a type? var request = MusicCatalogChartsRequest(kinds: [.cityTop], types: [Song.self]) request.limit = 1 let response = try await request.response() print(response.playlistCharts) The output is: [MusicCatalogChart<Playlist>(  id: "city-top:cityCharts",  kind: .cityTop,  title: "City Charts",  items: [   Playlist(    id: "pl.db537759ae3341eaa600bc5482209f7c",    name: "Top 25: Mumbai",    curatorName: "Apple Music",    isChart: true,    kind: "editorial",    lastModifiedDate: "2022-06-21",    url: "https://music.apple.com/in/playlist/top-25-mumbai/pl.db537759ae3341eaa600bc5482209f7c"   )  ],  hasNextBatch: true ), MusicCatalogChart<Playlist>(  id: "most-played:playlists",  kind: .mostPlayed,  title: "Top Playlists",  items: [   Playlist(    id: "pl.f4d106fed2bd41149aaacabb233eb5eb",    name: "Today’s Hits",    curatorName: "Apple Music Hits",    isChart: false,    kind: "editorial",    lastModifiedDate: "2022-06-21",    shortDescription: "Drake gets loose with “Sticky”.",    standardDescription: "Among the many surprises of Drake’s <i>Honestly, Nevermind</i>—not the least of which is the album’s very existence—is that it isn’t really a hip-hop album at all; he’s mainly singing. But “Sticky” is one of the few tracks where the project’s commitment to exploring dance music—courtesy here of production work from Australian artist RY X and Gordo (aka DJ Carnage)—meets actual rapping. Add Today’s Hits to your library to stay up on the biggest songs in pop, hip-hop, R&B and more.",    url: "https://music.apple.com/in/playlist/todays-hits/pl.f4d106fed2bd41149aaacabb233eb5eb"   )  ],  hasNextBatch: true )] Thank you!
3
0
1.9k
Jun ’22
Editing a Library Playlist (MusicKit: iOS 16 beta)
I've just begun to dip my toes into the iOS16 waters. One of the first things that I've attempted is to edit a library playlist using: try await MusicLibrary.shared.edit(targetPlaylist, items: tracksToAdd) Where targetPlaylist is of type MusicItemCollection<MusicKit.Playlist>.Element and tracksToAdd is of type [Track] The targetPlaylist was created, using new iOS16 way, here: let newPlaylist = try await MusicLibrary.shared.createPlaylist(name: name, description: description) tracksToAdd is derived by performing a MusicLibraryRequest on a specific playlist ID, and then doing something like this: if let tracksToAdd = try await playlist.with(.tracks).tracks {    // add tracks to target playlist } My problem is that when I perform attempt the edit, I am faced with a rather sad looking crash. libdispatch.dylib`dispatch_group_leave.cold.1:     0x10b43d62c <+0>:  mov    x8, #0x0     0x10b43d630 <+4>:  stp    x20, x21, [sp, #-0x10]!     0x10b43d634 <+8>:  adrp   x20, 6     0x10b43d638 <+12>: add    x20, x20, #0xfbf          ; "BUG IN CLIENT OF LIBDISPATCH: Unbalanced call to dispatch_group_leave()"     0x10b43d63c <+16>: adrp   x21, 40     0x10b43d640 <+20>: add    x21, x21, #0x260          ; gCRAnnotations     0x10b43d644 <+24>: str    x20, [x21, #0x8]     0x10b43d648 <+28>: str    x8, [x21, #0x38]     0x10b43d64c <+32>: ldp    x20, x21, [sp], #0x10 ->  0x10b43d650 <+36>: brk    #0x1 I assume that I must be doing something wrong, but I frankly have no idea how to troubleshoot this. Any help would be most appreciated. Thanks. @david-apple?
Replies
11
Boosts
0
Views
3.5k
Activity
May ’25
Thoughts on MusicLibraryRequest as a replacement for MPMediaQuery
I'm very excited about the new MusicLibrary API, but after a couple of days of playing around with it, I have to say that I find the implementation of filtering MusicLibraryRequests a little confusing. MPMediaQuery has a fairly extensive list of predicates that can be applied, including string and persistentID comparisons for artist, album artist genre, and more. It also lets you filter on an item’s title. MusicLibraryRequests let you filter on the item’s ID, or on its MusicKit Artist and Genre relationships. To me, this seems like it adds an extra step.  With an MPMediaQuery, if I wanted to fetch every album by a given artist, I’d apply an MPMediaPropertyPredicate looking at MPMediaItemPropertyAlbumArtist and compare the string. It was also easy to change the MPMediaPredicateComparison to .contains to match more widely. If I wanted to surface albums by “Aesop Rock” or “Aesop Rock & Blockhead,” I could use that. In the MusicLibraryRequest implementation, it looks like I need to perform a MusicLibraryRequest<Artist> first in order to get the Artist objects. There’s no filter for the name property, so if I don’t have their IDs, I’ve got to use filter(text:). From there, I can take the results of that request and apply them to my MusicLibraryRequest<Album> using the filter(matching:memberOf) function.  I could use filter(text:) on the MusicLibraryRequest<Album>, but that filters across multiple properties (title and artistName?) and is less precise than defining the actual property I want to match against. I think my ideal version of the MusicLibraryRequest API would offer something like filter(matching:equalTo:) or filter(matching:contains:) that worked off of KeyPaths rather than relationships. That seems more intuitive to me. I’m not saying we need every property from every filterable MPMediaItemProperty key, but I’d love to be able to do it on title, artistName, and other common metadata. That might look something like: filter(matching: \.title, contains: “Abbey Road”) filter(matching: \.artistName, equalTo: “Between The Buried And Me”) I noticed that filter(text:) is case insensitive, which is awesome, and something I’ve wanted for a long time in MPMediaPropertyPredicate. As a bonus, it would be great if a KeyPath based filter API supported a case sensitivity flag. This is less of a problem when dealing with Apple Music catalog content, but users’ libraries are a harsh environment, and you might have an artist “Between The Buried And Me” and one called “Between the Buried and Me.” It would be great to get albums from both with something like: filter(matching: \.artistName, equalTo: “Between The Buried And Me”, caseSensitive: false)  I've submitted the above as FB10185685. I also submitted another feedback this morning regarding filter(text:) and repeating text as FB10184823. My last wishlist item for this API (for the time being!) is exposing the MPMediaItemPropertyAlbumPersistentID as an available filter attribute. I know, I know… hear me out. If you take a look at the other thread I made today, you’ll see that due to missing metadata in MusicKit, I still have some use cases where I need to be able to reference an MPMediaItem and might need to fetch its containing MPMediaItemCollection to get at other tracks on the album. It would be nice to seamlessly be able to fetch the MPMediaItemCollection or the library Album using a shared identifier, especially when it comes to being able to play the album in MusicKit’s player rather than Media Player’s.  I've submitted that list bit as FB10185789 Thanks for bearing with my walls of text today. Keep up the great work!
Replies
11
Boosts
1
Views
4.1k
Activity
Nov ’24
iOS 16 Music Kit MusicLibrary methods have stopped working
Hi there, I have a related forum thread here and a Feedback Assistant ticket open, but this issue seems different. Sometime within the last 2-3 weeks, code related to MusicLibrary has stopped working. None of my code has changed. For example, the below two snippets used to work fine:  for track in newTracks {    try await MusicLibrary.shared.add(track, to: targetPlaylist)  } try await MusicLibrary.shared.edit(targetPlaylist, items: items) newTracks and items are both fetched using: try await targetPlaylist.with(.tracks, preferredSource: .catalog).tracks Using preferredSource: .catalog was a workaround used to address the issue in the aforementioned post above. All iOS 16 capable functions are decorated with: @available(iOS 16, *) or in an if block: if #available(iOS 16, *) {... What's happening is that the following is showing up in the console: 2022-11-28 23:31:11.279648+0700 MyApp[38653:6736450] [core] Attempted to register account monitor for types client is not authorized to access: {(     "com.apple.account.iTunesStore" )} 2022-11-28 23:31:11.279718+0700 MyApp[38653:6736450] [Default] <ICUserIdentityStoreACAccountBackend: 0x282adb520> Failed to register for account monitoring. err=Error Domain=com.apple.accounts Code=7 "(null)" 2022-11-28 23:31:11.279758+0700 MyApp[38653:6736450] [Default] ICUserIdentity - Unable to retrieve DSID for userIdentity=<ICUserIdentity 0x2806eb120: [Active Account: <unresolved>]> - error=Error Domain=com.apple.accounts Code=7 "(null)" These errors are not caught by a do/catch block, but I assume they are related to the issue, and I believe they have to do with MyApp trying to access things that Music Kit thinks it's not supposed to. For example, if MyApp attempts to work with a playlist that it did not create, errors would be expected, thrown errors. The thing is that I know I'm working with resources that are created by MyApp. In fact, in trying to test this, I just tried to create a playlist with the below, and the same behavior is occurring: @available(iOS 16, *) func createPlaylist2(name: String, description: String) async -> MusicKit.Playlist? {     do {         Logger.log(.info, "Creating Playlist: \(name)")         Logger.log(.shrug, "Does this work?")         let newPlaylist = try await MusicLibrary.shared.createPlaylist(name: name, description: description) // <= Things stop here!         Logger.log(.success, "New playlist created: \(newPlaylist)") // <= this isn't logged.         return newPlaylist // <= nothing is returned     } catch {         Logger.log(.error, "Could not create new playlist: \(error)") // <= no error logged.     }     return nil } The result is: 2022-11-29 00:15:01.875064+0700 MyApp[38794:6760471] [core] Attempted to register account monitor for types client is not authorized to access: {(     "com.apple.account.iTunesStore" )} 2022-11-29 00:15:01.875372+0700 MyApp[38794:6760471] [Default] <ICUserIdentityStoreACAccountBackend: 0x283005720> Failed to register for account monitoring. err=Error Domain=com.apple.accounts Code=7 "(null)" 2022-11-29 00:15:01.876677+0700 MyApp[38794:6760323] [EntityQuery] Finished executing query in 0.000999928s 2022-11-29 00:15:01.889055+0700 MyApp[38794:6760323] [EntityQuery] Finished fetching results in 0.0120001s 2022-11-29 00:15:01.891235+0700 MyApp[38794:6760329] [core] Attempted to register account monitor for types client is not authorized to access: {(     "com.apple.account.iTunesStore" )} 2022-11-29 00:15:01.891684+0700 MyApp[38794:6760329] [Default] <ICUserIdentityStoreACAccountBackend: 0x283005720> Failed to register for account monitoring. err=Error Domain=com.apple.accounts Code=7 "(null)" 📘 Creating Playlist: TEST PLAYLIST 🤷🏻‍♀️ Does this work? 2022-11-29 00:15:06.697374+0700 MyApp[38794:6760329] [] nw_path_necp_check_for_updates Failed to copy updated result (22) What's really nasty is that errors are not thrown, so they can't be caught and handled in a catch block. I know that iOS 16.1 got released around the end of October, but I really don't know what's going on here. The behavior is showing up in both prod and when testing locally. Any help would be most appreciated. @JoeKhun: Did I miss the memo?
Replies
10
Boosts
1
Views
5.5k
Activity
May ’24
MusicLibraryRequest to get all tracks from a playlist (iOS 16 beta)
Hi there, tl;dr: What's the best way to get all tracks (with catalog IDs) from a playlist that has more than 100 tracks, using MusicLibraryRequest. I'm doing something dumb, not understanding something, and possibly both. I've got an existing, kinda okay function that uses the MusicDataRequest and the Apple Music API to fetch all tracks from a playlist, with pagination like this: func getTracksFromAppleMusicLibraryPlaylist(playlist: AppleMusicPlaylist) async throws -> [MusicKit.Track]? {     var tracksToReturn: [MusicKit.Track] = []     var libraryTracks: [AppleMusicLibraryTrack] = []     @Sendable     func fetchTracks(playlist: AppleMusicPlaylist, offset: Int) async -> AppleMusicPlaylistFetchResponse? {         do {             let playlistId = playlist.id             var playlistRequestURLComponents = URLComponents()             playlistRequestURLComponents.scheme = "https"             playlistRequestURLComponents.host = "api.music.apple.com"             playlistRequestURLComponents.path = "/v1/me/library/playlists/\(playlistId)/tracks"             playlistRequestURLComponents.queryItems = [                 URLQueryItem(name: "include", value: "catalog"),                 URLQueryItem(name: "limit", value: "100"),                 URLQueryItem(name: "offset", value: String(offset)),             ]             if let playlistRequestURL = playlistRequestURLComponents.url {                 let playlistRequest = MusicDataRequest(urlRequest: URLRequest(url: playlistRequestURL))                 let playlistResponse = try await playlistRequest.response()                 let decoder = JSONDecoder()                 // print("Get Tracks Dump")                 // print(String(data: playlistResponse.data, encoding: .utf8)!)                            let response = try decoder.decode(AppleMusicPlaylistFetchResponse.self, from: playlistResponse.data)                 return response             } else {                 print("Bad URL!")             }         } catch {             print(error)         }         return nil     }     Logger.log(.info, "Fetching inital tracks from \(playlist.attributes.name)")     if let response = await fetchTracks(playlist: playlist, offset: 0) {         if let items = response.data {             libraryTracks = items         }         if let totalItemCount = response.meta?.total {             Logger.log(.info, "There are \(totalItemCount) track(s) in \(playlist.attributes.name)")             if totalItemCount > 100 {                 let remainingItems = (totalItemCount - 100)                 let calls = remainingItems <= 100 ? 1 : (totalItemCount - 100) / 100                 Logger.log(.info, "Total items: \(totalItemCount)")                 Logger.log(.info, "Remaining items: \(remainingItems)")                 Logger.log(.info, "Calls: \(calls)")                 await withTaskGroup(of: [AppleMusicLibraryTrack]?.self) { group in                     for offset in stride(from: 100, to: calls * 100, by: 100) {                         Logger.log(.info, "Fetching additional tracks from \(playlist.attributes.name) with offset of \(offset)")                         group.addTask {                             if let response = await fetchTracks(playlist: playlist, offset: offset) {                                 if let items = response.data {                                     return items                                 }                             }                             return nil                         }                     }                     for await (fetchedTracks) in group {                         if let tracks = fetchedTracks {                             libraryTracks.append(contentsOf: tracks)                         }                     }                 }             }         }     } // props to @JoeKun for this bit of magic     Logger.log(.info, "Matching library playlist tracks with catalog tracks...")     for (i, track) in libraryTracks.enumerated() {         if let correspondingCatalogTrack = track.relationships?.catalog?.first {             tracksToReturn.append(correspondingCatalogTrack)             print("\(i) => \(track.id) corresponds to catalog track with ID: \(correspondingCatalogTrack.id).")         } else {             Logger.log(.warning, "\(i) => \(track.id) doesn't have any corresponding catalog track.")         }     }     if tracksToReturn.count == 0 {         return nil     }     return tracksToReturn } While not the most elegant, it gets the job done, and it's kinda quick due to the use of withTaskGroup .esp with playlists containing more than 100 songs/tracks. Regardless, I'm kinda stuck, trying to do something similar with the new MusicLibraryReqeust in iOS 16. The only way I can think of to get tracks from a playlist, using MusicLibraryRequest, having read the new docs, is like this: @available(iOS 16.0, *) func getAllTracksFromHugePlaylist(id: MusicItemID) async throws -> [MusicKit.Track]? {     do {         var request = MusicLibraryRequest<MusicKit.Playlist>()         request.filter(matching: \.id, equalTo: id)         let response = try await request.response()         if response.items.count > 0 {             if let tracks = try await response.items[0].with(.tracks, preferredSource: .catalog).tracks {                 Logger.log(.info, "Playlist track count: \(tracks.count)")                 return tracks.compactMap{$0}             }         }     } catch {         Logger.log(.error, "Could not: \(error)")     }     return nil } The problem with this is that .with seems to be capped at 100 songs/tracks, and I can't see any way to change that. Knowing that, I can't seem to tell MusicLibraryRequest that I want the tracks of the playlist with the initial request, where before I could use request.properties = .tracks, which I could then paginate if available. Any help setting me on the right course would be greatly appreciated.
Replies
8
Boosts
0
Views
3.6k
Activity
Sep ’23
Usage of Dolby Atmos Badge
Since iOS 16, MusicKit exposes audio variants, such as Dolby Atmos. In the session video at 9:09, the code is configured to show the Atmos badge. Considering it is a third party logo and not provided as an SF symbol, are developers allowed to use it in their own apps when using MusicKit? Is permission from Dolby required?
Replies
1
Boosts
1
Views
1.5k
Activity
Jan ’23
Artwork of Track is not displayed correctly when used within Button in iOS 16.1 & 16.2
Hi Apple Engineers, I am reporting a issue that has surfaced in iOS 16.1 and iOS 16.2 beta, where when you have a long list of Tracks that are in the label of a Buttons, they are are displayed correctly. They appear intermittently, after you scroll they disappear (placeholder is shown) and it required a state change to display the final artwork correctly Here is code: let tracks = [Track] //With Issue List(tracks) { track in Button { //ACTION BLOCL } label: { if let artwork = track.artwork { ArtworkImage(artwork: artwork, width: 50, height: 50) } else { PlaceholderView() } } } //Without Issue List(tracks) { track in if let artwork = track.artwork { ArtworkImage(artwork: artwork, width: 50, height: 50) } else { PlaceholderView() } } Same code works correctly on iOS 15 and also when ArtworkImage is not used inside a button. Here is the related ticket: FB11840336
Replies
0
Boosts
0
Views
998
Activity
Dec ’22
How to develop for impending beta features while supporting older iOS versions
Hi there, This is more of a "how-to," "am I doing this right?" question, as I've never done this before, and I couldn't find any definitive write up for how to do this, so I'm asking here. With the release of iOS16 beta, I've been implementing some new MusicKit features in an iOS app I'm working on that was initially built for iOS 15. Setup: dev machine: masOS Monterey 12.4 test device 1: iOS 15.4 test device 2: iOS 16 beta 2 Xcode versions: 14.0 beta 2 (14A5229c) 13.4.1 (13F100) The original app was written, using Xcode 13 and has an iOS Development Target of 15.0 Steps: create new branch off main called beta16 open Xcode beta2 and switch to new branch set the iOS Development Target for the project to 15.0 make code changes in the new branch, using ifavailable to handle both iOS 16 and fallback version code. Results: When I test the new code using Xcode 14 beta 2 on an iOS 16 device, things work as expected. When I test the new code using Xcode 14 beta on an iOS 15 device, the app builds and then crashes immediately upon open with: dyld[38909]: Symbol not found: _$s8MusicKit0A20CatalogSearchRequestV17includeTopResultsSbvs   Referenced from: /private/var/containers/Bundle/Application/4BB8F74F-FDA6-4DF1-8B04-010EA87BA80C/MyApp.app/MyApp   Expected in: /System/Library/Frameworks/MusicKit.framework/MusicKit Symbol not found: _$s8MusicKit0A20CatalogSearchRequestV17includeTopResultsSbvs   Referenced from: /private/var/containers/Bundle/Application/4BB8F74F-FDA6-4DF1-8B04-010EA87BA80C/MyApp.app/MyApp   Expected in: /System/Library/Frameworks/MusicKit.framework/MusicKit When coding, I followed all of Xcodes prompting that says when a potentially unsupported new feature is being used. When I look to where .includeTopResults is being used, I can see that it was not wrapped with ifavailable: var request = MusicCatalogSearchRequest(term: searchString, types: [Song.self])     request.includeTopResults = true let response = try await request.response() If I comment out the line with .includeTopResults, the app runs on the iOS 15 device w/o issue. If I wrap it in ifavailable, like this, it crashes as before: var request = MusicCatalogSearchRequest(term: searchString, types: [Song.self]) if #available(iOS 16, *) {     request.includeTopResults = true } let response = try await request.response() If I check the docs here, I think I'm using .includeTopResults correctly. Question: My main question here is if I'm correctly developing the app toward the goal of supporting new iOS 16 features, while also supporting devices using iOS 15, via fallback. Ultimately, I plan to merge the beta16 branch into a branch from which I'll deploy the app. The app crashing when using .includeTopResults has me hesitant because I don't know if that's a bug or if I'm the bug. Usually, it's the latter. It seems like the steps outlined above are the correct way to develop toward supporting upcoming features and "legacy iOS versions", but I'm not sure. Any clarity on this would be most appreciated. Thanks!
Replies
3
Boosts
0
Views
1.6k
Activity
Nov ’22
MusicKit issues
Hi @JoeKun, Just informing the following 2 issues in MusicKit https://feedbackassistant.apple.com/feedback/11615702 https://feedbackassistant.apple.com/feedback/11615752 Thank you!
Replies
0
Boosts
0
Views
1.3k
Activity
Sep ’22
MusicLibrary.shared.createPlaylist adds wrong track to playlist
I’ve been trying to debug an issue where tracks in playlists created using the new MusicLibrary.shared.createPlaylist function seem to be missing their artwork. For example: I’ve found that when I look at the track which MusicKit added in the above example, it’s not the one which I specified - it’s a different version (catalogId) of the same song. Interestingly it also seems to be marked as a library track (the track is definitely not in my library). Here is the code: let request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: MusicItemID("1440800296")) let response = try await request.response() let initialCatalogId = response.items.first?.id.rawValue ?? "?" let playlist = try await MusicLibrary.shared.createPlaylist(name: "My Playlist", description: nil, authorDisplayName: "My App", items: response.items) while true {     guard let entry = try? await playlist.with([.entries], preferredSource: .library).entries?.first else {         continue     }     // This is just for debug purposes to work out what track actually got added:     let data = try JSONEncoder().encode(entry.playParameters)     guard let root = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {         continue     }     let newCatalogId = root["catalogId"] as? String ?? "?"     let isLibrary = root["isLibrary"] as? Bool ?? false     print(initialCatalogId, newCatalogId, isLibrary)     // Prints: 1440800296 309186793 true     break } If I’m doing something wrong either when saving the playlist or retrieving the catalogId of the track which was saved please let me know: I confess I’m finding it quite hard to navigate MusicKit Swift at the moment as examples & documentation seem quite thin on the ground. However if the above behaviour is the best I can expect then it’s a serious issue for me - the catalogId of the track I want to add, above, was carefully selected so if MusicKit is going to randomly swap this out then I can’t use it. I’ve created FB11589088 for this issue. @JoeKun, @david-apple: I’ve raised a number of issues with MusicKit on here recently and have received no responses at all. Appreciate you guys are really busy but I think a little help here would go a long way to making MusicKit the success it deserves to be. Please let me know if I should be doing this differently. Thanks!
Replies
0
Boosts
0
Views
2.0k
Activity
Sep ’22
Exposing library-related metadata on library Songs
I have been excitedly testing out MusicKit's new features to interact with a user's music library over the last couple of days. Albums and Songs are a much nicer paradigm than MPMediaItemCollection and MPMediaItem, but I'm afraid that there are some library-focused metadata options present on MPMediatem that are seemingly not currently exposed on Songs fetched from a MusicLibraryRequest that force me back into the world of MPMediaItems. A big component of my app is being able to sort and filter your library across properties like play count and date added. Am I right that these properties are not available when interacting with library Songs in MusicKit? dateAdded - The date the item was added to the library lastPlayTime - The date on which the item was last played playCount - The number of times the item has been played For as long as those properties are not available, I will need to dip back into MPMediaItems in order to continue offering important pieces of functionality in my app.  Those three are my highest priority for this area of MusicKit at this time, and are my biggest blockers on jumping into the new world as opposed to staying the old. I've submitted the above as FB10185523 While I'm at it, I also have a wishlist of some additional properties, only one of which is currently exposed on MPMediaItem. The rest of which correspond to metadata a user can set in Music.app on the desktop but have not historically been publicly exposed on MPMediaItem: isCloudItem - This is currently exposed on MPMediaItem and tells you whether an item is locally downloaded on the device. I understand that the includeOnlyDownloadedContent option can be set on a MusicLibraryRequest to return locally downloaded devices, but it would also be nice to have on a per-song basis to be able to display an indicator to the user.  startTime and stopTime - These properties correspond to the custom “start” and “stop” times a user can define for a song on the Options tab of Music.app on the desktop. These have not historically been exposed publicly. It would be nice to have access to these in order to be able to display an accurate play time if a user has customized it. sortAlbumTitle, sortAlbumArtist, sortArtist - These properties correspond to the “sort as” options a user can define for a song on the Sorting tab of Music.app on the desktop. These have not historically been exposed publicly. It would be nice to have access to these in order to display library items to a user in the order they’ve defined and expect, rather than ignore their preference.  I've submitted the above as FB10185575 Thanks!
Replies
6
Boosts
0
Views
2.4k
Activity
Sep ’22
Musickit Top Results only return 3 items
I've been trying to implement MusicCatalogSearchRequest with includeTopResults. It works. But topResults only returns 3 items. I tried with many different search terms and even compared the same search term with Apple Music app where it gives many more top results. Below is my code var searchRequest = MusicCatalogSearchRequest(term: searchTerm,                             types: [                              Song.self,                              MusicVideo.self,                              Album.self,                              Playlist.self,                              Curator.self,                              RadioShow.self,                              Artist.self                             ])      searchRequest.includeTopResults = true      searchResponse = try await searchRequest.response()      print(searchResponse?.topResults) What should I do to get more relevant top results? Could this be a bug in iOS 16 Beta or is topResults restricted to 3 items in Musickit?
Replies
2
Boosts
1
Views
2.1k
Activity
Sep ’22
PlayParameters in iOS 16 breaks backwards compatibility with iOS 15
Hi Apple Engineers, I noticed in iOS 16, PlayParameters which part of every Track that has been added/downloaded to the library, now includes two new properties isLibrary & musicKit_persistentID . Unfortunately, these create backward compatibility error when trying to decode the Track object on iOS 15. I make collaborative playlist app, which stores the Track added by User A which now includes the unique identifier for the locally downloaded file, when attempted to be played by User B won't play any music due to mismatching identifiers (even when everyone is on iOS 16). This essentially breaks the entire functionality of my app due to these new properties. Steps: Store a Track (which has been added to library) as a JSON object from iOS 16 Decode the JSON on iOS 15 Expected Results: New PlayParameters variables should be optional, to support backwards compatibility with iOS 15 and to support collaborative uses cases. Actual Results: New PlayParameters automatically include two new values that cause decoding errors. I request that at least for an option to ignore the new properties in the PlayParameters in iOS 16. Here is my feedback: FB11362589
Replies
1
Boosts
0
Views
1.8k
Activity
Aug ’22
Can we remove tracks from a user's Library Playlist yet?
Hi there, I just watched the WWDC22 vids on MusicKit enhancements. In the Explore More Content with MusicKit video, there is a brief mention of "edit playlists" at 24:33. I asked about this a couple years ago here in this forum. The ever-helpful @JoeKun suggested that I submit a Feedback Assistant ticket on the need/justification for this feature, which I did some time ago: FB9798928. Sadly, there's been no traction on that. I'm super hopeful that the ability to remove tracks from a library playlist is now, or at least will soon be, possible; however, I can't find anything in the docs yet to support this notion. Am I being overly optimistic, or are we finally going to get this much requested feature? Thanks.
Replies
7
Boosts
0
Views
3.7k
Activity
Aug ’22
Get Stations of a Curator/RadioShow using MusicKit
Hi, Is there a way to get all the episodes (Stations) of a Curator or RadioShow, particularly using the MusicKit enhancements announced at WW22? For example, if I have find an AppleMusic Curator such as "After School Radio" (Curator(id: "1496846020", name: "After School Radio", kind: .editorial)) or a RadioShow (RadioShow(id: "1496846020", name: "After School Radio", hostName: "Mark Hoppus")), is there a way to get all the Stations associated with this curator? There don't seem to be any relationships between Curator, RadioShow, and Station in MusicKit for Swift, and only a relationship from Station to RadioShow in the Apple Music API, but no reverse relationship from RadioShow to Stations. Thanks
Replies
1
Boosts
0
Views
1.2k
Activity
Jul ’22
Fetch all properties of the music item in MusicCatalogResourceRequest
I am working on an app that requires to fetch all properties of a music item. Instead of adding all the items, is there a way to just say .all that adds all the properties applicable for the music item? Example: var request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: song.id) request.properties = [.all] // Instead of request.properties = [.audioVariants, .albums, .artists, .composers, .genres, .musicVideos, .artistURL, .station] Thank you!
Replies
1
Boosts
0
Views
1.3k
Activity
Jul ’22
Is there any way to exclude curated and smart playlists?
The new MusicLibraryRequest<Playlist>() is great to get all the user's playlists but I have not found a way to exclude a specific type of playlist. For example; I wish to only get playlists that the user owns and can add content to (so this would exclude Smart and Curated playlists) I did find the Playlist.Kind enum, but I can't seem to apply this to the request, and it doesn't seem to have an option for smart playlists. So in short: Is there any way I can exclude Smart- and Curated Playlists from a MusicLibraryRequest
Replies
0
Boosts
0
Views
1.1k
Activity
Jul ’22
MusicPropertySource.library not on macOS?
Hello, MusicPropertySource.catalog says it is available in the docs on macOS, but .library is not. Is that an oversight? How does this integrate with current uses of iTunesMusicLibrary framework and ITLibrary? Thank you, -- Greg Bolsinga
Replies
3
Boosts
0
Views
1.4k
Activity
Jul ’22
Music Marathon App Source Code
Anyway we can get the source code for this app to see the MusicKit functionality used in it?
Replies
0
Boosts
0
Views
769
Activity
Jul ’22
MusicCatalogChartsRequest and kinds
Hi there! I was experimenting with the MusicCatalogChartsRequest. I have one doubt, and I do not know if it is the intended behavior. If I have the kinds as [.cityTop] and the types as [Song.self], why does it print the city playlists and the top played playlists? Ideally, it should print an empty array, right? As I have only Song as a type? var request = MusicCatalogChartsRequest(kinds: [.cityTop], types: [Song.self]) request.limit = 1 let response = try await request.response() print(response.playlistCharts) The output is: [MusicCatalogChart<Playlist>(  id: "city-top:cityCharts",  kind: .cityTop,  title: "City Charts",  items: [   Playlist(    id: "pl.db537759ae3341eaa600bc5482209f7c",    name: "Top 25: Mumbai",    curatorName: "Apple Music",    isChart: true,    kind: "editorial",    lastModifiedDate: "2022-06-21",    url: "https://music.apple.com/in/playlist/top-25-mumbai/pl.db537759ae3341eaa600bc5482209f7c"   )  ],  hasNextBatch: true ), MusicCatalogChart<Playlist>(  id: "most-played:playlists",  kind: .mostPlayed,  title: "Top Playlists",  items: [   Playlist(    id: "pl.f4d106fed2bd41149aaacabb233eb5eb",    name: "Today’s Hits",    curatorName: "Apple Music Hits",    isChart: false,    kind: "editorial",    lastModifiedDate: "2022-06-21",    shortDescription: "Drake gets loose with “Sticky”.",    standardDescription: "Among the many surprises of Drake’s <i>Honestly, Nevermind</i>—not the least of which is the album’s very existence—is that it isn’t really a hip-hop album at all; he’s mainly singing. But “Sticky” is one of the few tracks where the project’s commitment to exploring dance music—courtesy here of production work from Australian artist RY X and Gordo (aka DJ Carnage)—meets actual rapping. Add Today’s Hits to your library to stay up on the biggest songs in pop, hip-hop, R&B and more.",    url: "https://music.apple.com/in/playlist/todays-hits/pl.f4d106fed2bd41149aaacabb233eb5eb"   )  ],  hasNextBatch: true )] Thank you!
Replies
3
Boosts
0
Views
1.9k
Activity
Jun ’22
MusicKit development and Apple Music subscription
Do we have any development allowance/tools for playback from Apple Music without a necessity to pay for the subscription?
Replies
1
Boosts
0
Views
1.8k
Activity
Jun ’22