musickit-audio
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMusicKit + MediaPlayer
MusicKit + MediaPlayer
Search the Apple Music catalog, manage playback with ,
check subscriptions, and publish Now Playing metadata via
and . Targets Swift 6.2 / iOS 26+.
ApplicationMusicPlayerMPNowPlayingInfoCenterMPRemoteCommandCenter搜索Apple Music曲库,通过管理播放,检查订阅状态,通过和发布当前播放元数据。适用于Swift 6.2 / iOS 26+版本。
ApplicationMusicPlayerMPNowPlayingInfoCenterMPRemoteCommandCenterContents
目录
Setup
配置
Project Configuration
项目配置
- Enable the MusicKit capability in Xcode (adds the entitlement)
com.apple.developer.musickit - Add to Info.plist explaining why the app accesses Apple Music
NSAppleMusicUsageDescription - For background playback, add the background mode to
audioUIBackgroundModes
- 在Xcode中开启MusicKit能力(会添加权限声明)
com.apple.developer.musickit - 在Info.plist中添加字段,说明应用访问Apple Music的原因
NSAppleMusicUsageDescription - 如果需要后台播放,在中添加
UIBackgroundModes后台模式audio
Imports
导入依赖
swift
import MusicKit // Catalog, auth, playback
import MediaPlayer // MPRemoteCommandCenter, MPNowPlayingInfoCenterswift
import MusicKit // 曲库、授权、播放相关能力
import MediaPlayer // MPRemoteCommandCenter、MPNowPlayingInfoCenter相关能力Authorization
授权
Request permission before accessing the user's music data or playing Apple Music content. Authorization is a one-time prompt per app install.
swift
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在访问用户音乐数据或播放Apple Music内容前需要先请求权限,每个应用安装后仅会弹出一次授权提示。
swift
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.currentStatusCatalog Search
曲库搜索
Use to search the Apple Music catalog. The user must have an Apple Music subscription for full catalog access.
MusicCatalogSearchRequestswift
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
}使用搜索Apple Music曲库,用户需要有有效的Apple Music订阅才能访问完整曲库。
MusicCatalogSearchRequestswift
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
}Displaying Results
展示搜索结果
swift
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
}
}swift
for song in songs {
print("\(song.title) by \(song.artistName)")
if let artwork = song.artwork {
let url = artwork.url(width: 300, height: 300)
// 从url加载封面图
}
}Subscription Checks
订阅检查
Check whether the user has an active Apple Music subscription before offering playback features.
swift
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订阅。
swift
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 {
// 展示订阅引导
}
}
}Offering Apple Music
提供Apple Music订阅入口
Present the Apple Music subscription offer sheet when the user is not subscribed.
swift
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)
}
}当用户未订阅时展示Apple Music订阅引导页。
swift
import MusicKit
import SwiftUI
struct MusicOfferView: View {
@State private var showOffer = false
var body: some View {
Button("订阅Apple Music") {
showOffer = true
}
.musicSubscriptionOffer(isPresented: $showOffer)
}
}Playback with ApplicationMusicPlayer
使用ApplicationMusicPlayer播放
ApplicationMusicPlayerswift
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()
}ApplicationMusicPlayerswift
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()
}Observing Playback State
监听播放状态
swift
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
}
}swift
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
}
}Queue Management
队列管理
Build and manipulate the playback queue using .
ApplicationMusicPlayer.Queueswift
// 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)
}使用构建和管理播放队列。
ApplicationMusicPlayer.Queueswift
// 用多个内容项初始化队列
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)
}Now Playing Info
当前播放信息
Update so the Lock Screen, Control Center, and CarPlay
display current track metadata. This is essential when playing custom audio
(non-MusicKit sources). handles this automatically for
Apple Music content.
MPNowPlayingInfoCenterApplicationMusicPlayerswift
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
}更新可以让锁屏、控制中心和CarPlay显示当前播放的曲目元数据,播放自定义音频(非MusicKit来源)时必须手动更新,播放Apple Music内容时会自动处理。
MPNowPlayingInfoCenterApplicationMusicPlayerswift
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
}Adding Artwork
添加封面图
swift
func setArtwork(_ image: UIImage) {
let artwork = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
var info = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [:]
info[MPMediaItemPropertyArtwork] = artwork
MPNowPlayingInfoCenter.default().nowPlayingInfo = info
}swift
func setArtwork(_ image: UIImage) {
let artwork = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
var info = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [:]
info[MPMediaItemPropertyArtwork] = artwork
MPNowPlayingInfoCenter.default().nowPlayingInfo = info
}Remote Command Center
远程指令中心
Register handlers for to respond to Lock Screen controls,
AirPods tap gestures, and CarPlay buttons.
MPRemoteCommandCenterswift
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
}为注册处理方法可以响应锁屏控制、AirPods手势、CarPlay按钮的操作。
MPRemoteCommandCenterswift
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
}Scrubbing Support
支持进度拖拽
swift
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
}
}swift
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
}
}Common Mistakes
常见错误
DON'T: Skip the MusicKit entitlement or usage description
不要:遗漏MusicKit权限声明或使用说明
Without the MusicKit entitlement your app crashes at authorization. Without
, App Review rejects the submission.
NSAppleMusicUsageDescriptionswift
// 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()没有MusicKit权限的话应用在授权时会崩溃,没有的话应用会被应用审核拒绝。
NSAppleMusicUsageDescriptionswift
// 错误:没有配置权限
let status = await MusicAuthorization.request() // 会崩溃
// 正确:先在Xcode中开启MusicKit能力,再在Info.plist中添加NSAppleMusicUsageDescription
let status = await MusicAuthorization.request()DON'T: Forget to check subscription before playback
不要:播放前忘记检查订阅状态
Attempting to play catalog content without a subscription silently fails or throws.
swift
// 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()
}没有订阅的情况下尝试播放曲库内容会静默失败或者抛出异常。
swift
// 错误
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()
}DON'T: Use SystemMusicPlayer when you mean ApplicationMusicPlayer
不要:混淆SystemMusicPlayer和ApplicationMusicPlayer
SystemMusicPlayerApplicationMusicPlayerswift
// WRONG: Modifies the user's Music app queue
let player = SystemMusicPlayer.shared
// CORRECT: App-scoped playback
let player = ApplicationMusicPlayer.sharedSystemMusicPlayerApplicationMusicPlayerswift
// 错误:会修改用户的系统音乐应用队列
let player = SystemMusicPlayer.shared
// 正确:应用内独立播放
let player = ApplicationMusicPlayer.sharedDON'T: Forget to update Now Playing info when track changes
不要:曲目切换时忘记更新当前播放信息
Stale metadata on the Lock Screen confuses users. Update Now Playing info
every time the current track changes.
swift
// 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
)
}锁屏上的过时元数据会让用户困惑,每次切换曲目时都要更新当前播放信息。
swift
// 错误:只设置一次就不管了
updateNowPlaying(title: firstSong.title, ...)
// 正确:每次曲目切换都更新
func onTrackChanged(_ song: Song) {
updateNowPlaying(
title: song.title,
artist: song.artistName,
duration: song.duration ?? 0,
elapsed: 0
)
}DON'T: Register remote commands without handling them
不要:注册了远程指令却不处理
Registering a command but returning breaks Lock Screen controls.
Disable commands you do not support instead.
.commandFailedswift
// WRONG
center.skipForwardCommand.addTarget { _ in .commandFailed }
// CORRECT
center.skipForwardCommand.isEnabled = false注册了指令但返回会导致锁屏控制失效,不需要支持的指令直接禁用即可。
.commandFailedswift
// 错误
center.skipForwardCommand.addTarget { _ in .commandFailed }
// 正确
center.skipForwardCommand.isEnabled = falseReview Checklist
审核检查清单
- MusicKit capability enabled in Xcode project
- added to Info.plist
NSAppleMusicUsageDescription - called before any MusicKit access
MusicAuthorization.request() - Subscription checked before attempting catalog playback
- used (not
ApplicationMusicPlayer) for app-scoped playbackSystemMusicPlayer - Background audio mode enabled if music plays in background
- Now Playing info updated on every track change (for custom audio)
- Remote command handlers return for supported commands
.success - Unsupported remote commands disabled with
isEnabled = false - Artwork provided in Now Playing info for Lock Screen display
- Elapsed playback time updated periodically for scrubber accuracy
- Subscription offer presented when user lacks Apple Music subscription
- Xcode项目中已开启MusicKit能力
- Info.plist中已添加
NSAppleMusicUsageDescription - 访问任何MusicKit能力前已调用
MusicAuthorization.request() - 尝试播放曲库内容前已检查订阅状态
- 应用内播放使用的是而非
ApplicationMusicPlayerSystemMusicPlayer - 如果需要后台播放已开启后台音频模式
- 自定义音频播放时每次切换曲目都更新了当前播放信息
- 支持的远程指令处理方法返回
.success - 不支持的远程指令已通过禁用
isEnabled = false - 当前播放信息中包含封面图用于锁屏展示
- 定期更新已播放时长保证进度条准确性
- 用户没有Apple Music订阅时会展示订阅引导
References
参考资料
- Extended patterns (SwiftUI integration, genre browsing, playlist management):
references/musickit-patterns.md - MusicKit framework
- MusicAuthorization
- ApplicationMusicPlayer
- MusicCatalogSearchRequest
- MusicSubscription
- MPRemoteCommandCenter
- MPNowPlayingInfoCenter
- NSAppleMusicUsageDescription
- 扩展实现模式(SwiftUI集成、曲风浏览、播放列表管理):
references/musickit-patterns.md - MusicKit框架
- MusicAuthorization
- ApplicationMusicPlayer
- MusicCatalogSearchRequest
- MusicSubscription
- MPRemoteCommandCenter
- MPNowPlayingInfoCenter
- NSAppleMusicUsageDescription