musickit-audio by dpearson2699/swift-ios-skills
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill musickit-audio搜索 Apple Music 目录,使用 ApplicationMusicPlayer 管理播放,检查订阅状态,并通过 MPNowPlayingInfoCenter 和 MPRemoteCommandCenter 发布正在播放的元数据。目标平台:Swift 6.2 / iOS 26+。
com.apple.developer.musickit 权限)NSAppleMusicUsageDescription,说明应用为何访问 Apple Musicaudio 后台模式添加到 广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
UIBackgroundModesimport MusicKit // Catalog, auth, playback
import MediaPlayer // MPRemoteCommandCenter, MPNowPlayingInfoCenter
在访问用户的音乐数据或播放 Apple Music 内容之前,请求权限。授权在每次应用安装时是一次性提示。
func requestMusicAccess() async -> MusicAuthorization.Status {
let status = await MusicAuthorization.request()
switch status {
case .authorized:
// 完全访问 MusicKit API
break
case .denied, .restricted:
// 显示引导信息,提示用户在设置中启用
break
case .notDetermined:
break
@unknown default:
break
}
return status
}
// 检查当前状态而不触发提示
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)
// 从 url 加载封面图
}
}
在提供播放功能之前,检查用户是否拥有有效的 Apple Music 订阅。
func checkSubscription() async throws -> Bool {
let subscription = try await MusicSubscription.current
return subscription.canPlayCatalogContent
}
// 监听订阅状态变化
func observeSubscription() async {
for await subscription in MusicSubscription.subscriptionUpdates {
if subscription.canPlayCatalogContent {
// 启用完整的播放 UI
} else {
// 显示订阅提供界面
}
}
}
当用户未订阅时,呈现 Apple Music 订阅提供界面。
import MusicKit
import SwiftUI
struct MusicOfferView: View {
@State private var showOffer = false
var body: some View {
Button("订阅 Apple Music") {
showOffer = true
}
.musicSubscriptionOffer(isPresented: $showOffer)
}
}
ApplicationMusicPlayer 独立于 Music 应用播放 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 是一个 @Observable 属性
let state = player.state
switch state.playbackStatus {
case .playing:
break
case .paused:
break
case .stopped, .interrupted, .seekingForward, .seekingBackward:
break
@unknown default:
break
}
}
使用 ApplicationMusicPlayer.Queue 构建和操作播放队列。
// 使用多个项目初始化队列
func playAlbum(_ album: Album) async throws {
player.queue = [album]
try await player.play()
}
// 将歌曲追加到现有队列
func appendToQueue(_ songs: [Song]) async throws {
try await player.queue.insert(songs, position: .tail)
}
// 插入歌曲作为下一首播放
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
}
// 禁用不支持的命令
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 权限,你的应用会在授权时崩溃。没有 NSAppleMusicUsageDescription,应用审核会拒绝提交。
// 错误:未配置权限
let status = await MusicAuthorization.request() // 崩溃
// 正确:首先在 Xcode 中启用 MusicKit 能力,
// 然后将 NSAppleMusicUsageDescription 添加到 Info.plist
let status = await MusicAuthorization.request()
尝试在没有订阅的情况下播放目录内容会静默失败或抛出异常。
// 错误
func play(_ song: Song) async throws {
player.queue = [song]
try await player.play() // 如果没有订阅则失败
}
// 正确
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()
}
SystemMusicPlayer 控制全局的 Music 应用队列。更改会影响用户的 Music 应用状态。使用 ApplicationMusicPlayer 进行应用范围内的播放。
// 错误:修改用户的 Music 应用队列
let player = SystemMusicPlayer.shared
// 正确:应用范围内的播放
let player = ApplicationMusicPlayer.shared
锁屏界面上过时的元数据会使用户困惑。每次当前曲目更改时都应更新正在播放信息。
// 错误:设置一次后就忘记更新
updateNowPlaying(title: firstSong.title, ...)
// 正确:每次曲目更改时都更新
func onTrackChanged(_ song: Song) {
updateNowPlaying(
title: song.title,
artist: song.artistName,
duration: song.duration ?? 0,
elapsed: 0
)
}
注册命令但返回 .commandFailed 会破坏锁屏控制。应该禁用不支持的命令。
// 错误
center.skipForwardCommand.addTarget { _ in .commandFailed }
// 正确
center.skipForwardCommand.isEnabled = false
NSAppleMusicUsageDescriptionMusicAuthorization.request()ApplicationMusicPlayer(而非 SystemMusicPlayer)进行应用范围内的播放.successisEnabled = false 禁用了不支持的远程命令references/musickit-patterns.md每周安装量
350
代码仓库
GitHub 星标数
276
首次出现
2026年3月8日
安全审计
安装于
codex347
kimi-cli344
github-copilot344
amp344
cline344
opencode344
Search the Apple Music catalog, manage playback with ApplicationMusicPlayer, check subscriptions, and publish Now Playing metadata via MPNowPlayingInfoCenter and MPRemoteCommandCenter. Targets Swift 6.2 / iOS 26+.
com.apple.developer.musickit entitlement)NSAppleMusicUsageDescription to Info.plist explaining why the app accesses Apple Musicaudio background mode to UIBackgroundModesimport MusicKit // Catalog, auth, playback
import MediaPlayer // MPRemoteCommandCenter, MPNowPlayingInfoCenter
Request permission before accessing the user's music data or playing Apple Music content. Authorization is a one-time prompt per app install.
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
Use MusicCatalogSearchRequest to search the Apple Music catalog. The user must have an Apple Music subscription for full catalog access.
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
}
}
Check whether the user has an active Apple Music subscription before offering playback features.
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
}
}
}
Present the Apple Music subscription offer sheet when the user is not subscribed.
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 plays Apple Music content independently from the Music app. It does not affect the system player's state.
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
}
}
Build and manipulate the playback queue using 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)
}
Update MPNowPlayingInfoCenter so the Lock Screen, Control Center, and CarPlay display current track metadata. This is essential when playing custom audio (non-MusicKit sources). ApplicationMusicPlayer handles this automatically for Apple Music content.
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
}
Register handlers for MPRemoteCommandCenter to respond to Lock Screen controls, AirPods tap gestures, and CarPlay buttons.
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
}
}
Without the MusicKit entitlement your app crashes at authorization. Without NSAppleMusicUsageDescription, App Review rejects the submission.
// 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()
Attempting to play catalog content without a subscription silently fails or throws.
// 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()
}
SystemMusicPlayer controls the global Music app queue. Changes affect the user's Music app state. Use ApplicationMusicPlayer for app-scoped playback.
// WRONG: Modifies the user's Music app queue
let player = SystemMusicPlayer.shared
// CORRECT: App-scoped playback
let player = ApplicationMusicPlayer.shared
Stale metadata on the Lock Screen confuses users. Update Now Playing info every time the current track changes.
// 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
)
}
Registering a command but returning .commandFailed breaks Lock Screen controls. Disable commands you do not support instead.
// WRONG
center.skipForwardCommand.addTarget { _ in .commandFailed }
// CORRECT
center.skipForwardCommand.isEnabled = false
NSAppleMusicUsageDescription added to Info.plistMusicAuthorization.request() called before any MusicKit accessApplicationMusicPlayer used (not SystemMusicPlayer) for app-scoped playback.success for supported commandsisEnabled = falsereferences/musickit-patterns.mdWeekly Installs
350
Repository
GitHub Stars
276
First Seen
Mar 8, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex347
kimi-cli344
github-copilot344
amp344
cline344
opencode344
AI播客创作工具 - 使用inference.sh CLI快速生成AI驱动播客和音频内容
7,400 周安装