I'm experiencing this issue on Sonoma beta 7 and Xcode 15 beta 8:
FB13094612 -- MusicLibraryRequests are way slower on macOS than iOS
The following code has massive speed differences on macOS than iOS, with macOS taking 3-7 seconds for each request and iOS taking 0.1 seconds or less.
let start = Date()
var artistAlbumRequest = MusicLibraryRequest<Album>.init()
artistAlbumRequest.filter(matching: \.artistName, equalTo: "Ratboys")
print("Searching artist Ratboys")
let artistAlbums = try! await artistAlbumRequest.response()
let name = artistAlbums.items.first!.title
let id = artistAlbums.items.first!.id
let end = Date()
print("It took \(end.timeIntervalSince1970 - start.timeIntervalSince1970) to complete the artist request. \(artistAlbums.items.count) returned")
print(artistAlbums.items)
let start2 = Date()
print("Searching by title: \(name)")
var albumNameRequest = MusicLibraryRequest<Album>.init()
albumNameRequest.filter(matching: \.title, equalTo: name)
let nameAlbum = try! await albumNameRequest.response()
let end2 = Date()
print("It took \(end2.timeIntervalSince1970 - start2.timeIntervalSince1970) to complete this request")
print(nameAlbum.items)
print("Searching by ID")
let start3 = Date()
var albumIDRequest = MusicLibraryRequest<Album>.init()
albumIDRequest.filter(matching: \.id, equalTo: id)
let idalbum = try! await albumIDRequest.response()
let end3 = Date()
print("It took \(end3.timeIntervalSince1970 - start3.timeIntervalSince1970) to complete this request")
print(idalbum.items)
Awaiting the three requests takes 0.085, 0.019, and 0.102 seconds respectively on iOS. However, they take 7.10, 0.1, and 3.3 seconds on macOS. The second one only returns so quickly because of a bug where matching on title returns no results (see FB13094588)
This makes it very difficult to provide a good experience when starting music playback, because it takes several seconds between the user selecting an album and it starting.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
My app uses the Media Player framework to play music and has the Audio background mode. When a user pauses the music and backgrounds the app (or if the app is already in the background when they pause), the system does not kill the app as long as it has the active AVAudioSession. This means that as long as a user doesn't start playback from another audio app, mine is available in Control Center for a quick resume.
I recently implemented the UIBackgroundTask API (specifically UIApplication.shared.beginBackgroundTask/endBackgroundTask) to run a 5-20 second server communication task when the app is paused while in the background. Now, iOS kills my app at the conclusion of that task, despite it still having the active audio session. It is no longer in Control Center, and needs to be launched fresh.
Is there anything I can do to prevent the system from killing my app at the conclusion of the background task? I'm starting to get complaints from users that they're having to relaunch the app after every time they pause for more than a few seconds.
Thanks!
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
I'd really like to be able to show List row swipe actions with text and an image, like in Mail.app:
In my app, using the .swipeActions modifier on a list row, I have been unable to display both text and an image. It just displays the image.
Here is my code:
.swipeActions(edge: .leading, allowsFullSwipe: true) {
Button {
Task {
await albumProvider.treatGridAsQueue(album: album)
}
} label: {
Label("Play", systemImage: "play.fill")
}
Button {
AlbumContextHandler.shared.handleTag(albumObject: album, session: nil, presentedSheet: $albumProvider.sheetToPresent)
} label: {
Label("Tag", systemImage: "tag.fill") {
}
}
I have also tried various initializers on Button, including init(_:systemImage:action:), but none of them have worked.
The strange thing is that very occasionally just the top row of a List will display the title and label the first time the swipe actions are displayed. Showing them a second time will just show the icons, as in my screenshot.
I've also played around with .buttonStyles but haven't found those to make a difference.
Any ideas?
As the title asks, is it possible to support SharePlay using the applicationQueuePlayer in the Media Player framework? It seems not to be possible since there is no AVPlaybackCoordinator available like there is for AVPlayer.
Is that correct? It would be nice to be able to support this in my application if possible. Thanks!
Hello,
I just submitted FB9939377 about this issue, but figured I'd still post on the forums as well.
I have been working on bringing song and album rating functionality to my app, using MusicDataRequest from MusicKit for Swift.
When I like or dislike a song, or delete the existing rating, those changes are reflected immediately in the Music app across all of my devices.
However, when I try to rate or delete an existing rating for an album, the API request is successful, but the changes are not reflected in Music.app on any of my devices. Querying the /v1/me/ratings/albums/[id] endpoint correctly shows the change that was made, but it does not seem to sync the change to any devices, which continue to show whatever the rating state was before the change I made in my app.
I ran 3 tests on albums with the following IDs:
1485040020 - Disliked -> Loved
Disliked in Music.app
/v1/me/ratings/albums/1485040020 shows the value of the rating attribute as “-1”
Changed to “Loved” in my app
/v1/me/ratings/albums/1485040020 confirms the value of the rating attribute has changed from “-1” to “1”
Music.app on the iPod I used to make the change in my app, as well as another iPhone 11 signed into that Apple ID show the original “Disliked” rating for the album.
1485042017 - Loved -> No Rating
Liked in Music.app
/v1/me/ratings/albums/1485042017 shows the value of the rating attribute as “1”
Deleted the rating from my app
/v1/me/ratings/albums/1485042017 now 404s, indicating there is no rating for this album.
Music.app on the iPod I used to make the change in my app, as well as another iPhone 11 signed into that Apple ID show the original “Loved” rating for the album.
1485037658 - No Rating -> Loved
Confirmed /v1/me/ratings/albums/1485037658 returns a 404, as there is no rating for this album
“Loved” the album in my app
/v1/me/ratings/albums/1485037658 now returns a value of “1”
Music.app on the iPod I used to make the change in my app, as well as another iPhone 11 signed into that Apple ID show me the option to either “Love” or “Suggest Less Like This,” indicating there is no existing rating for the album.
There are sysdiagnoses attached to the feedback. Please let me know here or in the Feedback if there is any more information I can provide. Thanks!
(P.S., I'm requiring iOS 15 for the next version of my app, so I've finally been digging into MusicKit. What a well-designed and useful API! It's been so quick and easy to replace most of my existing URLSession data task code with MusicKit, and MusicDataRequest is such a thoughtful inclusion to be able to interface with the elements of the API not natively in MusicKit, with the added benefit of not needing to deal with tokens anymore. Thanks to the team for all of their continued hard work on it.)
Hey there Apple Music team! I'm excited to dig into the sessions coming up this week, and what I've seen so far from the developer documentation diffs looks great: audio quality, artist images, and a way to interface with a user's music library in MusicKit. Love it!
The thing at the very top of my WWDC wishlist this year was macOS/Mac Catalyst support for the ApplicationMusicPlayer class. I just got finished installing Ventura and Xcode 14, and sadly it looks like the support story is the same as on Big Sur. No API availability on macOS, and an available Mac Catalyst API that ultimately results in the same error from a feedback I submitted on Big Sur: FB9851840
The connection to service named com.apple.Music.MPMusicPlayerApplicationControllerInternal was invalidated: failed at lookup with error 3 - No such process.
Is that the end of the story on Ventura, or is there a chance support might be added in a later beta? Is there any additional detail at all that can be shared? I field several requests a week asking if/when my app is coming to the Mac, and I would really love to be able to make that happen. If there is anything at all I can do to test and help overcome the engineering challenges alluded to in the past, I am ready, willing, and able!
In any case, thanks for the great work, and I'm looking forward to spending time with the new stuff this summer.
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've been continuing to port my app to the Mac using Catalyst, and have discovered a couple of big issues with MusicLibraryRequest which make it not particularly usable. I've submitted FBs already, but figured a forum post wouldn't hurt :)
filter(matching:) functions unavailable on macOS/Catalyst - FB12301718
The first thing I noticed when building for a macOS or Catalyst target is that a couple of functions are simply not available:
I'm hoping this is a bug, as these methods are crucial to being able to interact with the library.
request.filter(matching: .id, equalTo:) always returns empty on Mac Catalyst - FB12301908
I have not been able to successfully retrieve an Album using a MusicLibraryRequest. The result is always empty. In the feedback, I attached a sample project that makes an unfiltered MusicLibraryRequest, then takes the first album and tries to make a new MusicLibraryRequest filtered to that album's ID. Fetching albums by ID using this method works just fine on iOS 17.
Thanks again for all your work on bringing these capabilities to the Mac!
As of the latest builds of both iOS 16.6 and iOS 17.0, playing albums from a users library using ApplicationMusicPlayer plays the songs on that album out of order. This is a dealbreaker for my app, and I’ve had to revert back to the Media Player framework for reliable behavior.
If I fetch an album from a MusicLibraryRequest and load it into the queue using the API introduced in 16.4, init(album:startingAt:)., it starts at track 1 but then plays the rest of the tracks in random order. This happens whether skipping tracks or letting them play through.
The shuffleMode of the player is .off. The issue does not occur with albums fetched from the Apple Music catalog and loaded using that same API, nor does it occur for MPMediaItemCollections loaded into an applicationQueuePlayer via a queue descriptor.
I've submitted this issue as FB12495051 and provided a sysdiagnose. Please let me know if I can provide any other information.
Hello,
I have an app that uses the MediaPlayer framework (applicationQueuePlayer specifically) and since the introduction of iOS 14.6 and Lossless audio, some users have been reporting that sometimes they cannot play a song past 15 seconds without playback either pausing or the app making a screeching noise. As far as I knew, this was supposed to be fixed in 14.7, but users running 14.7.1 are reporting it to me whether Lossless is on or off. The problem seems to be intermittent -- there for one user one day and gone the next, though sometimes it sticks around. I am pretty much never able to reproduce it for myself.
My code to load and play the player is pretty simple and has not changed since well before this issue started cropping up. I run setQueue and set the queue with either a descriptor of storeIDs for Apple Music Items or an MPMediaItemCollection for library items, then call Play.
Is there anything I can do about this issue? Are you guys still working on this server side? I need to continue supporting users on older OSes so I am not yet able to use the new MusicKit for Swift player. I don't know if it's fixed there or not.
Thanks! (Tagging in @JoeKun)
I've finally been spending some time getting to know the MusicKit API, which is really nicely designed and will let me get rid of a ton of less efficient code as I start implementing it.
I'm used to hitting the /artists/{id}/view/latest-release API endpoint via a URLSession data task to return an artist's most recent release, if available. I see that it seems to be possible to return that information in a MusicCatalogResourceRequest, but the following code returns nil for me:
var request = MusicCatalogResourceRequest<Artist>.init(matching: \.id, equalTo: "35719")
request.properties = [.latestRelease]
do {
let response = try await request.response()
print(response.items.first?.latestRelease)
} catch {
print(error.localizedDescription)
}
In the code above, I've hardcoded the ID for an artist, but I've tried this in a for loop and it returns nil for every artist in my library. (By contrast, when adding .fullAlbums or .albums to the properties array, it does fetch that relationship).
When I construct a MusicDataRequest against the endpoint I'm used to hitting, it correctly returns the latest release. This is the code I'm using for that:
let request = MusicDataRequest(urlRequest: URLRequest(url: URL(string: "https://api.music.apple.com/v1/catalog/us/artists/\(artist)/view/latest-release")!))
do {
let response = try await request.response()
let decoded = try JSONDecoder().decode(AlbumReponse.self, from: response.data)
print(decoded)
} catch {
print("error getting latest release: \(error.localizedDescription)")
}
Is this a bug, or am I doing something incorrect? I didn't file a Feedback yet in case this was an error on my end, but if it's a bug I'm happy to put one in. Thanks!
I’m unable to use the .searchScopes modifier to add a segmented Picker to my search bar as of developer beta 6. It will not display whether I’m using a NavigationStack, NavigationSplitView, or NavigationView. Has anyone had any luck using this modifier?
This simple code will demonstrate the problem.
struct ContentView: View {
@State var searchText: String = ""
@State var searchScope: String = "Scope 1"
let data = Array(0..<20)
var body: some View {
NavigationStack {
List {
ForEach(data, id:\.self) { item in
Text("\(item)")
}
}
.searchable(text: $searchText)
.searchScopes($searchScope, scopes: {
Text("Scope 1")
Text("Scope 2")
})
}
}
}
I've submitted this as FB11298015
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.
Now that's a mouthful! As of iOS 17 seed 5 and continuing into seed 6, an issue was introduced where default Navigation Bar and Tab Bar behavior breaks when a View is made up of a ScrollView with another ScrollView as the safeAreaInset.
This View renders correctly using the code below, with the Navigation Bar and Tab Bar taking on a material effect when there is content behind it (screenshot 1). However, uncommenting out the ScrollView(.horizontal) so it's active causes the Nav Bar and Tab Bars to be fully transparent (screenshot 2):
TabView {
NavigationStack {
ScrollView(.vertical) {
LazyVStack {
ForEach(0...100, id: \.self) { _ in
Color(uiColor: UIColor.red)
}
}
}
.safeAreaInset(edge: .top, content: {
//ScrollView(.horizontal) {
HStack {
Color.red
Color.pink
Color.yellow
}
// }
.frame(maxWidth: .infinity)
.frame(height: 44)
})
.navigationTitle("Tab 1")
.navigationBarTitleDisplayMode(.inline)
}.tabItem { Label("One", systemImage: "bolt") }
I have tried various combinations of the new scrollLayoutTarget() and related modifiers, but I haven’t been able to find a way to have the nav and tab bars maintain their functionality if the Horizontal ScrollView is enabled. This behavior seems like it’s supposed to be supported, because it’s one of the examples featured n this year’s WWDC session “Beyond scroll views” (at 12:34).
It is also worth noting that this issue occurs whether the View is wrapped in a TabView or not. Without the TabView the problem presents the same in the Navigation Bar.
I have submitted this issue as FB12983586