关于 musickit
MusicKit 是面向 Apple 平台开发者的 Swift 技能指南,用于搜索 Apple Music 曲库、管理应用内播放、检查订阅状态并设置播放信息。它涵盖授权、播放队列以及锁定屏幕和远程控制。
主要功能
- 01搜索 Apple Music 曲库
- 02检查音乐授权与订阅
- 03管理应用内播放和队列
- 04发布正在播放的曲目信息
- 05处理远程播放控制
适用场景
- 01在应用中搜索歌曲和专辑
- 02按订阅状态决定是否播放曲库内容
- 03构建并更新播放队列
- 04让锁定屏幕显示曲目信息并响应控制
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill musickit安装后在 Claude Code 中输入/musickit确认已加载。
附带文件evals/evals.jsonreferences/musickit-patterns.md
详细介绍
MusicKit
搜索 Apple Music 目录,使用 ApplicationMusicPlayer 管理播放,检查订阅状态,并通过 MPNowPlayingInfoCenter
和 MPRemoteCommandCenter 发布“正在播放”元数据。
目录
工作流
- 调试代码前,先验证 MusicKit App Service、Bundle ID、用途说明字符串和后台音频模式。
- 请求授权,然后明确处理所有非授权状态。
- 搜索或加载目录内容,并在将内容加入播放队列前检查
MusicSubscription.current。 - 对于应用范围内的播放,选择
ApplicationMusicPlayer;仅当应用负责相应界面时,才接入“正在播放”信息和远程命令。 - 测试已授权、已拒绝、未订阅、离线、队列失败、中断和曲目变更等状态。修复最小范围内的故障层,恢复测试样例,并重新运行相同的状态矩阵。
设置
项目配置
- 在 Apple Developer 门户中为应用的显式 Bundle ID 启用 MusicKit App Service,以便 MusicKit 自动生成开发者令牌。
- 在 Info.plist 中添加
NSAppleMusicUsageDescription,说明应用访问用户媒体资料库的原因。 - 如需后台播放,请将
audio后台模式添加到UIBackgroundModes。
导入
import MusicKit // Catalog, auth, playback
import MediaPlayer // MPRemoteCommandCenter, MPNowPlayingInfoCenter
授权
访问用户的音乐数据或播放 Apple Music 内容前,请先请求权限。必要时,request() 会显示 Apple 的授权对话框;使用 currentStatus 可在不触发提示的情况下读取当前设置。
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 资源,但播放订阅目录内容仍必须先通过 MusicSubscription.current.canPlayCatalogContent 检查。
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 订阅优惠页面。先检查 canBecomeSubscriber;如果页面需要上下文元数据或加载错误处理,则传入 MusicSubscriptionOffer.Options 或 onLoadCompletion。
import MusicKit
import SwiftUI
struct MusicOfferView: View {
@State private var showOffer = false
var body: some View {
Button("Subscribe to Apple Music") {
Task {
let subscription = try? await MusicSubscription.current
showOffer = subscription?.canBecomeSubscriber == true
}
}
.musicSubscriptionOffer(
isPresented: $showOffer,
options: .default,
onLoadCompletion: { error in
if let error {
// Surface loading errors in app UI or diagnostics.
print(error)
}
}
)
}
}
使用 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
}
}
常见错误
| 错误 | 修复方法 |
|---|---|
| 尚未配置 App Service 和用途说明字符串就开始调试授权 | 首先验证服务、Bundle ID 和 NSAppleMusicUsageDescription。 |
| 未检查订阅状态就将目录内容加入队列 | 检查 canPlayCatalogContent;仅在 canBecomeSubscriber 为 true 时提供订阅。 |
使用 SystemMusicPlayer 播放应用自有内容 | 使用 ApplicationMusicPlayer;系统播放器会更改 Music App 的全局队列。 |
| 仅发布一次“正在播放”元数据 | 在曲目、时长、速率和已播放时间发生变化时刷新。 |
| 注册不受支持的远程命令 | 将其禁用;受支持的处理程序必须执行相应操作并返回 .success。 |
检查清单
- 已为应用的显式 Bundle ID 启用 MusicKit App Service
- 已将
NSAppleMusicUsageDescription添加到 Info.plist - 在访问任何 MusicKit 内容之前已调用
MusicAuthorization.request() - 尝试播放目录内容之前已检查订阅状态
- 显示订阅优惠之前已检查
canBecomeSubscriber - 写入资料库之前已检查
hasCloudLibraryEnabled - 应用范围的播放使用了
ApplicationMusicPlayer(而非SystemMusicPlayer) - 如果音乐需要在后台播放,已启用后台音频模式
- 自定义音频的每次曲目变更都会更新“正在播放”信息
- 受支持的命令对应的远程命令处理程序会返回
.success - 不受支持的远程命令已通过
isEnabled = false禁用 - “正在播放”信息中已提供封面图,以便在锁定屏幕上显示
- 已定期更新已播放时间,确保进度条准确
- 用户没有 Apple Music 订阅时会显示订阅优惠
参考资料
- 扩展模式(SwiftUI 集成、流派浏览、播放列表管理):references/musickit-patterns.md
- MusicKit 框架
- 使用自动开发者令牌生成功能访问 Apple Music API
- MusicAuthorization
- ApplicationMusicPlayer
- MusicCatalogSearchRequest
- MusicSubscription
- canPlayCatalogContent
- canBecomeSubscriber
- hasCloudLibraryEnabled
- MusicCatalogChartsRequest 初始化器
- musicSubscriptionOffer(isPresented:options:onLoadCompletion:)
- MPRemoteCommandCenter
- MPNowPlayingInfoCenter
- NSAppleMusicUsageDescription