Meet MusicKit for Swift
Media & Web 入门 14m

认识 Swift 版 MusicKit

Meet MusicKit for Swift

2021年6月10日

在 Apple 官方观看视频

一句话判断

终于不用手写 JWT 和 URL Session 来调 Apple Music API 了——MusicKit for Swift 把认证、请求、响应解析全部变成了 Swift 原生类型安全的一行调用。

这场 Session 讲了什么

iOS 15 引入了全新的 MusicKit Swift 框架,用来替代之前基于 Web API 的调用方式。以前要用 Apple Music API,你需要自己生成 Developer Token(JWT)、管理 Music User Token、构建 HTTP 请求、解析 JSON 响应。MusicKit for Swift 把这些全部封装成了 Swift 原生 API:MusicCatalogSearchRequestMusicLibraryRequestMusicCatalogResourceRequest 等,配合 async/await 使用起来非常流畅。

Session 展示了一个完整的 App 示例:搜索歌曲、播放预览、获取推荐歌单、以及操作用户资料库。整个过程不需要写一行网络代码。框架内部自动处理认证和 token 刷新。

类型系统是这个框架的亮点。SongAlbumArtistPlaylistMusicVideo 等都是强类型的 Swift 结构体,属性都有明确的类型标注。编译器会帮你捕获「把 Album ID 传给 Song API」这种错误,而以前用 REST API 时这类错误只能在运行时发现。

值得深挖的点

MusicItemCollection 的延迟加载设计。 MusicKit 的 API 返回的不是普通数组,而是 MusicItemCollection< Song>。这个集合类型支持分页——当结果超过一页时,你可以通过 nextBatch() 方法加载下一页,而不需要手动计算 offset。这个设计让分页代码从「管理 offset/limit 变量 + 处理网络请求」简化成了「判断 hasNextBatch + 调用 nextBatch()」。对于大型资料库(用户有几万首歌),这种按需加载的模式避免了内存和性能问题。

播放能力与框架的边界。 MusicKit 本身不负责播放——播放仍然由 MPMusicPlayerController 处理。但 MusicKit 获取的歌曲和专辑对象可以直接传给 MPMusicPlayerController,因为它兼容 MPMusicPlayerQueueDescriptor。你的代码流程是:用 MusicKit 搜索/获取内容,然后用 MediaPlayer 框架播放。两个框架配合使用,MusicKit 负责「数据」,MediaPlayer 负责「播放」。

代码片段

import MusicKit

// 搜索歌曲——类型安全,不需要手写 URL
func searchSongs(query: String) async throws -> [Song] {
    var request = MusicCatalogSearchRequest(
        term: query,
        types: [Song.self]
    )
    request.limit = 25

    let response = try await request.response()
    return response.songs.items
}

// 调用示例
let songs = try await searchSongs(query: "Blinding Lights")
for song in songs {
    print("\(song.title) by \(song.artistName)")
    if let artwork = song.artwork {
        // 获取专辑封面 URL,可以指定尺寸
        let url = artwork.url(width: 200, height: 200)
    }
}
// 获取用户资料库中的歌曲
func fetchLibrarySongs() async throws -> MusicItemCollection<Song> {
    let request = MusicLibraryRequest<Song>()
    let response = try await request.response()
    return response.items
}

// 处理分页
func loadAllLibrarySongs() async throws -> [Song] {
    var allSongs: [Song] = []
    var collection = try await fetchLibrarySongs()

    while true {
        allSongs.append(contentsOf: collection)
        if let nextBatch = try await collection.nextBatch() {
            collection = nextBatch
        } else {
            break
        }
    }
    return allSongs
}
// 播放搜索到的歌曲
import MediaPlayer

func playSong(_ song: Song) {
    let player = MPMusicPlayerController.applicationQueuePlayer

    // 用 MusicKit 获取的 Song 对象创建播放队列
    player.setQueue(with: [song.id.rawValue])
    player.play()
}

// 将歌曲添加到用户资料库
func addToLibrary(_ song: Song) async throws {
    try await song.with(.artwork)  // 确保加载了完整属性
    let request = MusicAddToLibraryRequest(songs: [song])
    try await request.response()
}

最佳实践

  • 在 App 启动时就检查 MusicAuthorization.currentStatus,如果未授权就尽早发起授权请求,不要等到用户点击播放才弹出权限对话框
  • 搜索时用 MusicCatalogSearchSuggestionsRequest 先展示建议,比直接搜索的用户体验更好
  • 对于频繁访问的数据(比如热门排行榜),用本地缓存减少 API 调用次数
  • 处理 MusicKitError 的各种情况——用户没有 Apple Music 订阅、网络断开、token 过期等,每种情况给用户不同的提示

还有什么值得关注

  • MusicKit 也支持获取电台(Apple Music Radio)的内容列表
  • MusicCatalogChartRequest 可以获取各个类型的排行榜数据
  • watchOS 上也可以使用 MusicKit,适合做手表端的音乐控制 App
WWDC 2021