作者:wiilen,iOS 开发者。
Sessions: https://developer.apple.com/videos/play/wwdc2020/10073/
今年,苹果为 intent 提供了更强大的 API。本文的重点是如何有效地在 App 中支持 Siri 和 Shortcut 的相关功能,为此工程师提供了几个技巧和策略。
小提示:这个 Session 的字幕有些问题,有一大部分断句断的位置不对,看的时候最好直接听。
内容主要分为两块:
今年 SiriKit 提供的新 API.
如何调试自定义的 intent。
先来看看一图简介:
总体回顾
SiriKit 中提供了 Intent 和 Intents UI framework,让 App 提供的服务能和 Siri、Shortcuts 及 Maps 等服务融为一体,提供流畅的体验。而在 iOS 14 中,开发者能通过 intents 给 widget 添加配置,并使其更智能。
Intents UI extension 能在 Siri Shortcut 或者 Maps 界面中展现自定义的 UI。当 intents extension 完成了用户的请求,它能从 SiriKit 中获取用户的指令,并将其转化为 App 特定的操作,比如发消息、播放音乐、查看当前天气、或者定个外卖。SiriKit 中定义了大量系统级的 Intent,能执行常见操作。对系统级的 Intent 来说,Siri 已经定义好了工作流程,App 只需要提供数据即可完成对应操作。如果你的 App 能让用户执行一些日常的操作,而目前 SiriKit 中并没有提供,那可以创建自定义的 intent 来实现这个操作流程。
Intents extension 为 SiriKit 提供 Handler 对象,用来处理特定 intent 。Intent 处理的生命周期:每次用户执行一个 intent,你的 intent handler 都有 10 秒来完成这个请求。10 秒的超时时间从用户的请求初始化并链接到你的 intent extension 时就开始计算了。当这个过程开始时,如果你的 extension 还不在运行,系统会唤起它。Extension 的启动时间取决于载入所有 framework 的时间,以及该程序和它所链接的 framework 调用 load
方法和静态初始化的时间。该 extension 也需要一些时间来执行业务逻辑。
所以要优化启动时间,需要让 extension 只链接必要的 framework。处理请求的时间也影响用户使用 App 的感受。使用 Siri 的过程应该是快速简便的,所以在处理 intent 时应该减少用户的等待时间。由于在大部分情况下你的 extension 并不需要链接到所有的 framework,你就可以减少需要引入的 symbol。对于 intent extension,值得一提的是比起 App 来它们的执行过程相对独立,运行内存也较少。
In-app intent 处理
在 iOS 14 中,有时候无法使用 extension,或者用起来很麻烦。这次苹果提出了新的 in-app intent 处理。如今开发者可以选择将 intent handler 加入到 App 中,处理 SiriKit 中的请求。
适合 in-app intent 处理的场景
控制音乐播放、开始运动等 intent 之前需要先在 extension 中处理,然后再交给 App。如今直接在 App 中处理会更有效率。
处理 intent 的结果会影响当前 App 的 UI,这种 intent 也适合放在 App 中处理。
需要大量内存的场景,比如原先受 extension 内存限制无法实现的图片和视频处理。
app 架构导致无法把代码抽出来放到 extension 中,或放到一个共享的 framework。要小心,App 的启动时间也会包含在 10 秒的超时时间内。
如何实现 in-app intent handler
- 首先要确认 App 支持多 window,并适配了 UIScene lifecycle。当 App 因 SiriKit 请求被唤起时,还没有 UIScene 对象连接到 App。
- 然后你需要在 App target 的 support intents section 下列出所有你想要在 App 中处理的 intent。
- 最后,你需要在
AppDelagate
中实现handlerForIntent:
方法。这个方法作为调度中心,将 intent 分发给能够处理它的对象。在具体实现中,先检查 intent 的类型,然后返回能够处理它的对象。这个对象必须实现了用于处理该 intent 的协议。
如果需要用户使用 intent 之前正在操作 App,才能在处理该 intent 时更新相关 UI,那么 intent handler 就需要确保在处理过程中相关的 UI 仍在屏幕上。这可以通过检查 App 是否在后台来实现,如果 App 在后台,就需要返回 continueInApp
来让系统打开 App。
实现演示
演示的 App 主要用于浏览食谱,点击 direction 按钮时,可以分步骤浏览食谱。当要准备做饭,操作 App 就不太方便,所以就希望用户能够用语音唤起 Siri,执行 shortcut 来查看下一步的操作。由于需要和屏幕上的内容做交互,这里选择了 in-app intent 的实现方式。
接下来工程师演示了添加过程,从 07:57 开始,建议这里直接观看 demo 来学习,实现的步骤和上文中所述一致。
这里有几个要点可以参考:
- 检查 multiple window 的支持,以及添加 intent 到 App Target 中。
- 由于希望 App 中的每一个 view controller 都能响应
nextStep
这个 command,所以定义了一个通用的IntentHandler
类,初始化方法接收一个实现了NextStepProviding
协议的对象以及一个Recipe
对象。如果 view controller 实现了这个协议,就需要提供一个IntentHandler
的实例,并且需要实现nextStep
方法来让用户进入下一步。
让 IntentHandler
遵守 ShowDirectionsIntentHandling
协议,在 resolve
方法中,我们检查是否有 Recipe
对象来决定下一步怎么走。在 handle
方法中,首先要确认 App 在前台,这一点之前也提到了。如果需要唤起 App,需要在 UIScene Delegate 中处理。
- 由于需要引用
IntentHandler
对象,demo 使用了单例来弱引用,在viewDidAppear:
方法中更新对应的引用。然后在AppDelegate
中,实现新的handlerForIntent:
方法,并返回单例中持有的IntentHandler
。
而在 SceneDelegate
中,需要实现 willConnectToSession:
和 continueUserActivity:
两个代理方法,用于在 handle 方法返回了 continueInApp
之后继续处理 user activity。当还没有 UIScene
对象连接到 App 时,willConnectToSession:
方法会被调用。
- 让所有的 view controller 实现
NextStepProviding
协议,然后在viewDidAppear:
更新currentIntentHandler
。
一般来说在 extension 中处理 intent 是更好的选择。有以下几个理由:
Intent extension 更轻量,启动更快。
通过评估要链接的 framework 数量来优化启动时间。
In-app intent handling 总结
优先考虑使用 intent extension。
谨慎选择 App 所链接的 framework。
3 .In-app intent handling 需要 App 支持 multiple windows。(此处可以参考 WWDC 19 关于多 window 介绍的两个视频)
- In-app intent handling 可以用于处理新场景下的任务。
更丰富的歧义消除功能
在 iOS 13 中,苹果为 Shortcuts 引入了新的功能,可以让用户在运行时提供参数来帮助处理 intent。通过返回一个用于消除歧义的解析结果,用户可以从弹出的列表中选择一个作为 intent 的参数。今年你可以为这个列表添加副标题和图片了,只需要在运行时提供 String
类型的副标题和 INImage
类型的图片。在 Shortcuts App 中配置 intent 时,你也可以提供带有图片和副标题的选项。
为消除歧义列表添加的另一个功能是分页。作为一名开发者,现在你可以指定 Siri 一次性可以朗读的选项数目,也可以指定子项目中的引导词。只有用户用 Hey Siri 唤醒时,这种多级分页才会启用。
在自定义 intent 的文件中,对需要自定义分页的 intent,展开 Siri dialog,在这里你可以指定 Siri 一次性可以读给用户的选项数目。就像你可以指定子项目的引导词一样,你也可以覆盖 Siri 提供的消除歧义的引导词,提供个性化的词句。
Dynamic search
去年,苹果提供了动态选项的 API,让你可以在用户配置 intent 时为其提供合适的选项。今年的新 API 允许用户在配置 intent 时搜索可用选项。在自定义 intent 的文件中,Dynamic Options 栏下,有一个新的勾选框,勾上之后用户就可以搜索可用的选项。
勾选之后会生成一个新的方法,方法入参中含有用户的搜索词。用户在输入时,这个方法会不断被调用。当搜索词为空,你也可以为用户提供默认搜索词。Dynamic Search 只适合搜索大量的选项时使用,而不建议用于过滤少量的静态选项,这是因为 Shortcuts App 默认就支持过滤。
提供选项的 completion handler 中有一个新的类型 INObjectCollection,有了这个类型,你可以将选项按段划分,提供标题,并可以选择按索引来排序。
在 iOS 13 中,苹果为 Shortcuts 引入了可配置的参数,所有配置为用户输入的参数必须一次性解析完。在 iOS 14,现在可以分开配置和解析这些参数了。Siri 和 Shortcuts App 不会解析被标记为 unresolvable 的参数。
一些小建议
- 如果你有一些自定义的 intent,由于相关功能不再维护而不再使用了,或者你用另外一个 intent 替代了它,在 iOS 14 中你可以把这个 intent 标记为 deprecated。标记之后,用户就可以看到这个 intent 在将来不可用的提示了,且 Shortcuts App 中也无法再从动作列表选择该动作。
- 关于如何指定自定义 intent 的类名。当你在 intent 文件的 custom intent 中添加了 intent 的类型或者是自定义 enum 时,实际上添加的是类型的名称而不是具体的类型。具体的类型会根据类型名字来自动生成。这个地方并不是用来指定 intent 的类型和 enum的。
应该在 custom class inspector 中指定类型。
或者你也可以在 target 的项目文件中,为自定义的 intent 类型和 enum 指定类前缀。
- 如何高效管理多个 intent 和 intent UI extension。当往 intent UI extenstion 中添加 intent 定义文件时,Xcode 会自动列出所有该 intent UI extension 支持的 intent,然而有些 intent 你可能不想让它显示 UI。这时可以创建一个新的 intent 定义文件,加入非 UI 的 intent。然后在 intent 定义文件的 target membership Inspector,不将其加入 intent UI extension target。
- 选择生成代码的语言。在 project 的 build setting 中修改即可。
推荐阅读
关注我们
我们是「老司机技术周报」,每周会发布一份关于 iOS 的周报,也会定期分享一些和 iOS 相关的技术。欢迎关注。
关注有礼,关注【老司机技术周报】,回复「2020」,领取学习大礼包。
支持作者
这篇文章的内容来自于 《WWDC20 内参》。在这里给大家推荐一下这个专栏,专栏目前已经创作了 108 篇文章,只需要 29.9 元。点击【阅读原文】,就可以购买继续阅读 ~
WWDC 内参 系列是由老司机周报、知识小集合以及 SwiftGG 几个技术组织发起的。已经做了几年了,口碑一直不错。 主要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经验、苹果文档和视频内容做二次创作。
本文分享自微信公众号 - 老司机技术周报(LSJCoding)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。