Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Integrate Apple Music playback, catalog search, and Now Playing metadata using MusicKit and MediaPlayer. Use when adding music search, Apple Music subscription flows, queue management, playback controls, remote command handling, or Now Playing info to iOS apps.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 113% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 90% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 251% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 99% | 0% |
Search the Apple Music catalog, manage playback with ApplicationMusicPlayer, check subscriptions, and publish Now Playing metadata via MPNowPlayingInfoCenter and MPRemoteCommandCenter.
MusicSubscription.current before queueing playback.ApplicationMusicPlayer for app-scoped playback; wire Now Playing and remote commands only when the app owns those surfaces.NSAppleMusicUsageDescription to Info.plist explaining why the app accesses the user's media library.audio background mode to UIBackgroundModes.swiftimport MusicKit // Catalog, auth, playback import MediaPlayer // MPRemoteCommandCenter, MPNowPlayingInfoCenter
Request permission before accessing the user's music data or playing Apple Music content. request() presents Apple's consent dialog when necessary; use currentStatus to read the current setting without prompting.
swiftfunc requestMusicAccess() async -> MusicAuthorization.Status { let status = await MusicAuthorization.request() switch status { case .authorized: // Full access to MusicKit APIs break case .denied, .restricted: // Show guidance to enable in Settings break case .notDetermined: break @unknown default: break } return status } // Check current status without prompting let current = MusicAuthorization.currentStatus
Use MusicCatalogSearchRequest to search the Apple Music catalog. Catalog lookup can fetch Apple Music resources, but playback of subscription catalog content must still be gated on MusicSubscription.current.canPlayCatalogContent.
swiftfunc searchCatalog(term: String) async throws -> MusicItemCollection<Song> { var request = MusicCatalogSearchRequest(term: term, types: [Song.self]) request.limit = 25 let response = try await request.response() return response.songs }
swiftfor song in songs { print("\(song.title) by \(song.artistName)") if let artwork = song.artwork { let url = artwork.url(width: 300, height: 300) // Load artwork from url } }
Check whether the user has an active Apple Music subscription before offering playback features.
swiftfunc checkSubscription() async throws -> Bool { let subscription = try await MusicSubscription.current return subscription.canPlayCatalogContent } // Observe subscription changes func observeSubscription() async { for await subscription in MusicSubscription.subscriptionUpdates { if subscription.canPlayCatalogContent { // Enable full playback UI } else { // Show subscription offer } } }
Present the Apple Music subscription offer sheet when the user is not subscribed. Check canBecomeSubscriber first, and pass MusicSubscriptionOffer.Options or onLoadCompletion when the sheet needs contextual metadata or load-error handling.
swiftimport MusicKit import SwiftUI struct MusicOfferView: View { @State private var showOffer = false var body: some View { Button("Subscribe to Apple Music") { Task { let subscription = try? await MusicSubscription.current showOffer = subscription?.canBecomeSubscriber == true } } .musicSubscriptionOffer( isPresented: $showOffer, options: .default, onLoadCompletion: { error in if let error { // Surface loading errors in app UI or diagnostics. print(error) } } ) } }
ApplicationMusicPlayer plays Apple Music content independently from the Music app. It does not affect the system player's state.
swiftlet player = ApplicationMusicPlayer.shared func playSong(_ song: Song) async throws { player.queue = [song] try await player.play() } func pause() { player.pause() } func skipToNext() async throws { try await player.skipToNextEntry() }
swiftfunc observePlayback() { // player.state is an @Observable property let state = player.state switch state.playbackStatus { case .playing: break case .paused: break case .stopped, .interrupted, .seekingForward, .seekingBackward: break @unknown default: break } }
Build and manipulate the playback queue using ApplicationMusicPlayer.Queue.
swift// Initialize with multiple items func playAlbum(_ album: Album) async throws { player.queue = [album] try await player.play() } // Append songs to the existing queue func appendToQueue(_ songs: [Song]) async throws { try await player.queue.insert(songs, position: .tail) } // Insert song to play next func playNext(_ song: Song) async throws { try await player.queue.insert(song, position: .afterCurrentEntry) }
Update MPNowPlayingInfoCenter so the Lock Screen, Control Center, and CarPlay display current track metadata. This is essential when playing custom audio (non-MusicKit sources). ApplicationMusicPlayer handles this automatically for Apple Music content.
swiftimport MediaPlayer func updateNowPlaying(title: String, artist: String, duration: TimeInterval, elapsed: TimeInterval) { var info = [String: Any]() info[MPMediaItemPropertyTitle] = title info[MPMediaItemPropertyArtist] = artist info[MPMediaItemPropertyPlaybackDuration] = duration info[MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsed info[MPNowPlayingInfoPropertyPlaybackRate] = 1.0 info[MPNowPlayingInfoPropertyMediaType] = MPNowPlayingInfoMediaType.audio.rawValue MPNowPlayingInfoCenter.default().nowPlayingInfo = info } func clearNowPlaying() { MPNowPlayingInfoCenter.default().nowPlayingInfo = nil }
swiftfunc setArtwork(_ image: UIImage) { let artwork = MPMediaItemArtwork(boundsSize: image.size) { _ in image } var info = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [:] info[MPMediaItemPropertyArtwork] = artwork MPNowPlayingInfoCenter.default().nowPlayingInfo = info }
Register handlers for MPRemoteCommandCenter to respond to Lock Screen controls, AirPods tap gestures, and CarPlay buttons.
swiftfunc setupRemoteCommands() { let center = MPRemoteCommandCenter.shared() center.playCommand.addTarget { _ in resumePlayback() return .success } center.pauseCommand.addTarget { _ in pausePlayback() return .success } center.nextTrackCommand.addTarget { _ in skipToNext() return .success } center.previousTrackCommand.addTarget { _ in skipToPrevious() return .success } // Disable commands you do not support center.seekForwardCommand.isEnabled = false center.seekBackwardCommand.isEnabled = false }
swiftfunc enableScrubbing() { let center = MPRemoteCommandCenter.shared() center.changePlaybackPositionCommand.addTarget { event in guard let positionEvent = event as? MPChangePlaybackPositionCommandEvent else { return .commandFailed } seek(to: positionEvent.positionTime) return .success } }
| Mistake | Fix | |---|---| | Debugging authorization before configuring the App Service and purpose string | Verify service, bundle ID, and NSAppleMusicUsageDescription first. | | Queueing catalog content without a subscription gate | Check canPlayCatalogContent; offer subscription only when canBecomeSubscriber. | | Using SystemMusicPlayer for app-owned playback | Use ApplicationMusicPlayer; the system player changes the Music app's global queue. | | Publishing Now Playing metadata once | Refresh it on track, duration, rate, and elapsed-time changes. | | Registering unsupported remote commands | Disable them; supported handlers must perform the action and return .success. |
NSAppleMusicUsageDescription added to Info.plistMusicAuthorization.request() called before any MusicKit accesscanBecomeSubscriber checked before presenting a subscription offerhasCloudLibraryEnabled checked before library writesApplicationMusicPlayer used (not SystemMusicPlayer) for app-scoped playback.success for supported commandsisEnabled = falseOther measured skills in the registry, with their headline benchmark lift.