System & Services 进阶 20m
解读和处理常见的 Siri 错误
Decipher and deal with common Siri errors
2020年6月26日
一句话判断
你的 Intent Handler 返回 .failure 就结束了?这场 Session 讲了十几种不同的错误码和它们对应的用户体验——选对错误码比选对成功码更重要。
这场 Session 讲了什么
大多数开发者在 Intent 处理失败时只返回一个通用的 .failure,但 SiriKit 定义了大量细分的错误码,每个对应不同的用户反馈和处理方式。
Session 按错误类型分类讲解:用户输入相关的错误(内容不存在、参数不完整)、系统状态相关的错误(需要登录、需要更新 App)、网络相关的错误(超时、连接失败)、以及权限相关的错误(用户拒绝了权限)。
核心观点是:精确的错误码能让 Siri 给出更准确的反馈。比如 .failureNoContentViewable 会让 Siri 说”找不到这个内容”,而 .failureRequiringAppLaunch 会让 Siri 提示用户”请在 App 中完成操作”。模糊的 .failure 只会让 Siri 说”出了点问题”。
值得深挖的点
错误码与用户信任。 当 Siri 说”出了点问题,稍后再试”时,用户不知道是自己的问题还是 App 的问题,会降低对 App 和 Siri 的信任。精确的错误反馈(“你的会员已过期”、“这个内容需要付费”)帮助用户理解问题并采取行动。
可恢复错误 vs 不可恢复错误。 可恢复错误(如网络超时)可以让 Siri 建议用户重试。不可恢复错误(如内容已下架)应该直接告知用户并建议替代方案。区分这两类错误对用户体验至关重要。
代码片段
import Intents
// 精确的错误处理
class PlayMediaIntentHandler: NSObject, INPlayMediaIntentHandling {
func handle(intent: INPlayMediaIntent, completion: @escaping (INPlayMediaIntentResponse) -> Void) {
// 检查用户登录状态
guard userManager.isLoggedIn else {
// 用户未登录,提示打开 App 登录
let response = INPlayMediaIntentResponse(
code: .failureRequiringAppLaunch,
userActivity: makeActivity(for: .loginRequired)
)
completion(response)
return
}
// 检查内容是否存在
guard let mediaName = intent.mediaSearch?.mediaName else {
let response = INPlayMediaIntentResponse(
code: .failureNoContentViewable,
userActivity: nil
)
completion(response)
return
}
searchAndPlay(mediaName: mediaName) { result in
switch result {
case .success(let item):
let response = INPlayMediaIntentResponse(code: .success, userActivity: nil)
response.nowPlayingInfo = self.buildNowPlayingInfo(for: item)
completion(response)
case .failure(let error):
let response: INPlayMediaIntentResponse
switch error {
case .contentNotFound:
// 内容不存在,建议替代
response = INPlayMediaIntentResponse(
code: .failureNoContentViewable,
userActivity: nil
)
case .subscriptionRequired:
// 需要订阅
response = INPlayMediaIntentResponse(
code: .failureRequiringAppLaunch,
userActivity: self.makeActivity(for: .subscriptionPage)
)
case .networkError:
// 网络问题,可以重试
response = INPlayMediaIntentResponse(
code: .failure,
userActivity: nil
)
}
completion(response)
}
}
}
}
// 创建带上下文的 NSUserActivity
// 当用户点击 Siri 的"打开 App"按钮时跳转到对应页面
func makeActivity(for destination: AppDestination) -> NSUserActivity {
let activity = NSUserActivity(activityType: "com.example.app.\(destination.rawValue)")
activity.userInfo = ["destination": destination.rawValue]
activity.title = destination.title
return activity
}
enum AppDestination: String {
case loginRequired
case subscriptionPage
case contentDetail
var title: String {
switch self {
case .loginRequired: return "登录"
case .subscriptionPage: return "订阅"
case .contentDetail: return "内容详情"
}
}
}
最佳实践
- 永远不要用通用的
.failure。 至少区分”找不到内容”和”需要用户操作”两类错误。 - 为可恢复错误提供重试建议。 网络超时、服务暂时不可用等错误可以建议用户稍后重试。
- 用
userActivity指引下一步操作。 当 Siri 提示”请在 App 中操作”时,通过 userActivity 让 App 打开到正确的页面。 - 记录错误的分布和频率。 通过分析错误数据找出最常见的问题并优先修复。
还有什么值得关注
.failureRequiringAppLaunch会自动在 Siri 界面上添加”打开 App”按钮- 新的错误类型在每年的 SDK 中会增加,查阅最新的 IntentResponse 文档
- Siri 会在连续失败后建议用户尝试其他方式(如手动打开 App)
- 错误反馈的多语言适配需要考虑语气和措辞
WWDC 2020