axiom-now-playing-musickit by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-now-playing-musickit所需时间 : 5-10 分钟
MusicKit 的 ApplicationMusicPlayer 会自动发布信息到 MPNowPlayingInfoCenter。 播放 Apple Music 内容时,你无需手动更新“正在播放”信息。
使用 ApplicationMusicPlayer 时,以下内容会自动处理:
系统会为你处理所有 MPNowPlayingInfoCenter 的更新。
import MusicKit
func requestMusicAccess() async -> Bool {
let status = await MusicAuthorization.request()
return status == .authorized
}
// 检查当前状态而不提示用户
let currentStatus = MusicAuthorization.currentStatus
// .authorized, .denied, .notDetermined, .restricted
func checkSubscription() async -> Bool {
do {
let subscription = try await MusicSubscription.current
return subscription.canPlayCatalogContent
} catch {
return false
}
}
// 监听订阅状态变化
func observeSubscription() {
Task {
for await subscription in MusicSubscription.subscriptionUpdates {
if subscription.canPlayCatalogContent {
// 拥有完整的 Apple Music 访问权限
} else if subscription.canBecomeSubscriber {
// 显示订阅优惠
showSubscriptionOffer()
}
}
}
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
import MusicKit
import StoreKit
// 呈现 Apple Music 订阅优惠
MusicSubscriptionOffer.Options(
messageIdentifier: .playMusic,
itemID: song.id
)
// 在 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 {
// 完整播放
try await play(song: song)
} else {
// 仅预览(30 秒片段)
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 {
// ✅ 直接播放 - MPNowPlayingInfoCenter 会自动更新
player.queue = [song]
try await player.play()
// ❌ 不要在此处手动设置 nowPlayingInfo
// MPNowPlayingInfoCenter.default().nowPlayingInfo = [...] // 错误!
}
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() {
// 监听播放状态
Task {
for await state in player.state.objectWillChange.values {
isPlaying = player.state.playbackStatus == .playing
}
}
// 监听当前条目(曲目变化)
Task {
for await queue in player.queue.objectWillChange.values {
currentEntry = player.queue.currentEntry
}
}
}
}
let player = ApplicationMusicPlayer.shared
// 单曲
player.queue = [song]
// 专辑
player.queue = ApplicationMusicPlayer.Queue(album: album)
// 播放列表
player.queue = ApplicationMusicPlayer.Queue(playlist: playlist)
// 多个项目
player.queue = ApplicationMusicPlayer.Queue(for: [song1, song2, song3])
// 从特定项目开始播放
player.queue = ApplicationMusicPlayer.Queue(for: songs, startingAt: songs[2])
// 跳到下一首
try await player.skipToNextEntry()
// 跳到上一首
try await player.skipToPreviousEntry()
// 重新播放当前曲目
player.restartCurrentEntry()
// 添加到队列
try await player.queue.insert(song, position: .afterCurrentEntry)
try await player.queue.insert(song, position: .tail) // 队列末尾
// 随机播放和重复模式
player.state.shuffleMode = .songs // .off, .songs
player.state.repeatMode = .all // .none, .one, .all
// 当前曲目信息
if let entry = player.queue.currentEntry {
let title = entry.title
let subtitle = entry.subtitle // 艺术家名称
let artwork = entry.artwork // 用于显示的专辑封面
// 如果需要,获取完整的 Song 对象
if case .song(let song) = entry.item {
let albumTitle = song.albumTitle
}
}
如果你的应用同时播放 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 处理“正在播放”信息
case ownContent // 我们处理“正在播放”信息
}
func playAppleMusicSong(_ song: Song) async throws {
// 切换到 MusicKit
avPlayer?.pause()
currentSource = .appleMusic
musicKitPlayer.queue = [song]
try await musicKitPlayer.play()
// ✅ MusicKit 自动处理“正在播放”信息
}
func playOwnContent(_ url: URL) {
// 切换到 AVPlayer
musicKitPlayer.pause()
currentSource = .ownContent
avPlayer = AVPlayer(url: url)
avPlayer?.play()
// ✅ 手动更新“正在播放”信息(参见 axiom-now-playing)
updateNowPlayingForOwnContent()
}
private func updateNowPlayingForOwnContent() {
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "我的曲目"
// ... 其余的手动设置
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
}
// ❌ 错误 - 覆盖了 MusicKit 自动的“正在播放”数据
func playAppleMusicSong(_ song: Song) async throws {
try await ApplicationMusicPlayer.shared.play()
// ❌ 这会清除 MusicKit 的“正在播放”信息!
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = song.title
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
// ✅ 正确 - 让 MusicKit 处理
func playAppleMusicSong(_ song: Song) async throws {
try await ApplicationMusicPlayer.shared.play()
// 就这样!MusicKit 会自动发布“正在播放”信息。
}
仅在以下情况下覆盖 MPNowPlayingInfoCenter:
默认做法:让 MusicKit 自动管理“正在播放”信息。
文档 : /musickit, /musickit/applicationmusicplayer, /musickit/musicsubscription
技能 : axiom-now-playing, axiom-now-playing-carplay
每周安装量
92
代码仓库
GitHub 星标数
610
首次出现时间
Jan 21, 2026
安全审计
安装于
opencode76
codex70
claude-code70
gemini-cli69
cursor69
github-copilot65
Time cost : 5-10 minutes
MusicKit's ApplicationMusicPlayer automatically publishes to MPNowPlayingInfoCenter. You don't need to manually update Now Playing info when playing Apple Music content.
When using ApplicationMusicPlayer:
The system handles all MPNowPlayingInfoCenter updates for you.
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
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
}
}
If your app plays both Apple Music and your own content:
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.
}
Only override MPNowPlayingInfoCenter if:
Default : Let MusicKit manage Now Playing automatically.
Docs : /musickit, /musickit/applicationmusicplayer, /musickit/musicsubscription
Skills : axiom-now-playing, axiom-now-playing-carplay
Weekly Installs
92
Repository
GitHub Stars
610
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode76
codex70
claude-code70
gemini-cli69
cursor69
github-copilot65
AI播客创作工具 - 使用inference.sh CLI快速生成AI驱动播客和音频内容
7,800 周安装