关于 axiom-now-playing-musickit
面向使用 MusicKit 集成 Apple Music 的 Swift 技能,介绍授权、订阅检查、播放和队列管理,并说明 ApplicationMusicPlayer 会自动更新 Now Playing 信息。
主要功能
- 01请求 MusicKit 授权
- 02检查 Apple Music 订阅
- 03控制播放和队列
- 04观察播放及队列状态
- 05处理混合内容播放
适用场景
- 01构建 Apple Music 播放流程
- 02根据订阅状态控制目录播放
- 03显示当前曲目和封面
- 04将 Apple 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