axiom-now-playing-musickit について
MusicKitでApple Musicを統合するためのSwiftスキルです。認証、サブスクリプション確認、再生とキュー管理に加え、ApplicationMusicPlayerがNow Playing情報を自動更新する仕組みを説明します。
主な機能
- 01MusicKitの認証要求
- 02Apple Musicのサブスクリプション確認
- 03再生とキューの制御
- 04再生状態とキューの監視
- 05ハイブリッドコンテンツの再生
活用シーン
- 01Apple Musicの再生フロー構築
- 02サブスクリプションに応じたカタログ再生制御
- 03現在の曲とアートワークの表示
- 04Apple Musicとアプリ内コンテンツの併用
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-now-playing-musickitインストール後、Claude Code で/axiom-now-playing-musickitを入力して読み込まれたか確認します。
詳細
MusicKit の統合(Apple Music)
所要時間:5~10 分
重要なポイント
MusicKit の ApplicationMusicPlayer は、MPNowPlayingInfoCenter に情報を自動的に公開します。 Apple Music のコンテンツを再生する場合、「再生中」の情報を手動で更新する必要はありません。
自動で処理される項目
ApplicationMusicPlayer を使用すると、次の項目が自動的に処理されます。
- トラック名、アーティスト、アルバム
- アートワーク(Apple のアルバムアート)
- 再生時間と経過時間
- 再生レート(再生中/一時停止の状態)
MPNowPlayingInfoCenter の更新は、システムがすべて処理します。
自動で処理されない項目
- カスタムメタデータ(チャプターマーカー、カスタムのアーティストノート)
- 標準のコントロールを超えるリモートコマンドのカスタマイズ
- MusicKit のコンテンツと独自のコンテンツの混在
サブスクリプションと認証
Music の認証を確認する
import MusicKit
func requestMusicAccess() async -> Bool {
let status = await MusicAuthorization.request()
return status == .authorized
}
// Check current status without prompting
let currentStatus = MusicAuthorization.currentStatus
// .authorized, .denied, .notDetermined, .restricted
Apple Music のサブスクリプションを確認する
func checkSubscription() async -> Bool {
do {
let subscription = try await MusicSubscription.current
return subscription.canPlayCatalogContent
} catch {
return false
}
}
// Observe subscription changes
func observeSubscription() {
Task {
for await subscription in MusicSubscription.subscriptionUpdates {
if subscription.canPlayCatalogContent {
// Full Apple Music access
} else if subscription.canBecomeSubscriber {
// Show subscription offer
showSubscriptionOffer()
}
}
}
}
サブスクリプションのオファーシート
import MusicKit
import StoreKit
// Present Apple Music subscription offer
MusicSubscriptionOffer.Options(
messageIdentifier: .playMusic,
itemID: song.id
)
// In SwiftUI
.musicSubscriptionOffer(isPresented: $showOffer, options: offerOptions)
サブスクリプションがない場合の適切なフォールバック
@MainActor
class MusicPlayer: ObservableObject {
@Published var canPlay = false
func handlePlayRequest(song: Song) async {
let authorized = await requestMusicAccess()
guard authorized else {
showAuthorizationDeniedAlert()
return
}
do {
let subscription = try await MusicSubscription.current
if subscription.canPlayCatalogContent {
// Full playback
try await play(song: song)
} else {
// Preview only (30-second clips)
if let previewURL = song.previewAssets?.first?.url {
playPreview(url: previewURL)
}
}
} catch {
handleError(error)
}
}
}
再生
基本的な再生
import MusicKit
@MainActor
class MusicKitPlayer {
private let player = ApplicationMusicPlayer.shared
func play(song: Song) async throws {
// ✅ Just play - MPNowPlayingInfoCenter updates automatically
player.queue = [song]
try await player.play()
// ❌ DO NOT manually set nowPlayingInfo here
// MPNowPlayingInfoCenter.default().nowPlayingInfo = [...] // WRONG!
}
func pause() {
player.pause()
}
func stop() {
player.stop()
}
}
再生状態の監視
@MainActor
class PlayerViewModel: ObservableObject {
private let player = ApplicationMusicPlayer.shared
@Published var isPlaying = false
@Published var currentEntry: ApplicationMusicPlayer.Queue.Entry?
@Published var playbackTime: TimeInterval = 0
func observeState() {
// Observe playback status
Task {
for await state in player.state.objectWillChange.values {
isPlaying = player.state.playbackStatus == .playing
}
}
// Observe current entry (track changes)
Task {
for await queue in player.queue.objectWillChange.values {
currentEntry = player.queue.currentEntry
}
}
}
}
キュー管理
キューの設定
let player = ApplicationMusicPlayer.shared
// Single song
player.queue = [song]
// Album
player.queue = ApplicationMusicPlayer.Queue(album: album)
// Playlist
player.queue = ApplicationMusicPlayer.Queue(playlist: playlist)
// Multiple items
player.queue = ApplicationMusicPlayer.Queue(for: [song1, song2, song3])
// Start at specific item
player.queue = ApplicationMusicPlayer.Queue(for: songs, startingAt: songs[2])
キューの操作
// Skip to next
try await player.skipToNextEntry()
// Skip to previous
try await player.skipToPreviousEntry()
// Restart current track
player.restartCurrentEntry()
// Append to queue
try await player.queue.insert(song, position: .afterCurrentEntry)
try await player.queue.insert(song, position: .tail) // End of queue
// Shuffle and repeat
player.state.shuffleMode = .songs // .off, .songs
player.state.repeatMode = .all // .none, .one, .all
キューの変更の監視
// Current track info
if let entry = player.queue.currentEntry {
let title = entry.title
let subtitle = entry.subtitle // Artist name
let artwork = entry.artwork // Artwork for display
// Get full Song object if needed
if case .song(let song) = entry.item {
let albumTitle = song.albumTitle
}
}
ハイブリッドアプリ(独自コンテンツ+Apple Music)
アプリで Apple Music と独自のコンテンツの両方を再生する場合:
import MusicKit
@MainActor
class HybridPlayer {
private let musicKitPlayer = ApplicationMusicPlayer.shared
private var avPlayer: AVPlayer?
private var currentSource: ContentSource = .none
enum ContentSource {
case none
case appleMusic // MusicKit handles Now Playing
case ownContent // We handle Now Playing
}
func playAppleMusicSong(_ song: Song) async throws {
// Switch to MusicKit
avPlayer?.pause()
currentSource = .appleMusic
musicKitPlayer.queue = [song]
try await musicKitPlayer.play()
// ✅ MusicKit handles Now Playing automatically
}
func playOwnContent(_ url: URL) {
// Switch to AVPlayer
musicKitPlayer.pause()
currentSource = .ownContent
avPlayer = AVPlayer(url: url)
avPlayer?.play()
// ✅ Manually update Now Playing (see axiom-now-playing)
updateNowPlayingForOwnContent()
}
private func updateNowPlayingForOwnContent() {
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "My Track"
// ... rest of manual setup
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
}
よくある間違い
// ❌ WRONG - Overwrites MusicKit's automatic Now Playing data
func playAppleMusicSong(_ song: Song) async throws {
try await ApplicationMusicPlayer.shared.play()
// ❌ This clears MusicKit's Now Playing info!
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = song.title
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
// ✅ CORRECT - Let MusicKit handle it
func playAppleMusicSong(_ song: Song) async throws {
try await ApplicationMusicPlayer.shared.play()
// That's it! MusicKit publishes Now Playing automatically.
}
MusicKit で手動更新を使用する場合
次の場合に限り、MPNowPlayingInfoCenter を上書きしてください。
- メタデータを追加する場合(例:ポッドキャストのチャプターマーカー)
- Apple Music と並べてカスタムコンテンツを表示する場合
- MusicKit の自動動作を置き換える具体的な理由がある場合
デフォルト:MusicKit に「再生中」の情報を自動管理させます。
リソース
ドキュメント:/musickit、/musickit/applicationmusicplayer、/musickit/musicsubscription
スキル:axiom-now-playing、axiom-now-playing-carplay
コメント
Markdown 対応