musickit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MusicKit

MusicKit

Search the Apple Music catalog, manage playback with
ApplicationMusicPlayer
, check subscriptions, and publish Now Playing metadata via
MPNowPlayingInfoCenter
and
MPRemoteCommandCenter
. Targets Swift 6.3 / iOS 26+.
搜索Apple Music目录,使用
ApplicationMusicPlayer
管理播放、检查订阅状态,并通过
MPNowPlayingInfoCenter
MPRemoteCommandCenter
发布正在播放的元数据。适配Swift 6.3 / iOS 26+。

Contents

目录

Setup

配置

Project Configuration

项目配置

  1. Enable the MusicKit capability in Xcode (adds the
    com.apple.developer.musickit
    entitlement)
  2. Add
    NSAppleMusicUsageDescription
    to Info.plist explaining why the app accesses Apple Music
  3. For background playback, add the
    audio
    background mode to
    UIBackgroundModes
  1. 在Xcode中开启MusicKit能力(会添加
    com.apple.developer.musickit
    权限)
  2. 在Info.plist中添加
    NSAppleMusicUsageDescription
    字段,说明应用访问Apple Music的原因
  3. 如需后台播放,请在
    UIBackgroundModes
    中添加
    audio
    后台模式

Imports

导入依赖

swift
import MusicKit       // Catalog, auth, playback
import MediaPlayer    // MPRemoteCommandCenter, MPNowPlayingInfoCenter
swift
import MusicKit       // Catalog, auth, playback
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:
        // 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

Catalog Search

目录搜索

Use
MusicCatalogSearchRequest
to search the Apple Music catalog. The user must have an Apple Music subscription for full catalog access.
swift
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
}
使用
MusicCatalogSearchRequest
搜索Apple Music目录,用户需要持有有效的Apple Music订阅才能访问完整目录内容。
swift
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播放

ApplicationMusicPlayer
plays Apple Music content independently from the Music app. It does not affect the system player's state.
swift
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()
}
ApplicationMusicPlayer
独立于系统音乐 app 播放Apple Music内容,不会影响系统播放器的状态。
swift
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.Queue
.
swift
// 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.Queue
创建和管理播放队列。
swift
// 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
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.
swift
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
}
更新
MPNowPlayingInfoCenter
可以让锁屏、控制中心和CarPlay显示当前播放曲目元数据,这对播放自定义音频(非MusicKit来源)非常重要。
ApplicationMusicPlayer
会自动为Apple Music内容更新该信息。
swift
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
MPRemoteCommandCenter
to respond to Lock Screen controls, AirPods tap gestures, and CarPlay buttons.
swift
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
}
MPRemoteCommandCenter
注册处理方法,以响应锁屏控制、AirPods手势和CarPlay按钮操作。
swift
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
NSAppleMusicUsageDescription
, App Review rejects the submission.
swift
// 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权限的话,应用在请求授权时会崩溃;没有配置
NSAppleMusicUsageDescription
的话,应用会被应用审核驳回。
swift
// 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

SystemMusicPlayer
controls the global Music app queue. Changes affect the user's Music app state. Use
ApplicationMusicPlayer
for app-scoped playback.
swift
// WRONG: Modifies the user's Music app queue
let player = SystemMusicPlayer.shared

// CORRECT: App-scoped playback
let player = ApplicationMusicPlayer.shared
SystemMusicPlayer
会控制全局的系统音乐app队列,修改会影响用户的音乐app状态,应用内播放请使用
ApplicationMusicPlayer
swift
// WRONG: Modifies the user's Music app queue
let player = SystemMusicPlayer.shared

// CORRECT: App-scoped playback
let player = ApplicationMusicPlayer.shared

DON'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
.commandFailed
breaks Lock Screen controls. Disable commands you do not support instead.
swift
// WRONG
center.skipForwardCommand.addTarget { _ in .commandFailed }

// CORRECT
center.skipForwardCommand.isEnabled = false
注册了命令但返回
.commandFailed
会导致锁屏控制失效,不支持的命令请直接禁用。
swift
// WRONG
center.skipForwardCommand.addTarget { _ in .commandFailed }

// CORRECT
center.skipForwardCommand.isEnabled = false

Review Checklist

审核检查清单

  • MusicKit capability enabled in Xcode project
  • NSAppleMusicUsageDescription
    added to Info.plist
  • MusicAuthorization.request()
    called before any MusicKit access
  • Subscription checked before attempting catalog playback
  • ApplicationMusicPlayer
    used (not
    SystemMusicPlayer
    ) for app-scoped playback
  • Background audio mode enabled if music plays in background
  • Now Playing info updated on every track change (for custom audio)
  • Remote command handlers return
    .success
    for supported commands
  • 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()
  • 播放目录内容前已检查订阅状态
  • 应用内播放使用的是
    ApplicationMusicPlayer
    而非
    SystemMusicPlayer
  • 如需后台播放,已开启后台音频模式
  • 自定义音频播放时,每次曲目切换都会更新正在播放信息
  • 支持的远程命令处理会返回
    .success
  • 不支持的远程命令已通过
    isEnabled = false
    禁用
  • 正在播放信息中包含封面,用于锁屏展示
  • 已定期更新播放进度,保证进度条准确性
  • 用户没有Apple Music订阅时会弹出订阅引导

References

参考资料