从 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,并提供了两种方式初始化参数:NSUserActivity
和 INIntent
,用来封装动作的上下文。
NSUserActivity
:iOS 8 开始提供,用来实现 Handoff 和 Spotlight 搜索,用来在手机和电脑之间传递用户操作的上下文。INIntent
:iOS 10 新添加的类,使得用户不用离开 Siri 的界面就可以完成动作。开发者不需要继承这个类,而是直接使用 Xcode 自动创建的子类。
这里只介绍第二种。
如果要实现的功能不属于 SiriKit 支持的 domain,就需要创建一个自定义的 Intent。我们首先需要通过 File > New > File… 新建一个 SiriKit Intent Definition File 文件,然后创建自己的 Intent
这里依次填入基本信息,重点是下方的 Parameters 和 Shortcut Types。Parameters 用来存放完成动作的上下文信息,比如要点的咖啡种类、数量、选项(是否加冰等)。Shortcut Types 是 Parameters 的组合,比如“咖啡种类”和“咖啡种类加选项”,可以为不同的种类设置是否支持后台执行。如果支持后台执行,Siri 会调用 app extension 来完成动作。
这里我们定义了一个 Intent 叫 MakeDecisionIntent
,它有两个参数,决定的 id 和名称。
定义完 Intent 之后需要定义 response。
在这里可以定义一些 Properties,下面可以组合成不同的结果模板。系统提供了一些默认的错误码,开发者也可以定义新的错误码,Xcode 会为这些错误码生成对应的代码。这里我定义了一个 decisionNotFound
的错误码,来代表 id 对应的决定找不到的情况,在响应 Shortcut 的时候需要特殊处理。
捐赠(Donate)Shortcuts
然后,我们需要告诉系统定义好的 shortcuts。
要使用 NSUserActivity
来 donate shortcuts,只需要将 isEligibleForPrediction
设置成 true 即可。用这种方法 donate,Siri 在匹配到 Shortcuts 后会跳转到 app 中完成后续的操作。
如果使用的是 Intent,那么在合适的时机(比如用户刚刚决定今晚吃什么),可以用 INInteraction
来 donate 「今晚吃什么」这个 intent。在一段时间后,Siri 会找到用户的行为模式(比如每周五下午都会做这个决定),会在「合适的时机」在锁屏界面或者 Siri 的搜索界面显示这个 shortcut。
响应 Shortcuts
为了达到不离开 Siri 完成动作的目的,我们需要为 app 添加一个 Intent App Extension,跟其他类型的 App Extension 一样是以独立进程的形式运行的,因此编译完成后跟主 app 是两个独立的二进制文件,可以将公用的逻辑以 framework 的方式共享。
在 Xcode 自动生成的 IntentHandler
中,有两个方法需要实现:
func handler(for intent: INIntent) -> Any // 1
func handle(intent: MakeDecisionIntent, completion: @escaping (MakeDecisionIntentResponse) -> Void) // 2
方法一中需要返回一个 handler,在这里就是 IntentHandler
本身。
方法二处理具体的逻辑。可以看到这个方法包含两个参数,一个是之前 app donate 的 Intent,另一个参数是一个闭包,用来作为处理完成后的回调。其中的 MakeDecisionIntent
和 MakeDecisionIntentResponse
都是 Xcode 自动生成的。如果用户在添加了短语后删除了对应的决定,就返回前面定义的 decisionNotFound
,这个时候 Siri 将前面定义好的错误信息展示给用户。
快捷短语
除此之外,用户还可以主动为某个 shortcut 添加一个短语(类似语音命令),当用户对 Siri 说出这个短语时,就会触发相应 shortcut 的执行。INIntent
有一个属性叫做 suggestedInvocationPhrase
,用来为用户提供短语的建议,这个短语应该尽可能简洁并有区分度。
Apple 还特地为管理短语提供了一个 Add to Siri 按钮(INUIAddVoiceShortcutButton)。这个按钮统一了 Siri Shortcuts 短语的视觉和交互,使得用户更容易接受。
用户点按这个按钮后会展示一个系统提供的界面,可以通过它添加、修改、删除短语。
短语添加完成后就可以在 Siri 中使用了: