Highlight
MusicKit 为 SwiftUI 提供了
.musicPicker()和.musicSubscriptionOffer()两个原生修饰器,让非音乐类 App 也能在几行代码内完成授权、选歌、播放控制和订阅引导的完整闭环。
核心内容
以前接 Apple Music 有多麻烦
你想在健身 App 里加一段背景音乐,流程是这样的:先写授权逻辑请求用户许可,再判断用户是否订阅了 Apple Music,没订阅的话得自己设计一套引导订阅的 UI,订阅了还要手搓选歌界面、处理播放队列、监听播放状态。一套下来,为了加个 BGM 功能,可能要写上千行代码。
Apple 做了什么改变
WWDC26 的 MusicKit 把上述流程打包成了两个 SwiftUI 修饰器和一个原生选歌器。授权用 MusicAuthorization.request(),订阅引导用 .musicSubscriptionOffer(),选歌用 .musicPicker(),播放控制直接操作 ApplicationMusicPlayer.shared。整个接入流程从”重写半个 App”变成了”加几行修饰器”。
新能力如何解决痛点
以 Session 中演示的健身 App 为例:用户开始骑行训练时,点击”选歌”按钮弹出 .musicPicker(),从 Apple Music 曲库或个人资料库中挑选歌曲;如果用户没订阅,界面上自动出现订阅按钮,点击后弹出 .musicSubscriptionOffer(),用户无需离开 App 就能完成订阅;选好的歌曲通过 ApplicationMusicPlayer 播放,App 内直接显示专辑封面、歌曲信息和播放控制按钮。整个体验是原生的、无缝的。
详细内容
授权与项目配置
(01:03)接入 MusicKit 的第一步是在 Xcode 中启用 MusicKit 服务。在 App ID 注册页面的 App Services 标签页勾选 MusicKit,Xcode 会自动生成开发者令牌(Developer Token),无需手动管理。
(03:12)请求用户授权使用 MusicAuthorization.request(),这是一个异步方法,返回授权状态。为了让权限弹窗显示更清晰的说明,需要在 Xcode 的 Signing & Capabilities 中添加 Media Library 能力,并在描述文本框中填写 App 使用音乐库的目的。
订阅引导
(04:46)对于未订阅 Apple Music 的用户,可以用 .musicSubscriptionOffer() 修饰器在 App 内直接展示订阅引导界面,用户无需跳转到 Music App 或 App Store。
@State var showSubscriptionOffer = false
let options = MusicSubscriptionOffer.Options(
messageIdentifier: .playMusic
)
@ViewBuilder
var musicSubscriptionButton: some View {
Button("Subscribe to Apple Music", systemImage: "music.note") {
showSubscriptionOffer = true
}
.musicSubscriptionOffer(isPresented: $showSubscriptionOffer, options: options)
}
关键点:
MusicSubscriptionOffer.Options的messageIdentifier决定弹窗展示的 UI 文案和样式,.playMusic适合播放场景- 开发者可以通过 Apple Services Performance Partner Program 获得订阅佣金
- 弹窗通过
isPresented绑定控制显示与隐藏
(05:59)在主视图中监听订阅状态,只在用户有资格订阅时显示按钮:
@State var subscription: MusicSubscription?
var body: some View {
VStack {
if let subscription, subscription.canBecomeSubscriber {
musicSubscriptionButton
}
}
.task(id: isAuthorized) {
self.subscription = try? await MusicSubscription.current
for await subscription in MusicSubscription.subscriptionUpdates {
self.subscription = subscription
}
}
}
关键点:
MusicSubscription.current获取当前订阅状态MusicSubscription.subscriptionUpdates是 AsyncSequence,订阅状态变化时自动推送新值canBecomeSubscriber判断用户是否有资格订阅(未订阅且所在地区支持)
音乐选择器
(08:48).musicPicker() 是一个 SwiftUI 视图修饰器,呼出原生界面让用户浏览 Apple Music 曲库和个人资料库。支持单选和多选,也支持选择专辑或播放列表。
@State var showMusicPicker = false
@State var selectedSong: Song? = nil
@ViewBuilder
var musicPickerButton: some View {
Button("Pick some Music", systemImage: "music.note.list") {
showMusicPicker = true
}
.musicPicker(isPresented: $showMusicPicker, selection: $selectedSong)
}
关键点:
selection绑定到Song?是单选,绑定到[Song]是多选- 不需要 Apple Music 订阅也能使用,但未订阅用户只能看到个人资料库中的内容
- 支持搜索、浏览专辑详情页、选择整个播放列表
两种播放器的选择
(10:54)MusicKit 提供两种播放器:
| 特性 | SystemMusicPlayer | ApplicationMusicPlayer |
|---|---|---|
| 控制对象 | 系统 Music App | App 自身 |
| 后台播放 | 自动支持 | 需开启 Audio Background Mode |
| 队列访问 | 只读当前项 | 完全读写 |
| 适用场景 | 独立音乐客户端 | 健身、冥想等需要 BGM 的 App |
ApplicationMusicPlayer 的队列支持延迟加载(Lazy loading)。如果用专辑或播放列表初始化队列,播放器会按需加载其中的歌曲,而不是一次性全部加载,这能显著减少启动延迟。
(13:02)prepareToPlay() 方法可以在用户点击播放前预先缓冲音频资产,消除点击后的等待时间:
let player = ApplicationMusicPlayer.shared
// 提前缓冲
Task {
try await player.prepareToPlay()
}
播放控制与状态观察
(14:49)ApplicationMusicPlayer 的 queue 和 state 都是可观察对象,可以直接在 SwiftUI 视图中使用。
显示当前歌曲的专辑封面:
@State var queue = ApplicationMusicPlayer.shared.queue
var body: some View {
VStack {
if let artwork = queue.currentEntry?.artwork {
ArtworkImage(artwork, width: 200, height: 200)
} else {
RoundedRectangle(cornerRadius: 16)
.fill(.quaternary)
.frame(width: 200, height: 200)
}
}
}
关键点:
ArtworkImage是 MusicKit 提供的 SwiftUI 视图,自动处理专辑封面加载和显示queue.currentEntry?.artwork获取当前播放项的封面资源- 用
.quaternary填充色做占位图,视觉过渡自然
(15:06)显示歌曲标题和副标题:
if let currentSong = queue.currentEntry {
Text(currentSong.title)
.font(.title3.bold())
if let subtitle = currentSong.subtitle {
Text(subtitle)
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
(15:14)播放/暂停控制:
let player = ApplicationMusicPlayer.shared
@State var state = ApplicationMusicPlayer.shared.state
var isPlaying: Bool {
state.playbackStatus == .playing
}
var playPause: some View {
Button(
isPlaying ? "Pause" : "Play",
systemImage: isPlaying ? "pause.fill" : "play.fill"
) {
if isPlaying {
player.pause()
} else {
Task {
try await player.play()
}
}
}
}
关键点:
state.playbackStatus返回.playing、.paused等枚举值player.play()和player.pause()都是异步方法,需要在 Task 中调用- 播放状态变化会自动触发 SwiftUI 视图更新
(15:38)上一首/下一首控制:
let player = ApplicationMusicPlayer.shared
var controls: some View {
HStack {
Button("Back", systemImage: "backward.fill") {
Task {
try await player.skipToPreviousEntry()
}
}
// ...
Button("Next", systemImage: "forward.fill") {
Task {
try await player.skipToNextEntry()
}
}
}
}
曲库请求与跨区等效
(16:26)MusicCatalogResourceRequest 用于从 Apple Music 曲库中请求特定资源。可以指定关联数据(如歌曲的 Artists 关系)和返回数量限制。
(18:58)跨区等效是 MusicKit 的一个重要特性。同一首歌曲在不同区服的 Apple Music 可能有不同的 ID,某些区服可能有”清洁版”替代” explicit 版”。使用 .findEquivalents 选项可以自动处理这些映射:
func fetchSongs(songIDs: [MusicItemID]) async throws -> (featured: Song?, other: [Song]) {
var request = MusicCatalogResourceRequest<Song>(
matching: \.id, memberOf: songIDs
)
request.options = [.findEquivalents]
let response = try await request.response()
let featuredSongID = songIDs[0]
let featuredSong = response.item(for: featuredSongID)
let others: [Song] = songIDs[1...].compactMap { songID in
return response.item(for: songID)
}
return (featuredSong, others)
}
关键点:
MusicCatalogResourceRequest<Song>是泛型请求,<Song>指定返回类型matching: \.id, memberOf: songIDs表示按 ID 匹配请求列表中的歌曲.findEquivalents启用跨区等效查找,自动替换不可用的资源response.item(for:)按原始 ID 获取结果,即使实际返回的是等效版本MusicItemCollection支持分页,通过hasNextBatch和nextBatch()获取后续结果
核心启发
1. 为健身 App 添加训练 BGM
在训练开始界面加一个”选歌”按钮,用 .musicPicker() 让用户挑选播放列表,通过 ApplicationMusicPlayer 在训练期间循环播放。利用 prepareToPlay() 在训练倒计时阶段提前缓冲,用户点击”开始”时音乐即刻响起。
入口 API:ApplicationMusicPlayer.shared + .musicPicker()
2. 构建专注计时器的白噪音/背景音乐功能
番茄钟或冥想 App 可以用 MusicCatalogResourceRequest 请求 Apple Music 上的专注歌单或白噪音专辑,展示给用户作为预设选项。用户点击即可开始播放,无需离开 App 去 Music 中搜索。
入口 API:MusicCatalogResourceRequest<Playlist> + ApplicationMusicPlayer
3. 社交 App 的”分享此刻在听”功能
用 queue.currentEntry 获取用户当前播放的歌曲信息,结合 ArtworkImage 生成分享卡片。通过 MusicCatalogResourceRequest 的 .findEquivalents 选项确保分享的歌曲链接在不同区服的用户都能正常打开。
入口 API:queue.currentEntry + MusicCatalogResourceRequest + .findEquivalents
4. 游戏 App 的动态背景音乐切换
根据游戏场景(战斗、探索、剧情)预加载不同的 MusicItemCollection,通过 ApplicationMusicPlayer 的队列操作实现无缝切换。开启 Audio Background Mode 后,即使玩家切换到其他 App,音乐也能继续播放。
入口 API:ApplicationMusicPlayer.shared.queue + prepareToPlay()
5. 儿童 App 的内容安全过滤
利用 MusicCatalogResourceRequest 的 .findEquivalents 选项,当请求的 explicit 内容在用户账号设置下不可用时,自动返回清洁版替代。无需在 App 中维护内容分级逻辑。
入口 API:MusicCatalogResourceRequest + .findEquivalents
关联 Session
- 253 - Explore music understanding — Apple Music 的音乐理解能力,可与 MusicKit 结合实现智能推荐
- 312 - What’s new in Now Playing — 系统级播放控制与 Now Playing 界面的最新更新
- 310 - What’s new in Shortcuts — 通过快捷指令扩展 App 的音乐相关自动化能力
- 262 - What’s new in Swift — MusicKit 全面采用 Swift Concurrency 和 SwiftUI 的新特性
- 269 - What’s new in SwiftUI —
.musicPicker()和.musicSubscriptionOffer()等修饰器的 SwiftUI 实现细节
评论
GitHub Issues · utterances