mcpField
M
musickit-audio

musickit-audio

github.com/dpearson2699开发1.2k409 次安装13 周前更新
代码仓库

关于 musickit-audio

面向 Apple 平台应用开发者的 MusicKit 与 MediaPlayer 技能,涵盖 Apple Music 搜索、订阅检查和应用内播放,也介绍 Now Playing 信息及远程控制。

主要功能

  1. 01搜索 Apple Music 曲库
  2. 02检查订阅状态
  3. 03使用 ApplicationMusicPlayer 播放
  4. 04管理播放队列
  5. 05更新 Now Playing 元数据
  6. 06处理远程播放命令

适用场景

  1. 01在应用中搜索 Apple Music 歌曲
  2. 02按订阅状态控制曲库播放
  3. 03为自定义音频显示锁屏信息
  4. 04响应耳机或 CarPlay 播放控制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill musickit-audio

安装后在 Claude Code 中输入/musickit-audio确认已加载。

附带文件references/musickit-patterns.md

详细介绍

MusicKit + MediaPlayer

搜索 Apple Music 目录,使用 ApplicationMusicPlayer 管理播放,检查订阅状态,并通过 MPNowPlayingInfoCenter 和 MPRemoteCommandCenter 发布“正在播放”元数据。面向 Swift 6.2 / iOS 26+。

目录

设置

项目配置
  1. 在 Xcode 中启用 MusicKit 功能(会添加 com.apple.developer.musickit 权限)
  2. 将 NSAppleMusicUsageDescription 添加到 Info.plist,说明应用访问 Apple Music 的原因
  3. 如需后台播放,请将 audio 后台模式添加到 UIBackgroundModes
导入
import MusicKit       // Catalog, auth, playback
import MediaPlayer    // MPRemoteCommandCenter, MPNowPlayingInfoCenter

授权

在访问用户的音乐数据或播放 Apple Music 内容之前,请先请求权限。每次安装应用只会显示一次授权提示。

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,才能完整访问目录。

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 订阅优惠页。

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 播放

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
    }
}

常见错误

不要:跳过 MusicKit 权限或使用说明

没有 MusicKit 权限,应用会在授权时崩溃。没有 NSAppleMusicUsageDescription,App Review 会拒绝提交。

// 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()
不要:忘记在播放前检查订阅

没有订阅却尝试播放目录内容,会静默失败或抛出错误。

// 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()
}
不要:需要 ApplicationMusicPlayer 时却使用 SystemMusicPlayer

SystemMusicPlayer 控制全局“音乐”应用队列。更改会影响用户“音乐”应用的状态。请使用 ApplicationMusicPlayer 实现应用范围内的播放。

// WRONG: Modifies the user's Music app queue
let player = SystemMusicPlayer.shared

// CORRECT: App-scoped playback
let player = ApplicationMusicPlayer.shared
不要:曲目更改时忘记更新“正在播放”信息

锁定屏幕上的过时元数据会让用户困惑。当前曲目每次更改时都要更新“正在播放”信息。

// 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
    )
}
不要:注册远程命令却不处理

注册命令却返回 .commandFailed 会导致锁定屏幕控件无法正常工作。应禁用不支持的命令。

// WRONG
center.skipForwardCommand.addTarget { _ in .commandFailed }

// CORRECT
center.skipForwardCommand.isEnabled = false

审查清单

  • 已在 Xcode 项目中启用 MusicKit 功能
  • 已将 NSAppleMusicUsageDescription 添加到 Info.plist
  • 在访问 MusicKit 前调用了 MusicAuthorization.request()
  • 尝试播放目录内容前检查了订阅状态
  • 使用 ApplicationMusicPlayer(而非 SystemMusicPlayer)进行应用范围内的播放
  • 如果音乐会在后台播放,已启用后台音频模式
  • 每次曲目切换时都更新了「正在播放」信息(用于自定义音频)
  • 远程命令处理程序对支持的命令返回 .success
  • 使用 isEnabled = false 禁用不支持的远程命令
  • 在「正在播放」信息中提供了封面图,以便在锁定屏幕上显示
  • 定期更新已播放时长,确保拖动条准确
  • 当用户没有 Apple Music 订阅时,展示订阅优惠

参考资料

评论

支持 Markdown