Posts

Post marked as solved
1 Replies
2.7k Views
I've got an iOS app build on Swift MusicKit that retrieves Apple Music tracks and presents them to a user in a list. Tapping the track attempts to the track using this function: func handlePlayThisTrack(track: AppleMusicTrack) async {   do {     let tracksToQueue = sortedTracks.compactMap{$0.track}     if let track = tracksToQueue.first(where: {$0.id.rawValue == track.id}) {       print("track", track)       player.queue = ApplicationMusicPlayer.Queue(for: tracksToQueue, startingAt: track )       try await player.play()     } else {       Logger.log(.error, "Track does not exist in sorted tracks!")     }   } catch {     print(error)   } } Sometimes, however, the track does not play, and checking the logs, I see the below. This only happens on some tracks, but on those that don't work it happens consistently. When printing the details for a track that doesn't work, it looks like this: track Track.song(Song(id: "1443100129", title: "She's Not There", artistName: "The Zombies")) And this looks like all the other tracks that do play. So I don't know where to look for said missing play parameters mentioned in the error. I have an inkling that this may have to do with the track not being available due to the country/storefront, and that the error message is misleading. I'm using the following to get the tracks: MusicCatalogResourceRequest<Song>(matching: \.isrc, equalTo: isrc) // <= the ISRC of the track I want to fetch My assumption is that the MusicCatalogResourceRequest would only respond with tracks in my country/storefront, but I think this may not be the case. Is there any way to create a MusicCatalogRequest in such a way that I only get results that are playable in my country/storefront? Any advice would be most appreciated. Thanks! 2022-02-25 02:24:56.971343+0700 MusicApp[19452:1186508] [Playback] Failed to insert MusicPlayer.Queue.Entry(id: "F6A04D56-F5C5-4628-B136-5438E188FDA5", transientItem: Track.song(Song(id: "1443100129", title: "She's Not There", artistName: "The Zombies"))) into queue descriptor because it's missing play parameters. 2022-02-25 02:24:57.102432+0700 MusicApp[19452:1186733] [Entitlements] MSVEntitlementUtilities - Process MusicApp PID[19452] - Group: (null) - Entitlement: com.apple.accounts.appleaccount.fullaccess - Entitled: NO - Error: (null) 2022-02-25 02:24:57.104579+0700 MusicApp[19452:1186733] [core] Attempted to register account monitor for types client is not authorized to access: {(   "com.apple.account.iTunesStore" )}
Posted Last updated
.
Post not yet marked as solved
8 Replies
4.0k Views
I've got an iOS app that performs a series of operations when initialized and when a refresh is performed. In short, that app has: An obervable object (appManager) that houses variables that act as "state" for the app. Methods in the observable object that perform a variety of operations. Here's an example: Perform an API operation to get tokens to perform API actions on remote API service. Perform a fetch on an external database to retrieve some data. Decode that data an place the data into @Published variables, using (DispatchQueue.main.async). Perform another remote API fetch operation on a different endpoint. Put more variables in "state" So when the user opens the app and after init runs, they see what they expect to see. And when they tap a "refresh" button, another function (e.g. getUpdatedAppData()) is called with similar steps to the above, and the app updates the published variables and the view(s) update. The above steps are all performed using async functions, using Swift's new async/await functionality. Everything works swimmingly when the app is in the foreground. What I'd like to do is perform the updateAppData() function using Background tasks as described here.. I have successfully setup the app to register and schedule a task, using BGTaskScheduler, and I can successfully emulate the triggerring of that task by setting a breakpoint and running the following code in the console: e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.task"] If the background tasks is something simple, like printing to the console, it seems to work    private func configureBackgroundTasks() {     Logger.log(.info, "Registering background tasks...")     let bgTaskIdentifier = "com.example.task"     BGTaskScheduler.shared.register(forTaskWithIdentifier: bgTaskIdentifier, using: DispatchQueue.main) { (task) in       Logger.log(.info, "Performing background task \(bgTaskIdentifier)")       task.setTaskCompleted(success: true)       backgroundTaskManager.scheduleAppRefresh()     }   } However, if the function is something like the below, I can see the function being triggered, when simulating the running of the task, but the function hangs at the start of the first async operation (e.g. fetch tokens above). It seems to "try to complete" (and usuually fails) once the app is brought to the foreground, but that's obviously not what I want to happen. Surely, I'm misunderstanding something and/or doing something wrong, so I'm hoping to get some help here. Thanks in advance for any assistance toward achieving what I'm trying to do here.    private func configureBackgroundTasks() {     Logger.log(.info, "Registering background tasks...")     let bgTaskIdentifier = "com.example.task"     BGTaskScheduler.shared.register(forTaskWithIdentifier: bgTaskIdentifier, using: DispatchQueue.main) { (task) in       Logger.log(.info, "Performing background task \(bgTaskIdentifier)")       Task.init {         if let updatedData = await appManager.getUpdateAppData() {           DispatchQueue.main.async {             appManager.data = updatedData           }         }       }       task.setTaskCompleted(success: true)       backgroundTaskManager.scheduleAppRefresh()     }   }
Posted Last updated
.
Post marked as solved
1 Replies
935 Views
Hi there, I don't know if this is possible or not, so I'm asking here. I'd like to know if there is a way to get a list of an Apple Music public playlist via the Apple Music API. I don't think that Music Kit is an option here, as these users would not be Apple Music users, but, again, I'm not sure. The first stumbling block is that I can't seem to figure out how to get an Apple Music users ID or maybe their curator ID, if that's what is needed. Once I had that, I'm still not sure how to get that user's public playlists. Any help on this would be appreciated. Thanks!
Posted Last updated
.
Post not yet marked as solved
2 Replies
2.3k Views
Hi there, The following code snippet will open Apple Music on an iOS device to a catalog playlist, using the globalId. if let url = URL(string: "music://music.apple.com/us/playlist/\(playlist!.attributes.playParams.globalId!)") {             await UIApplication.shared.open(url) What I'd like to do is open Apple Music using the standard ID, as I don't want to force users to make a playlist public in order to link to it from my app. This requires the above code to use a library link instead of a catalog link. I've tried various permutations of the url, but I can't seem to find the secret sauce. Any tips, would be most appreciated. Thanks!
Posted Last updated
.
Post marked as solved
2 Replies
6.1k Views
Hi there, I've started building an iOS app a couple months ago from scratch, using iOS 15 as the deployment target. I've finally gotten to the point where I'd like to have this tested in Test Flight, which I've done w/o issue with other apps. The problem and my confusion is around the fact that the app will run when my device (iPhone XS) is connected via USB and I run the app in Xcode, but when I run it after deployed via AppConnect and installed via Test Flight or when exported to ipa and imported via Apple Configurator 2, the app opens for an instant and then closes. No crash reports seem to be generated. The best I can seem to do is open the console on the device via Xcode, filter by Errors and Faults, and try to see what might be the problem. There's a couple things I see that make me think it's related to my problem. These two seem likely culprits: error 00:17:20.220010-0700 ReportCrash vm_read failed for task 14895, kr=4, start_address = 0x280c24180, end_address = 0x0 Scene FBSceneManager/sceneID:xyz.uniqeappname-default update failed: <NSError: 0x28385b960; domain: FBSceneErrorDomain; code: 1 (operation-failed); reason: "Scene update failed."> {   NSUnderlyingError = <NSError: 0x28243ff60; domain: FBWorkspaceScene; code: 1; reason: "Client process exited.">; } The thing is that I've googled the heck out of these, and can't find anything that might help me resolve the issue. Here's what I've tried (not all at the same time): Edit the scheme so that Run uses the Release build config. I thought this might make the app crash when running locally, but no. This has no effect when running the app locally via Xcode. The app still works as expected. Edit the scheme so that Archive uses the Debug configuration. I thought this might give more crash detail, but no effect. App still stops and there's no crash report. Setting the Optimization Level under Apple Clang - Code Generation from Fastest to None. I've read that this helps some folks. Selectively disabling parts of the app. I've read that certain things break the app when not running locally (e.g. deprecated UISearchDisplayController, which I'm not using). None of the above seem to have any effect at all, let alone toward resolving the issue. I'm using the following 5 packages in the app: Lottie Amplify SpotifyAPI (SpotifyWebAPI by Peter Schorn) KeychainAccess Firebase These add some additional dependencies. One of these, swift-nio-zlib-support, which is a dependency of Starscream, apparently, shows a couple warnings, not errors at build time. Amplify is the ios-sdk and used for auth and storage. Firebase is used for notifications and is configured in the AppDelegate. Notifications work when running via Xcode. This app also uses MusicKit iOS. Again, everything works when running via Xcode, which is baffling. Frankly, I'm at a loss as to how to troubleshoot this any further, so I'm hoping to get some help here. Thanks in advance for any advice toward resolving this issue.
Posted Last updated
.
Post marked as solved
10 Replies
2.6k Views
I'm trying to do something that I though would be simple; however, I'm tripping up on something... In brief, I want to get all tracks from a playlist in an AppleMusic authorized user's playlist with the following function:     func getTracksFromAppleMusicPlaylist(playlistId: MusicItemID) async throws -> [Track]? {         print("Fetching AppleMusic Playlists...")         print("Playlist ID: \(playlistId)")         var playlistTracks: [Track]? = []         do {             var playlistRequest = MusicCatalogResourceRequest<Playlist>(matching: \.id, equalTo: playlistId )             playlistRequest.properties = [.tracks]             let playlistResponse = try await playlistRequest.response()             print("Playlist Response: \(playlistResponse)")             let playlistWithTracks = playlistResponse.items             let tracks = playlistWithTracks.flatMap { playlist -> MusicItemCollection<Track> in                 playlist.tracks ?? []             }             playlistTracks = tracks.count > 1 ? tracks : nil         } catch {             print("Error", error)             // handle error         }         return playlistTracks     } This function results in the following error: 2021-08-28 04:25:14.335455+0700 App[90763:6707890] [DataRequesting] Failed to perform MusicDataRequest.Context(   url: https://api.music.apple.com/v1/catalog/us/playlists/p.7XxxsXxXXxX?include=tracks&omit%5Bresource%5D=autos,   currentRetryCounts: [.other: 1] ) with MusicDataRequest.Error(   status: 404,   code: 40400,   title: "Resource Not Found",   detailText: "Resource with requested id was not found",   id: "QMK7GH4U7ITPMUTTBKIOMXXXX",   originalResponse: MusicDataResponse(     data: 159 bytes,     urlResponse: <NSHTTPURLResponse: 0x00000002820c0b60>   ) ). The playlistID being used is a value that has been picked from an existing playlist in the user's library, via a function that uses this code snippet: if let url = URL(string: "https://api.music.apple.com/v1/me/library/playlists?limit=100") {                let dataRequest = MusicDataRequest(urlRequest: URLRequest(url: url)) ... The only thing I can think of is that the playlistId from the above snippet is converted to a string when decoding into a struct, after which it is changed back to a MusicItemID with an init, like MusicItemID(playlistId). Any thoughts? Because I'm at a loss...
Posted Last updated
.
Post marked as solved
4 Replies
2.0k Views
I'm trying to perform a search for a song by album, artist, & title in the cases when I don't know the song's id (or if there is no isrc match) Reading the docs, it seems that I can only search for one term at a time, but against a list of MusicCatalogSearchable Types So my strategy would be to search for albums by name, then filter the results by artist and title. Two questions here, really: Is the above a good strategy, or is there a better way to do this? For the life of my I cannot figure out from the docs, what to put in place of [MusicCatalogSearchable.Album] in the below. var albumRequest = MusicCatalogSearchRequest(term: album.name, types: [MusicCatalogSearchable.Album]) Xcode doesn't like the above and gives the following error: Type 'MusicCatalogSearchable' has no member 'Album' Sadly, everything I've tried, results in an error. When I look in MusicKit, I see the below, which seems empty to me: @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) public protocol MusicCatalogSearchable : MusicItem { } What am I missing?
Posted Last updated
.
Post not yet marked as solved
1 Replies
741 Views
Trying to do what I can do pretty easily with a standard post request in a JS app, but now with Swift, and I'm struggling. I've tried with MusicRequestItem, but that fails, so I went with this and it responds with a 400. func addTracksToAppleMusicPlaylist(targetPlaylistId: String, tracksToSave: [Song]) async throws -> Void {     let tracks = tracksToSave.compactMap{         AppleMusicPlaylistPostRequest(id: $0.id, type: "songs")     }     do {         print("Saving tracks to Apple Music Playlist: \(targetPlaylistId)")         let tokens = try await self.getAppleMusicTokens()         if let url = URL(string: "https://api.music.apple.com/v1/me/library/playlists/\(targetPlaylistId)/tracks") {             var request = URLRequest(url: url)             request.httpMethod = "POST"             request.addValue("Bearer \(tokens.devToken)", forHTTPHeaderField: "Authorization")             request.addValue("\(tokens.userToken)", forHTTPHeaderField: "Music-User-Token")             request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")             let encoder = JSONEncoder()             let data = try encoder.encode(tracks)             request.httpBody = data             let session = URLSession(configuration: .default)             let task = session.dataTask(with: request) {(data, response, error) in                 if error == nil {                     if let httpResponse = response as? HTTPURLResponse {                         print("statusCode: \(httpResponse.statusCode)")                         print("response: \(httpResponse)")                     }                 } else {                     print("Error saving tracks to playlist \(String(describing: error))")                 }             }             task.resume()         } else {             print("Bad URL!")         }     } catch {         print("Error Saving Tracks to Playlist", error)         throw error     } }
Posted Last updated
.
Post not yet marked as solved
1 Replies
813 Views
I'm displaying Apple Music widgets in a React app with the following code block:&lt;iframe allow="autoplay *; encrypted-media *;" frameBorder="0" height="150" src="https://embed.music.apple.com/us/album/fatty-boom-boom/1240204028?i=1240204290&amp;app=music" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-top-navigation-by-user-activation" style={{ width: "100%", overflow: "hidden", backgroundColor: "transparent" }} title={item.id} width="100%" &gt;This is basically the embedCode received when using:const embedCode = await window.MusicKit.generateEmbedCode()And it's virtually the same as what you get from here: https://tools.applemusic.com/The code block above has been edited a bit to get the params working with React. The widget displays, and it's lovely; however for every widget displayed, there is an warning logged to the browser console: Metrics config: No config provided via delegate or fetched via init(), using default/cached config values.I'm wondering: How can I address these warnings, knowing that I'm working in a React app?Thanks in advance for any pointers.
Posted Last updated
.
Post not yet marked as solved
0 Replies
717 Views
I've got a web app that uses Music Kit to access authorized user's playlists and such. One feature of the app is to show a list of tracks, and I am using the Apple Music Embed code in an iframe to display their nicely designed track widget. The problem I have is that although the user has already authorized Apple Music within the app, using a configured MusicKit instance, the widget doesn't recognize this. This leaves the tracklist in a state where only previews can be played. And if the user wants to play the whole track they need to click Sign In, which opens a window to enter a user name and password, and then displays, "You're signed in". QUESTION: How can I get the widgets to recognize the fact that the user has already signed in?
Posted Last updated
.
Post not yet marked as solved
1 Replies
718 Views
I can add a playlist to a user's library with the following code:const postOptions = { method: "POST", url: `https://api.music.apple.com/v1/me/library/playlists`, headers: { Authorization: "Bearer " + token, "Music-User-Token": appleMusicUserToken, "Content-Type": "application/json" }, data: { attributes: { description: "Amazing playlist!", name: "amazingPlaylist" } } }; const { data } = await axios(postOptions);At this point, the playlist is not public, and there is no attributes =&gt; playParams =&gt; globalId key/value shown in the response or in future GETs of the playlist.If I turn on "Show on My Profile and in Search" in Music on an iPhone, this adds the globalId.The globalID is also added, when, in iTunes, the more icon is clicked and any of the Share Playlist options are clicked. Interestingly, you don't need to do anything else. For example, just selecting Share Playlist =&gt; Copy Link will add the globalID to the playlist.What I want to do: Ideally, I'd like to somehow have the globalID created when the playlist is created, or be able to perform some API call that will do this after creation, so as to prevent the user from having to do this on their device as an additional step. As the user is initiating this action, and they have already authorized the app, this shouldn't be a privacy or security issue.Thanks in advance for any ideas toward helping me get this working.
Posted Last updated
.
Post not yet marked as solved
1 Replies
732 Views
I have a web app that will pop up an Apple Music authorization window when the following code is run: const handleAppleMusicAuthorize = async () =&gt; { try { await window.MusicKit.getInstance().authorize(); // do stuff } catch (error) { console.warn("Error authorizing apple music", error); } };If the user successfully authorizes the app, all is good; however, if the user closes the pop-up or cancels the process, there is no feedback for the app to detect.I was thinking of using a timeout, but that seems ineligant. What's the best way to handle this?Any advice would be appreciated.
Posted Last updated
.
Post not yet marked as solved
0 Replies
1.3k Views
HI there,I'm developing an app that I would like to use the Apple Music API with.I'm trying not to use Music Kit, only the API.Here's an example of something I'd like to do, using axios and JavaScript in a React Native App: const getOptions = { method: "GET", url: "https://api.music.apple.com/v1/me/library/playlists", headers: { Authorization: "Bearer " + APPLE_MUSIC_DEVELOPER_TOKEN, "Content-Type": "application/json" } }; const { data } = await axios(getOptions);This will result in a 403 error because the user is no authentication.If using MusicKit, I could do the following: const music = window.MusicKit.getInstance(); await music.authorize(); const cloudPlaylists = await music.api.library.playlists();But this is not straight forward in React Native.So the question is, how can I authenticate the user prior to making the API calls?Thanks,Kim
Posted Last updated
.
Post not yet marked as solved
0 Replies
488 Views
In starting to work with MusicKit JS in a react app, I came across this method for loading the library (no NPM package available) in the documentation here.This is super easy, but it doesn't seem safe to be putting the developer token in a place where anyone can read it.&lt;head&gt; ... &lt;meta name="apple-music-developer-token" content="DEVELOPER-TOKEN"&gt; &lt;meta name="apple-music-app-name" content="My Cool Web App"&gt; &lt;meta name="apple-music-app-build" content="1978.4.1"&gt; ... &lt;/head&gt;QUESTION: Is this safe, and/or is there a better way to do this?For the record, I did try the approach of setting up an "musickitloaded" event listener, but I couldn't get it to fire consistently.Thanks,Kim
Posted Last updated
.
Post not yet marked as solved
1 Replies
1.4k Views
HI there,I'm writing a web app and one of the feature's I want to implement is reading another user's playlist.Ideally, I'd like to do this using the Apple Developer token only, without the app having to authenticate as an Apple Music user.Assumptions are:I would know the id of the user who's playlist I want to find/access in order to gain a list of that user's public playlists.I know the id of the playlist that I want to get.I've tryied "Get a Catalog Playlist" as described here, but it returns a "resource not found" error.And I've tryied "Search for Catalog Resources" as described here, but it returns empty results.I"m wondering if there is a way to do what I'm trying, or if I'm barking up the wrong tree here.Thanks,Kim
Posted Last updated
.