WWDC.ai

Integrate MusicKit into your app

Use MusicKit to authorize music access, present subscription offers, pick Apple Music or library content, control playback, and fetch catalog songs.

Watch on Apple Developer

TL;DR

  • Configure MusicKit by enabling the MusicKit service for the App ID, adding the Media Library capability in Xcode, and requesting MusicAuthorization before accessing music content.
  • Use .musicSubscriptionOffer with MusicSubscription.current and MusicSubscription.subscriptionUpdates to show an in-app Apple Music offer only when a person can become a subscriber.
  • Use the SwiftUI .musicPicker modifier to select Song values from a unified Apple Music catalog and library UI; without a subscription, it shows only the person's library.
  • Choose between SystemMusicPlayer and ApplicationMusicPlayer, build playback UI from observable player state and queue data, and use MusicCatalogResourceRequest with .findEquivalents for storefront-aware catalog content.

Project setup, authorization, and subscription state

MusicKit is a Swift framework for Apple platforms that integrates with Swift concurrency and SwiftUI. To make MusicKit requests, the app needs a developer token. The session shows the automatic-token path: enable the MusicKit checkbox for the app's App ID in the developer portal, then make sure Xcode is signed into the same developer account.

Before browsing or playing a person's music content, request access with MusicAuthorization.request(). Add the Media Library capability in Xcode and provide a usage description; that text appears in the MusicKit permission alert.

An Apple Music subscription is not required for MusicKit, but without one the app can access only purchased or synced music. If the person can become a subscriber, present an in-app subscription offer using .musicSubscriptionOffer and keep subscription state current with MusicSubscription.current and MusicSubscription.subscriptionUpdates.

  • Enable MusicKit for the App ID in App Services so developer tokens can be generated automatically.
  • Add the Media Library capability and describe why the app needs access to music content.
  • Use MusicSubscription.canBecomeSubscriber to decide whether to show a subscription button.
  • MusicSubscriptionOffer.Options can include partner-program information and a messageIdentifier, such as .playMusic.

Present an Apple Music subscription offer

Shows the in-app Apple Music subscription UI with a message tailored for playing music.

@State var showSubscriptionOffer = false

let options = MusicSubscriptionOffer.Options(
    messageIdentifier: .playMusic
)

@ViewBuilder var musicSubscriptionButton: some View {
    Button("Subscribe to Apple Music", systemImage: "music.note") {
        showSubscriptionOffer = true
    }
    .musicSubscriptionOffer(isPresented: $showSubscriptionOffer, options: options)
}

Track subscription updates

Fetches the current subscription and listens for changes after authorization.

@State var subscription: MusicSubscription?

var body: some View {
    VStack {
        if let subscription, subscription.canBecomeSubscriber {
            musicSubscriptionButton
        }
    }
    .task(id: isAuthorized) {
        self.subscription = try? await MusicSubscription.current
        for await subscription in MusicSubscription.subscriptionUpdates {
            self.subscription = subscription
        }
    }
}

Music items and Music Picker

MusicKit models content as music items. Items such as Album, Song, Genre, Station, and Playlist are value types with attributes, relationships, and associations. Attributes are direct properties like an album title or content rating; relationships describe strongly related content such as an album's tracks; associations describe related content with weaker ties, such as other versions of an album.

The Music Picker is exposed as a SwiftUI view modifier and presents a unified interface for the Apple Music catalog and the person's library. If the person does not have an Apple Music subscription, the picker is still usable, but it shows library items rather than both library and catalog content.

The picker can bind to a single selected item or to an array for multi-selection. It can also select container content such as albums or playlists from their detail pages.

  • Use .musicPicker(isPresented:selection:) to present the picker from any SwiftUI view.
  • Bind selection to Song? for a single song or to an array for multiple selections.
  • The picker handles browsing and search across supported MusicKit content sources.
  • Subscription status affects catalog availability, not whether the picker itself can be shown.

Add a Music Picker for a song

Presents the Music Picker and stores the selected Song in SwiftUI state.

@State var showMusicPicker = false
@State var selectedSong: Song? = nil

@ViewBuilder var musicPickerButton: some View {
    Button("Pick some Music", systemImage: "music.note.list") {
        showMusicPicker = true
    }
    .musicPicker(isPresented: $showMusicPicker, selection: $selectedSong)
}

Players, queues, and playback behavior

MusicKit provides SystemMusicPlayer and ApplicationMusicPlayer, both subclasses of MusicPlayer. SystemMusicPlayer controls the system Music app: apps can set its queue, but cannot inspect the full queue beyond the current item. ApplicationMusicPlayer plays from within the app and gives full read/write access to its queue.

Both players support playback state such as repeat and shuffle, and both can control whether playback affects the Music app's listening history through affectsListeningHistory. By default, playback generally appears in Recently Played, while respecting the Music app's Use Listening History setting.

SystemMusicPlayer continues playing if the app backgrounds or quits because it controls the system Music app. To get comparable background playback with ApplicationMusicPlayer, enable the Audio Background Mode capability.

  • Create queues from playable items such as songs, albums, and playlists.
  • Use special queue initializers for container types to lazily load their contents.
  • Call prepareToPlay() when upcoming content is known to reduce delay before audible playback.
  • Call play() to start and pause() to stop playback.
  • Observe player state and queue objects directly in SwiftUI for playback UI.

Building playback UI in SwiftUI

The workout app UI reads ApplicationMusicPlayer.shared.queue to display the current entry's artwork, title, and subtitle. ArtworkImage renders MusicKit artwork when available, while a placeholder can cover the empty state.

Playback controls derive their state from ApplicationMusicPlayer.shared.state. The play/pause button checks playbackStatus, calls pause() when already playing, and otherwise calls async play(). Previous and next controls call async skip methods on the player.

  • Use queue.currentEntry?.artwork with ArtworkImage for now-playing artwork.
  • Use queue.currentEntry?.title and optional subtitle for now-playing labels.
  • Use state.playbackStatus == .playing to derive play/pause UI.
  • Use skipToPreviousEntry() and skipToNextEntry() for transport controls.

Display current artwork and song information

Uses the application player queue as the source of truth for now-playing artwork and metadata.

@State var queue = ApplicationMusicPlayer.shared.queue

var body: some View {
    VStack {
        if let artwork = queue.currentEntry?.artwork {
            ArtworkImage(artwork, width: 200, height: 200)
        } else {
            RoundedRectangle(cornerRadius: 16)
                .fill(.quaternary)
                .frame(width: 200, height: 200)
        }

        if let currentSong = queue.currentEntry {
            Text(currentSong.title)
                .font(.title3.bold())
            if let subtitle = currentSong.subtitle {
                Text(subtitle)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }
        }
    }
}

Play, pause, previous, and next controls

Builds simple transport controls around ApplicationMusicPlayer state and async player operations.

let player = ApplicationMusicPlayer.shared
@State var state = ApplicationMusicPlayer.shared.state

var isPlaying: Bool {
    state.playbackStatus == .playing
}

var controls: some View {
    HStack {
        Button("Back", systemImage: "backward.fill") {
            Task { try await player.skipToPreviousEntry() }
        }

        Button(isPlaying ? "Pause" : "Play",
               systemImage: isPlaying ? "pause.fill" : "play.fill") {
            if isPlaying {
                player.pause()
            } else {
                Task { try await player.play() }
            }
        }

        Button("Next", systemImage: "forward.fill") {
            Task { try await player.skipToNextEntry() }
        }
    }
}

Catalog requests and storefront equivalency

Catalog requests let an app query Apple Music independently of the person's library, which is useful for curated shelves or quick-start recommendations. MusicCatalogResourceRequest is a structured request for a specific resource type, such as Song. Requests can configure options, requested relationships and associations, and result limits.

Calling async response() returns a MusicCatalogResourceResponse with strongly typed results in a MusicItemCollection. Collections support pagination: when hasNextBatch is true, fetch more items with async nextBatch().

Resource availability can vary by account settings, storefront, region, and explicit-content restrictions. A requested resource may have an equivalent item with a different ID in another region, or a clean equivalent when explicit content is unavailable. The .findEquivalents option enables that equivalency behavior, and callers should handle missing results because the catalog is not guaranteed to return every requested item.

  • Use structured catalog requests for Apple Music API content in MusicKit.
  • Use matching: \.id, memberOf: ids to request specific known catalog IDs.
  • Use .findEquivalents for cross-storefront or content-restriction equivalents.
  • Use response.item(for:) to retrieve the returned item corresponding to an input ID.
  • Always handle unavailable catalog resources.

Fetch songs by catalog IDs with equivalency

Requests a set of songs, allows storefront/content equivalents, and safely maps returned items back to requested IDs.

func fetchSongs(songIDs: [MusicItemID]) async throws -> (featured: Song?, other: [Song]) {
    var request = MusicCatalogResourceRequest<Song>(matching: \.id, memberOf: songIDs)
    request.options = [.findEquivalents]

    let response = try await request.response()

    let featuredSongID = songIDs[0]
    let featuredSong = response.item(for: featuredSongID)

    let others: [Song] = songIDs[1...].compactMap { songID in
        response.item(for: songID)
    }

    return (featuredSong, others)
}

Resources

Unofficial, not associated with Apple. BySuperwall

On this page

Ask AI