As the summer continues, I have been diving deeper and deeper into MusicKit, largely with great results. A few issues have arisen that I've outlined here, feedbacks already filed and numbers included here. All of this happens on the lasted developer beta and latest Xcode beta. Thanks!
FB10967343 - Setting the queue with library and non-library items at the same time doesn't work correctly
In my app, I am working on a feature that lets a user shuffle songs from a collection of albums that may or may not be in their library. However, I’ve discovered an issue where the queue does not seem to work correctly when mixing these types. I’ve attempted to load ApplicationMusicPlayer by creating a Queue and to load applicationQueuePlayer using a MPMusicPlayerPlayParametersQueueDescriptor, but the same issue occurs each time. The queue is able to play songs from the same source, but if it’s been playing a library song and tries to move to a non-library song, the queue stops.
The first thing I do is pick random songs from each album, using a MusicLibraryRequest or a MusicCatalogResourceRequest as appropriate, then taking a randomElement() from the ensuing MusicItemCollection for the album. I append each track to an array, which I then cast to MusicItemCollection so I’ve now got a MusicItemCollection consisting of the tracks I want.
If I’m in MusicKit land, I simply set the queue as follows:
player.queue = ApplicationMusicPlayer.Queue(for: tracks)
It takes a bit more doing in MediaPlayer, but in theory this should also work, right?
do {
let paramObjects = tracks.compactMap {
$0.playParameters
}
let params = try paramObjects.map({try JSONEncoder().encode($0)})
let dicts = try params.compactMap {
try JSONSerialization.jsonObject(with: $0, options: []) as? [String:Any]
}
let finalParams = dicts.compactMap {
MPMusicPlayerPlayParameters(dictionary: $0)
}
let descriptor = MPMusicPlayerPlayParametersQueueDescriptor(playParametersQueue: finalParams)
mediaPlayer.setQueue(with: descriptor)
} catch {
print(error)
}
In either case, the following issue occurs: say that I end up with a queue made up of one library song, then one non-library song. The player will play just the first song, then it acts as if the queue has ended. Say that it has two non-library songs, then one library song. Just the two non-library songs play. Indeed, printing queue.entries shows just the number of items that were from the same source type.
FB10967076 - Publishing changes from background thread error when inserting queue items
When using the .insert method on ApplicationMusicPlayer.Queue on the last iOS 16 and Xcode betas, it returns a “Publishing changes from background thread” error even though the function I’m doing in is marked as a @MainActor and the stacktace indicates it was on the main thread.
FB10967277 - song.with([.albums], preferredSource: .library) generates thousands of lines of EntityQueries in the console
I’ve noticed that when using the preferredSource: .library when requesting additional properties on a library item creates ~6,000 of “EntityQuery” entries in the console, all in the span of a second. This doesn’t seem to be leading to any major performance issues, but it sure seems like something isn't right.
let request = MusicLibraryRequest<Song>.init()
do {
let response = try await request.response()
guard let song = response.items.first else {
return
}
let songWithAlbums = try await song.with([.albums], preferredSource: .library)
} catch {
print(error)
}
generates the following output (except... 6,000 of them)
2022-07-31 13:02:07.729003-0400 MusicKitFutzing[9405:2192606] [EntityQuery] Finished fetching results in 0s
2022-07-31 13:02:07.729047-0400 MusicKitFutzing[9405:2192605] [EntityQuery] Finished executing query in 0.00100017s
2022-07-31 13:02:07.729202-0400 MusicKitFutzing[9405:2192611] [EntityQuery] Finished executing query in 0s
2022-07-31 13:02:07.729240-0400 MusicKitFutzing[9405:2192605] [EntityQuery] Finished fetching results in 0s
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I've submitted this as FB13628591 but I figure a forum post never hurts ;)
I’ve discovered an issue with keyboard presentation when using .searchable in SwiftUI in iOS 17.4 beta 3, shown in this video (https://imgur.com/1hmM5u0) running this sample code.
struct Animal: Identifiable {
var id: String {
return name
}
let name: String
}
struct ContentView: View {
let animals: [Animal] = [.init(name: "Dog"), .init(name: "Cat"), .init(name: "Turtle")]
@State var presentedAnimal: Animal? = nil
@State var searchText: String = ""
var body: some View {
NavigationStack {
List {
ForEach(animals) { item in
Button(item.name) {
self.presentedAnimal = item
}
}
}
.sheet(item: $presentedAnimal) { item in
Text(item.name)
}
.searchable(text: $searchText)
}
}
}
As of iOS 17.4, the behavior of the above code is that if you tap one of the list buttons to present a sheet while the keyboard is still presented, the keyboard will dismiss then represent once the sheet is presented. In the video, I tap the “Cat” button while the search keyboard is still presented. This is confusing, because the keyboard actually belongs to the presenting .searchable view, not the presented sheet. In all previous versions, the keyboard would dismiss and stay that way. That is the correct behavior.
I’ve noticed this erroneously presented keyboard does not respond to calls to resignFirstResponder:
let resign = #selector(UIResponder.resignFirstResponder)
UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
ApplicationMusicPlayer is available on the Mac! 🎉🎉🎉 Enormous thanks to @JoeKun and the team. I've already gotten my app up and running through Catalyst, and I've successfully played music! I also got some timeouts, but that was happening on my phone a lot that day too, so maybe my local CDN was just having a bad day.
I wanted to ask this question in a lab this week, but the timing didn't work out: Do you expect the experience to be the same using ApplicationMusicPlayer on a Catalyst vs a macOS target? I'm hoping to reuse much of my iPad app and go the Catalyst route, but I wanted to double check that the new support wasn't just for macOS.
I'm running into a couple of breaking issues with AppIntents for which I just submitted feedbacks. I figured I'd post them on the forum too for good measure.
FB12701143 - @IntentParameterDependency causing crashes in all AppIntents with Entity Queries on iOS 16
I have a pre-existing AppIntent with its own EntityQuery that works just fine on iOS 16. I've built a totally separate intent only available in iOS 17 that makes use of a new entity and a new query using @IntentParameterDependency, but now the original intent crashes after building and running the app on an iOS 16 device with the latest Xcode beta. The line it crashes on is the initializer of the iOS 17-only EntityQuery, even though 1) it shouldn't be able to see it and 2) that EntityQuery isn't involved in the crashing intent.
This breaks significant functionality in my AppIntents, and I'm hoping this is a bug rather than a framework limitation. The feedback includes a sample project, crash log, and sysdiagnose.
FB12701491 - iOS 17-only CustomIntentMigratedAppIntent stops SiriKit intent equivalent from working on iOS 16
I have a SiriKit intent in my app that I was not able to convert to an AppIntent until iOS 17 because it makes use of an @IntentParameterDependency. However, even though the new AppIntent version is marked as only available in iOS 17 and above, once I conform it to CustomIntentMigratedAppIntent and provide the old intent’s class name, it stops working on iOS 16 devices with an error that the shortcut is unavailable on that device.
The CustomIntentMigratedAppIntent should respect the iOS 17 availability annotation and leave the SiriKit intent alone since its migrated replacement isn’t available on iOS 16.
The feedback includes a sample project illustrating the issue.
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!
I've filed this as FB21446798 but figured I'd post here too.
In the first build of macOS 26.3, playback via ApplicationMusicPlayer is completely broken. When starting playback of anything at all, the console shows the following error:
applicationController: xpc service connection interrupted
Failed to obtain remoteObject: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service created from an endpoint was invalidated from this process." UserInfo={NSDebugDescription=The connection to service created from an endpoint was invalidated from this process.}
Failed to prepareToPlay with error: Error Domain=MPMusicPlayerControllerErrorDomain Code=10 "(null)" UserInfo={NSUnderlyingError=0xc92910ff0 {Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service created from an endpoint was invalidated from this process." UserInfo={NSDebugDescription=The connection to service created from an endpoint was invalidated from this process.}}}
In addition, several crash logs for RemotePlayerService are generated, showing my app as the parent process.
This issue is 100% repeatable. No matter how I load the queue, whether it’s catalog or library content, any variation I can think of all fails like this.
I really hope this can be fixed before 26.3 comes out, otherwise my app will be totally unusable. 😅