mcpField
M
musickit-audio

musickit-audio

github.com/dpearson2699開発1.2k409 回インストール13 週間前に更新
リポジトリ

musickit-audio について

Appleプラットフォームのアプリ開発者向けに、MusicKitとMediaPlayerの使い方を解説するスキルです。Apple Musicの検索や購読確認、アプリ内再生、Now Playing情報とリモート操作を扱います。

主な機能

  1. 01Apple Musicカタログの検索
  2. 02購読状況の確認
  3. 03ApplicationMusicPlayerでの再生
  4. 04再生キューの管理
  5. 05Now Playingメタデータの更新
  6. 06リモート再生命令の処理

活用シーン

  1. 01アプリ内でのApple Music楽曲検索
  2. 02購読状況に応じたカタログ再生の制御
  3. 03カスタム音声のロック画面情報表示
  4. 04AirPodsやCarPlayの操作への応答

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill musickit-audio

インストール後、Claude Code で/musickit-audioを入力して読み込まれたか確認します。

同梱ファイルreferences/musickit-patterns.md

詳細

MusicKit + MediaPlayer

Apple Music カタログを検索し、ApplicationMusicPlayer で再生を管理し、サブスクリプションを確認して、MPNowPlayingInfoCenter と MPRemoteCommandCenter を介して「再生中」のメタデータを公開します。対象は Swift 6.2 / iOS 26 以降です。

目次

セットアップ

プロジェクトの設定
  1. Xcode で MusicKit ケイパビリティを有効にします(com.apple.developer.musickit エンタイトルメントが追加されます)
  2. Info.plist に NSAppleMusicUsageDescription を追加し、アプリが Apple Music にアクセスする理由を説明します
  3. バックグラウンド再生を行うには、UIBackgroundModes に audio バックグラウンドモードを追加します
インポート
import MusicKit       // Catalog, auth, playback
import MediaPlayer    // MPRemoteCommandCenter, MPNowPlayingInfoCenter

認証

ユーザーのミュージックデータにアクセスしたり、Apple Music のコンテンツを再生したりする前に、許可をリクエストします。認証プロンプトは、アプリのインストールごとに一度だけ表示されます。

func 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

カタログ検索

MusicCatalogSearchRequest を使用して Apple Music カタログを検索します。カタログ全体にアクセスするには、ユーザーが Apple Music に登録している必要があります。

func 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
}
結果の表示
for 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
    }
}

サブスクリプションの確認

再生機能を提供する前に、ユーザーが有効な Apple Music サブスクリプションに登録しているかどうかを確認します。

func 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
        }
    }
}
Apple Music の案内

ユーザーが未登録の場合は、Apple Music のサブスクリプション案内シートを表示します。

import MusicKit
import SwiftUI

struct MusicOfferView: View {
    @State private var showOffer = false

    var body: some View {
        Button("Subscribe to Apple Music") {
            showOffer = true
        }
        .musicSubscriptionOffer(isPresented: $showOffer)
    }
}

ApplicationMusicPlayer による再生

ApplicationMusicPlayer は、ミュージックアプリとは独立して Apple Music のコンテンツを再生します。システムプレーヤーの状態には影響しません。

let 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()
}
再生状態の監視
func 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
    }
}

キュー管理

ApplicationMusicPlayer.Queue を使用して再生キューを作成、操作します。

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

再生中の情報

MPNowPlayingInfoCenter を更新すると、ロック画面、コントロールセンター、CarPlay に現在のトラックのメタデータが表示されます。カスタムオーディオ(MusicKit 以外のソース)を再生する場合は必須です。Apple Music のコンテンツでは、ApplicationMusicPlayer がこの処理を自動的に行います。

import 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
}
アートワークの追加
func setArtwork(_ image: UIImage) {
    let artwork = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
    var info = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [:]
    info[MPMediaItemPropertyArtwork] = artwork
    MPNowPlayingInfoCenter.default().nowPlayingInfo = info
}

リモートコマンドセンター

MPRemoteCommandCenter のハンドラーを登録して、ロック画面のコントロール、AirPods のタップ操作、CarPlay のボタンに応答します。

func 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
}
シーク操作のサポート
func 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
    }
}

よくある間違い

やってはいけないこと:MusicKit エンタイトルメントまたは使用目的の説明を省略する

MusicKit エンタイトルメントがないと、認証時にアプリがクラッシュします。NSAppleMusicUsageDescription がない場合、App Review で申請が却下されます。

// WRONG: No entitlement configured
let status = await MusicAuthorization.request() // Crashes

// CORRECT: Enable MusicKit capability in Xcode first,
// then add NSAppleMusicUsageDescription to Info.plist
let status = await MusicAuthorization.request()
やってはいけないこと:再生前にサブスクリプションを確認し忘れる

サブスクリプションに登録せずにカタログのコンテンツを再生しようとすると、失敗が通知されずに再生されないか、エラーがスローされます。

// WRONG
func play(_ song: Song) async throws {
    player.queue = [song]
    try await player.play() // Fails if no subscription
}

// CORRECT
func play(_ song: Song) async throws {
    let sub = try await MusicSubscription.current
    guard sub.canPlayCatalogContent else {
        showSubscriptionOffer()
        return
    }
    player.queue = [song]
    try await player.play()
}
やってはいけないこと:ApplicationMusicPlayer を使うべき場面で SystemMusicPlayer を使う

SystemMusicPlayer はミュージックアプリのグローバルキューを操作します。変更はユーザーのミュージックアプリの状態に影響します。アプリ内で完結する再生には ApplicationMusicPlayer を使用してください。

// WRONG: Modifies the user's Music app queue
let player = SystemMusicPlayer.shared

// CORRECT: App-scoped playback
let player = ApplicationMusicPlayer.shared
やってはいけないこと:トラック変更時に「再生中」の情報を更新し忘れる

ロック画面に古いメタデータが表示されると、ユーザーが混乱します。現在のトラックが変わるたびに、「再生中」の情報を更新してください。

// WRONG: Set once and forget
updateNowPlaying(title: firstSong.title, ...)

// CORRECT: Update on every track change
func onTrackChanged(_ song: Song) {
    updateNowPlaying(
        title: song.title,
        artist: song.artistName,
        duration: song.duration ?? 0,
        elapsed: 0
    )
}
やってはいけないこと:処理せずにリモートコマンドを登録する

コマンドを登録して .commandFailed を返すと、ロック画面のコントロールが機能しなくなります。サポートしていないコマンドは、代わりに無効にしてください。

// WRONG
center.skipForwardCommand.addTarget { _ in .commandFailed }

// CORRECT
center.skipForwardCommand.isEnabled = false

レビューチェックリスト

  • Xcode プロジェクトで MusicKit の機能を有効化している
  • Info.plist に NSAppleMusicUsageDescription を追加している
  • MusicKit にアクセスする前に MusicAuthorization.request() を呼び出している
  • カタログの再生を試みる前にサブスクリプションを確認している
  • アプリ内での再生には SystemMusicPlayer ではなく ApplicationMusicPlayer を使用している
  • 音楽をバックグラウンドで再生する場合は、バックグラウンドオーディオモードを有効にしている
  • カスタムオーディオでは、トラックが変わるたびに再生中情報を更新している
  • リモートコマンドのハンドラーが、対応するコマンドに対して .success を返す
  • 対応していないリモートコマンドは isEnabled = false で無効にしている
  • ロック画面に表示するため、再生中情報にアートワークを設定している
  • スクラバーの精度を保つため、再生経過時間を定期的に更新している
  • ユーザーが Apple Music のサブスクリプションに登録していない場合、サブスクリプションの案内を表示している

参考資料

コメント