从 SiriKit 到 Siri Shortcuts

Apple 在 WWDC 2016 上推出了 SiriKit,使得开发者可以开发 app extension 让用户可以利用 Siri 完成特定的几种任务。这里有两个基本概念:

  • Domain:任务的类型,比如消息、VoIP、支付、运动、打车、搜索照片等

  • Intent:使用 Siri 完成的具体任务,比如发红包、发消息

Siri 会完成自然语言处理,理解用户的意图,并决定什么时机来调用我们的 app extension,我们只需要告诉 Siri 是否可以处理这个 intent 并完成就可以了。Apple 为各个 domain 的 intents 提供了协议作为模板供开发者实现,以发送信息为例:

当用户想用 Siri 发送消息时,系统会创建一个 INSendMessageIntent,里面包含消息接受者、消息内容等信息,然后调用 app extension 的 handler,handler 解析这些上下文,确认可以处理并发送完消息后返回 INSendMessageIntentResponse 类型的结果。

在 WWDC 2018 上,Apple 推出了 Siri Shortcuts 进一步拓展了 Siri 的使用场景。Siri Shortcuts 能让用户通过一句自定义的短语让 Siri 完成自己经常进行的动作,比如查看今天的天气,叫一份外卖。与 SiriKit 最主要的不同点有两个:

  • 只针对具体的动作,不需要用户提供信息。可以理解成类和实例的关系。我们用 SiriKit 来打车,每次的出发地和目的地都不一样;而 Siri Shortcuts 只完成打车回家这个具体的动作,永远是从当前位置到家里,用户也就不需要再告诉 Siri 出发地和目的地。

  • 可以用 Siri Shortcuts 完成的动作不限于 SiriKit 的几个 domain。Siri Shortcuts 可以理解成为特定动作设置的一个快捷方式,因为 Siri Shortcuts 不涉及语义分析,也就不用对实现的动作做限制。

使用

假设我们有一个 app 叫小决定,用转盘来帮助选择困难症患者做决定。现在要为小决定添加 Siri Shortcuts 的支持。

定义 Shortcuts

首先,我们需要定义要完成的动作。Apple 用 INShortcut 来表示一个 shortcut,并提供了两种方式初始化参数:NSUserActivityINIntent,用来封装动作的上下文。

  • NSUserActivity:iOS 8 开始提供,用来实现 Handoff 和 Spotlight 搜索,用来在手机和电脑之间传递用户操作的上下文。

  • INIntent:iOS 10 新添加的类,使得用户不用离开 Siri 的界面就可以完成动作。开发者不需要继承这个类,而是直接使用 Xcode 自动创建的子类。

这里只介绍第二种。

如果要实现的功能不属于 SiriKit 支持的 domain,就需要创建一个自定义的 Intent。我们首先需要通过 File > New > File… 新建一个 SiriKit Intent Definition File 文件,然后创建自己的 Intent
Siri Shortcuts 指北 - 图1

这里依次填入基本信息,重点是下方的 Parameters 和 Shortcut Types。Parameters 用来存放完成动作的上下文信息,比如要点的咖啡种类、数量、选项(是否加冰等)。Shortcut Types 是 Parameters 的组合,比如“咖啡种类”和“咖啡种类加选项”,可以为不同的种类设置是否支持后台执行。如果支持后台执行,Siri 会调用 app extension 来完成动作。

这里我们定义了一个 Intent 叫 MakeDecisionIntent,它有两个参数,决定的 id 和名称。

定义完 Intent 之后需要定义 response。
Siri Shortcuts 指北 - 图2

在这里可以定义一些 Properties,下面可以组合成不同的结果模板。系统提供了一些默认的错误码,开发者也可以定义新的错误码,Xcode 会为这些错误码生成对应的代码。这里我定义了一个 decisionNotFound 的错误码,来代表 id 对应的决定找不到的情况,在响应 Shortcut 的时候需要特殊处理。

捐赠(Donate)Shortcuts

然后,我们需要告诉系统定义好的 shortcuts。

要使用 NSUserActivity 来 donate shortcuts,只需要将 isEligibleForPrediction 设置成 true 即可。用这种方法 donate,Siri 在匹配到 Shortcuts 后会跳转到 app 中完成后续的操作。

如果使用的是 Intent,那么在合适的时机(比如用户刚刚决定今晚吃什么),可以用 INInteraction 来 donate 「今晚吃什么」这个 intent。在一段时间后,Siri 会找到用户的行为模式(比如每周五下午都会做这个决定),会在「合适的时机」在锁屏界面或者 Siri 的搜索界面显示这个 shortcut。
Siri Shortcuts 指北 - 图3

响应 Shortcuts

为了达到不离开 Siri 完成动作的目的,我们需要为 app 添加一个 Intent App Extension,跟其他类型的 App Extension 一样是以独立进程的形式运行的,因此编译完成后跟主 app 是两个独立的二进制文件,可以将公用的逻辑以 framework 的方式共享。

在 Xcode 自动生成的 IntentHandler 中,有两个方法需要实现:

  1. func handler(for intent: INIntent) -> Any // 1
  2. func handle(intent: MakeDecisionIntent, completion: @escaping (MakeDecisionIntentResponse) -> Void) // 2

方法一中需要返回一个 handler,在这里就是 IntentHandler 本身。
方法二处理具体的逻辑。可以看到这个方法包含两个参数,一个是之前 app donate 的 Intent,另一个参数是一个闭包,用来作为处理完成后的回调。其中的 MakeDecisionIntentMakeDecisionIntentResponse 都是 Xcode 自动生成的。如果用户在添加了短语后删除了对应的决定,就返回前面定义的 decisionNotFound,这个时候 Siri 将前面定义好的错误信息展示给用户。

快捷短语

除此之外,用户还可以主动为某个 shortcut 添加一个短语(类似语音命令),当用户对 Siri 说出这个短语时,就会触发相应 shortcut 的执行。INIntent 有一个属性叫做 suggestedInvocationPhrase,用来为用户提供短语的建议,这个短语应该尽可能简洁并有区分度。

Apple 还特地为管理短语提供了一个 Add to Siri 按钮(INUIAddVoiceShortcutButton)。这个按钮统一了 Siri Shortcuts 短语的视觉和交互,使得用户更容易接受。
Siri Shortcuts 指北 - 图4

用户点按这个按钮后会展示一个系统提供的界面,可以通过它添加、修改、删除短语。

Siri Shortcuts 指北 - 图5

短语添加完成后就可以在 Siri 中使用了:
Siri Shortcuts 指北 - 图6