musickit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMusicKit
MusicKit
Search the Apple Music catalog, manage playback with ,
check subscriptions, and publish Now Playing metadata via
and . Targets Swift 6.3 / iOS 26+.
ApplicationMusicPlayerMPNowPlayingInfoCenterMPRemoteCommandCenter搜索Apple Music目录,使用管理播放、检查订阅状态,并通过和发布正在播放的元数据。适配Swift 6.3 / 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 // Catalog, auth, playback
import MediaPlayer // MPRemoteCommandCenter, MPNowPlayingInfoCenterAuthorization
授权
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:
// 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.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)
// Load artwork from 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
}
// Observe subscription changes
func observeSubscription() async {
for await subscription in MusicSubscription.subscriptionUpdates {
if subscription.canPlayCatalogContent {
// Enable full playback UI
} else {
// Show subscription offer
}
}
}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("Subscribe to 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 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
}
}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
// 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)
}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
}
// Disable commands you do not support
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
// 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()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
// 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()
}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
// WRONG: Modifies the user's Music app queue
let player = SystemMusicPlayer.shared
// CORRECT: App-scoped playback
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
// 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
)
}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
// WRONG
center.skipForwardCommand.addTarget { _ in .commandFailed }
// CORRECT
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 framework
- MusicAuthorization
- ApplicationMusicPlayer
- MusicCatalogSearchRequest
- MusicSubscription
- MPRemoteCommandCenter
- MPNowPlayingInfoCenter
- NSAppleMusicUsageDescription