Highlight
iOS 17 让 HomePod 用户可以直接通过 Siri 播放 iPhone 或 iPad 上安装的媒体应用内容,已有 SiriKit Media Intents 支持的应用无需任何代码改动即可使用,系统通过 Wi-Fi 将请求路由到用户设备,应用启动后通过 AirPlay 将内容回传到 HomePod。
核心内容
工作原理
用户向 HomePod 发出播放请求时,流程如下:
- HomePod 处理语音请求,生成 SiriKit Intent
- Intent 通过 Wi-Fi 发送到用户的 iPhone(主设备)
- iPhone 启动应用,应用处理播放请求
- 音频通过 AirPlay 回传到 HomePod
设备需要在同一 Wi-Fi 网络,但不需要物理 proximity。用户在客厅对 HomePod 说话,手机在卧室充电也能正常工作。
(00:50)
支持的媒体类型和请求
支持的媒体类型包括音乐、有声读物、播客、电台、冥想等。Play、Add 和 Affinity(喜爱/添加到播放列表)请求都会路由到主设备。
请求示例:
- “Play jazz on Control Audio” — 播放类型 + 应用名
- “Play Push It by Dukes on Control Audio” — 歌曲名 + 应用名
- “Play my audiobook” — 恢复上次播放
- “Play the latest news” — 播客/新闻类请求
- “Add this to my playlist” — 添加到播放列表
- “I like this song” — 标记喜爱
(02:15)
实现 SiriKit Media Intents
import Intents
class PlayMediaIntentHandler: NSObject, INPlayMediaIntentHandling {
func handle(intent: INPlayMediaIntent, completion: @escaping (INPlayMediaIntentResponse) -> Void) {
// 检查请求的具体内容
if let mediaName = intent.mediaSearch?.mediaName,
mediaName == "Push It" {
let mediaItem = INMediaItem(
identifier: "song-push-it",
title: "Push It",
type: .song,
artwork: nil
)
let response = INPlayMediaIntentResponse(
code: .success,
userActivity: nil
)
response.mediaItems = [mediaItem]
completion(response)
return
}
// 检查是否是不支持的媒体类型
if intent.mediaSearch?.mediaType == .music {
let response = INPlayMediaIntentResponse(
code: .failureRequiringAppLaunch,
userActivity: nil
)
completion(response)
return
}
// 默认:未找到内容
completion(INPlayMediaIntentResponse(code: .failure, userActivity: nil))
}
}
关键点:
- 已有 SiriKit Media Intents 支持的应用无需任何改动
- 新应用需要添加 Intents Extension,实现
INPlayMediaIntentHandling - 返回具体的错误原因(如不支持的媒体类型、需要登录),Siri 会给出更有帮助的回复
(04:11)
响应方式选择
import Intents
func handle(intent: INPlayMediaIntent, completion: @escaping (INPlayMediaIntentResponse) -> Void) {
// 方式一:后台播放(推荐用于音频)
let backgroundResponse = INPlayMediaIntentResponse(
code: .handleInApp,
userActivity: nil
)
// 方式二:前台启动
let foregroundResponse = INPlayMediaIntentResponse(
code: .continueInApp,
userActivity: nil
)
// 音频播放推荐用 handleInApp,不需要解锁手机
completion(backgroundResponse)
}
关键点:
handleInApp在后台启动应用播放,用户不需要解锁手机continueInApp将应用带到前台- HomePod 场景下后台播放体验更好,因为手机可能不在手边
(07:10)
详细内容
音频会话配置
正确的音频会话配置是后台播放的基础。
import AVFoundation
func configureAudioSession() {
let session = AVAudioSession.sharedInstance()
do {
// 设置为播放类别,支持后台播放
try session.setCategory(.playback, mode: .default)
// 播客/有声读物推荐用 spokenAudio 模式
// 被打断时暂停而非降低音量
try session.setCategory(.playback, mode: .spokenAudio)
// 激活会话
try session.setActive(true)
} catch {
print("Failed to configure audio session: \(error)")
}
}
关键点:
- 必须在激活会话前设置 category 为
.playback .spokenAudio模式在被打断时暂停播放,适合播客和有声读物- 未正确配置时,应用进入后台(如锁屏)后播放会停止
(08:55)
AirPlay 集成
import MediaPlayer
func setupNowPlayingAndRemoteCommands() {
// 设置当前播放信息
var nowPlayingInfo: [String: Any] = [
MPMediaItemPropertyTitle: "Push It",
MPMediaItemPropertyArtist: "Dukes",
MPNowPlayingInfoPropertyPlaybackRate: 1.0
]
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
// 接收远程控制命令
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.addTarget { event in
self.resumePlayback()
return .success
}
commandCenter.pauseCommand.addTarget { event in
self.pausePlayback()
return .success
}
commandCenter.nextTrackCommand.addTarget { event in
self.skipToNext()
return .success
}
commandCenter.previousTrackCommand.addTarget { event in
self.skipToPrevious()
return .success
}
}
关键点:
MPNowPlayingInfoCenter向系统报告当前播放内容MPRemoteCommandCenter接收播放/暂停/跳过等远程命令- 这是 AirPlay 正常工作的必要条件
(10:52)
处理播放中断
import AVFoundation
class AudioPlayerManager: NSObject {
override init() {
super.init()
NotificationCenter.default.addObserver(
self,
selector: #selector(handleInterruption),
name: AVAudioSession.interruptionNotification,
object: nil
)
}
@objc func handleInterruption(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
switch type {
case .began:
// 暂停播放
pausePlayback()
case .ended:
if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// 恢复播放
resumePlayback()
}
}
@unknown default:
break
}
}
}
关键点:
- 监听
AVAudioSession.interruptionNotification - 来电、导航提示、Siri 请求等都会触发中断
- 中断结束后根据
shouldResume选项决定是否恢复播放
(10:28)
请求路由机制
Siri 如何确定使用哪台设备:
- 请求路由到主 iPhone(在 Apple ID 和 Find My 设置中共享位置的设备)
- 请求用户需要在 Home 中注册并启用”识别我的声音”
- 不需要启用个人请求
- 首次使用应用时,Siri 会请求权限访问应用数据
(11:46)
个人应用词汇表
import Intents
func donatePersonalVocabulary() {
let intent = INAddMediaIntent(
mediaSearch: INMediaItem(
identifier: "playlist-workout",
title: "Workout Mix",
type: .playlist,
artwork: nil
)
)
// 向系统捐赠个人词汇
let interaction = INInteraction(intent: intent, response: nil)
interaction.donate { error in
if let error = error {
print("Donation failed: \(error)")
}
}
}
关键点:
- 使用个人应用词汇表告知系统用户特定的实体
- 包括个人播放列表、购买的有声读物、收藏的播客等
- 提升 Siri 对个性化请求的识别率
(08:30)
核心启发
-
做什么:为媒体应用添加 SiriKit Media Intents 支持
- 为什么值得做:无需额外代码即可让应用在 HomePod 上可用,覆盖全新的使用场景
- 怎么开始:添加 Intents Extension,实现
INPlayMediaIntentHandling,处理mediaName和mediaType字段
-
做什么:优化 AirPlay 音频体验
- 为什么值得做:HomePod 场景完全依赖 AirPlay,缓冲和延迟直接影响用户体验
- 怎么开始:配置
AVAudioSession为.playback类别,设置MPNowPlayingInfoCenter和MPRemoteCommandCenter,使用缓冲播放 API 消除恢复延迟
-
做什么:实现具体的错误反馈
- 为什么值得做:模糊的”找不到内容”会让用户困惑,具体的错误提示能帮助用户解决问题
- 怎么开始:对不支持的媒体类型返回
.failureRequiringAppLaunch,在应用内给出具体原因(如”不支持播放流派”、“需要登录”)
-
做什么:为播客和有声读物优化音频会话
- 为什么值得做:
.spokenAudio模式在中断时暂停而非降低音量,确保用户不会错过内容 - 怎么开始:将
AVAudioSession的 mode 设置为.spokenAudio,处理interruptionNotification在中断结束后恢复播放
- 为什么值得做:
-
做什么:捐赠个人词汇提升识别率
- 为什么值得做:用户创建的播放列表、收藏的内容名称是通用模型无法识别的
- 怎么开始:用
INInteraction捐赠用户特定的媒体项,让 Siri 了解用户的个性化内容
关联 Session
- Spotlight your app with App Shortcuts — 通过 App Shortcuts 让用户用语音触发应用功能
- Explore enhancements to App Intents — App Intents 框架的最新增强
- Tune up your AirPlay experience — 优化 AirPlay 音频体验
评论
GitHub Issues · utterances