WWDC Quick Look 💓 By SwiftGGTeam
MusicKit を App に統合する

MusicKit を App に統合する

元の動画を見る

ハイライト

MusicKit は SwiftUI 向けに .musicPicker().musicSubscriptionOffer() という2つのネイティブ修飾子を提供します。音楽専用ではない App でも、数行のコードで認可、曲選択、再生制御、購読案内までを一連の体験として組み込めます。

主要内容

以前の Apple Music 連携がなぜ大変だったのか

フィットネス App にバックグラウンド音楽を追加したいとします。以前の流れはこうでした。まずユーザーの許可を求める認可ロジックを書く。次に Apple Music の購読状況を判定する。未購読なら購読を促す UI を自前で作る。購読済みなら曲選択画面を作り、再生キューを扱い、再生状態を監視する。単に BGM 機能を入れたいだけなのに、数千行近いコードを書くこともありました。

Apple が変えたこと

WWDC26 の MusicKit は、この流れを2つの SwiftUI 修飾子とネイティブの音楽ピッカーにまとめました。認可には MusicAuthorization.request()、購読案内には .musicSubscriptionOffer()、曲選択には .musicPicker()、再生制御には ApplicationMusicPlayer.shared を使います。連携作業は「App の半分を作り直す」ものから「いくつかの修飾子を追加する」ものに変わりました。

新機能がどのように課題を解くか

セッションで示されたフィットネス App を例にすると、ユーザーがサイクリングワークアウトを開始するときに「音楽を選ぶ」ボタンをタップし、.musicPicker() で Apple Music カタログまたは個人ライブラリから曲を選べます。未購読のユーザーには購読ボタンを自動的に表示し、タップすると .musicSubscriptionOffer() が出て、App を離れずに購読できます。選んだ曲は ApplicationMusicPlayer で再生され、App 内にアルバムアート、曲情報、再生コントロールを直接表示できます。体験全体がネイティブで、途切れません。

詳細

認可とプロジェクト設定

(01:03) MusicKit を組み込む最初の手順は、Xcode で MusicKit サービスを有効化することです。App ID 登録ページの App Services タブで MusicKit にチェックを入れます。Xcode が開発者トークンを自動生成するため、手動で管理する必要はありません。

(03:12) ユーザー認可は MusicAuthorization.request() でリクエストします。これは非同期メソッドで、認可状態を返します。権限ダイアログでより明確な説明を表示するには、Xcode の Signing & Capabilities に Media Library 機能を追加し、説明テキスト欄に App が音楽ライブラリを使う目的を記入します。

購読案内

(04:46) Apple Music に未購読のユーザーには、.musicSubscriptionOffer() 修飾子を使って App 内で購読案内画面を直接表示できます。ユーザーは Music App や App Store に移動する必要がありません。

@State var showSubscriptionOffer = false

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

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

重要な点:

  • MusicSubscriptionOffer.OptionsmessageIdentifier は、オファー UI に表示される文言とスタイルを決めます。.playMusic は再生シーンに向いています。
  • 開発者は Apple Services Performance Partner Program を通じて購読コミッションを得られます。
  • オファーの表示と非表示は isPresented バインディングで制御します。

(05:59) メインビューでは購読状態を監視し、ユーザーが購読可能な場合だけボタンを表示します。

@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
        }
    }
}

重要な点:

  • MusicSubscription.current は現在の購読状態を取得します。
  • MusicSubscription.subscriptionUpdatesAsyncSequence で、購読状態が変わると新しい値を自動的に流します。
  • canBecomeSubscriber は、ユーザーが購読可能かどうか、つまり未購読で、かつ地域が対応しているかを判定します。

音楽ピッカー

(08:48) .musicPicker() は SwiftUI のビュー修飾子で、Apple Music カタログと個人ライブラリを閲覧するネイティブ UI を表示します。単一選択と複数選択の両方に対応し、アルバムやプレイリストも選べます。

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

@ViewBuilder
var musicPickerButton: some View {
    Button("音楽を選ぶ", systemImage: "music.note.list") {
        showMusicPicker = true
    }
    .musicPicker(isPresented: $showMusicPicker, selection: $selectedSong)
}

重要な点:

  • selectionSong? にバインドすると単一選択、[Song] にバインドすると複数選択になります。
  • Apple Music に未購読でも使えますが、その場合ユーザーに見えるのは個人ライブラリ内のコンテンツだけです。
  • 検索、アルバム詳細ページの閲覧、プレイリスト全体の選択に対応しています。

2種類のプレイヤーの選び方

(10:54) MusicKit は2種類のプレイヤーを提供します。

特徴SystemMusicPlayerApplicationMusicPlayer
制御対象システムの Music AppApp 自身
バックグラウンド再生自動対応Audio Background Mode が必要
キューアクセス現在の項目のみ読み取り完全な読み書き
向いている場面独立した音楽クライアントフィットネスや瞑想など BGM が必要な App

ApplicationMusicPlayer のキューは遅延読み込みに対応します。アルバムやプレイリストでキューを初期化すると、プレイヤーは全曲を一度に読み込むのではなく、必要に応じて曲を読み込みます。これにより起動時の遅延を大きく減らせます。

(13:02) prepareToPlay() メソッドは、ユーザーが再生をタップする前に音声アセットを事前バッファし、タップ後の待ち時間をなくせます。

let player = ApplicationMusicPlayer.shared

// 事前にバッファする
Task {
    try await player.prepareToPlay()
}

再生制御と状態監視

(14:49) ApplicationMusicPlayerqueuestate はどちらも監視可能なオブジェクトなので、SwiftUI ビュー内で直接使えます。

現在の曲のアルバムアートを表示する例です。

@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)
        }
    }
}

重要な点:

  • ArtworkImage は MusicKit が提供する SwiftUI ビューで、アルバムアートの読み込みと表示を自動で処理します。
  • queue.currentEntry?.artwork は現在再生中の項目のアートワークリソースを取得します。
  • .quaternary の塗りつぶしをプレースホルダーに使うと、自然な視覚遷移になります。

(15:06) 曲名とサブタイトルの表示です。

if let currentSong = queue.currentEntry {
    Text(currentSong.title)
        .font(.title3.bold())

    if let subtitle = currentSong.subtitle {
        Text(subtitle)
            .font(.subheadline)
            .foregroundStyle(.secondary)
    }
}

(15:14) 再生と一時停止の制御です。

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

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

var playPause: some View {
    Button(
        isPlaying ? "一時停止" : "再生",
        systemImage: isPlaying ? "pause.fill" : "play.fill"
    ) {
        if isPlaying {
            player.pause()
        } else {
            Task {
                try await player.play()
            }
        }
    }
}

重要な点:

  • state.playbackStatus.playing.paused などの列挙値を返します。
  • player.play()player.pause() はどちらも非同期メソッドなので、Task 内で呼び出します。
  • 再生状態の変化は SwiftUI ビューの更新を自動的に引き起こします。

(15:38) 前の曲と次の曲の制御です。

let player = ApplicationMusicPlayer.shared

var controls: some View {
    HStack {
        Button("戻る", systemImage: "backward.fill") {
            Task {
                try await player.skipToPreviousEntry()
            }
        }
        // ...
        Button("次へ", systemImage: "forward.fill") {
            Task {
                try await player.skipToNextEntry()
            }
        }
    }
}

カタログリクエストと地域間の同等項目

(16:26) MusicCatalogResourceRequest は Apple Music カタログから特定のリソースをリクエストするために使います。曲の Artists リレーションなどの関連データや、返される件数の上限を指定できます。

(18:58) 地域間の同等項目は MusicKit の重要な機能です。同じ曲でも Apple Music の地域ごとに異なる ID を持つことがあり、一部の地域では explicit 版の代わりにクリーン版が提供されることもあります。.findEquivalents オプションを使うと、こうした対応付けを自動で処理できます。

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
        return response.item(for: songID)
    }

    return (featuredSong, others)
}

重要な点:

  • MusicCatalogResourceRequest<Song> はジェネリックなリクエストで、<Song> が戻り値の型を指定します。
  • matching: \.id, memberOf: songIDs は、リクエスト一覧の曲を ID で照合することを表します。
  • .findEquivalents は地域間の同等項目検索を有効化し、利用できないリソースを自動的に置き換えます。
  • response.item(for:) は元の ID で結果を取得します。実際に返ったものが同等版でも扱えます。
  • MusicItemCollection はページングに対応し、hasNextBatchnextBatch() で後続結果を取得できます。

実践アイデア

1. フィットネス App にワークアウト用 BGM を追加する

ワークアウト開始画面に「音楽を選ぶ」ボタンを追加し、.musicPicker() でユーザーにプレイリストを選ばせます。ApplicationMusicPlayer を使ってワークアウト中にループ再生します。カウントダウン中に prepareToPlay() を呼ぶと、ユーザーが「開始」をタップした瞬間に音楽を鳴らせます。

入り口 API:ApplicationMusicPlayer.shared + .musicPicker()

2. 集中タイマーにホワイトノイズや背景音楽を組み込む

ポモドーロや瞑想 App では、MusicCatalogResourceRequest を使って Apple Music 上の集中用プレイリストやホワイトノイズアルバムを取得し、プリセットとして表示できます。ユーザーは Music App で検索するために離脱せず、タップだけで再生を始められます。

入り口 API:MusicCatalogResourceRequest<Playlist> + ApplicationMusicPlayer

3. ソーシャル App に「今聴いている曲を共有」機能を追加する

queue.currentEntry で現在再生中の曲情報を取得し、ArtworkImage と組み合わせて共有カードを生成します。MusicCatalogResourceRequest.findEquivalents オプションを使えば、異なる地域のユーザーでも共有された曲リンクを正しく開けます。

入り口 API:queue.currentEntry + MusicCatalogResourceRequest + .findEquivalents

4. ゲーム App で動的に背景音楽を切り替える

戦闘、探索、ストーリーなどのゲーム場面ごとに異なる MusicItemCollection を事前読み込みし、ApplicationMusicPlayer のキュー操作で滑らかに切り替えます。Audio Background Mode を有効にすると、プレイヤーが別の App に切り替えても音楽を継続できます。

入り口 API:ApplicationMusicPlayer.shared.queue + prepareToPlay()

5. 子ども向け App でコンテンツを安全にフィルタリングする

MusicCatalogResourceRequest.findEquivalents オプションを使うと、ユーザーのアカウント設定で explicit コンテンツが利用できない場合に、MusicKit が自動でクリーン版を返します。App 側でコンテンツレーティングのロジックを維持する必要はありません。

入り口 API:MusicCatalogResourceRequest + .findEquivalents

関連セッション

コメント

GitHub Issues · utterances