Siri快速命令


Siri Shortcuts是在WWDC 2018上引入的有用的iOS 12功能之一(在我看来)。


快捷方式( shortcut )-快速命令,一种绕过标准脚本执行任何操作的快捷方式。


在您的应用程序中,您可以为某些操作捕捉快捷方式。 了解用户执行方式的方式以及何时执行它们,Siri会在正确的时间和地点明智地启动,​​为他提供这些快捷方式,最重要的是,用户将能够使用附加到他们的短语来调用它们! 下猫多。


如何运作


我们使用可以执行某些操作的应用程序,这些应用程序可以创建快捷方式并将其传递给系统。


您可以在“设置”→“ Siri和搜索”中查看这些快捷方式。

上面的屏幕截图显示了系统从不同应用程序捕获的最后三个快捷方式。 如果单击“更多快捷方式”按钮,我们将看到每个应用程序交付给系统的所有快捷方式。


通过在快捷方式创建代码中进行某些设置,Siri将在通知和搜索中心的锁定屏幕上为用户提供这些快捷方式,重点是我们使用这些操作的频率,时间,时间,星期几以及其他因素。


例如,如果您通常在星期五晚上寻找自动提款机,然后经过培训,Siri将在星期五晚上为您提供此操作的捷径。



如果单击“ + ”图标,则可以将语音命令添加到每个快捷方式。


我们说出语音命令,按“完成”,现在我们可以通过Siri使用语音执行快捷方式后面的操作。 事实证明,用户无需打开应用程序本身即可通过Siri执行应用程序的功能。 该短语的快捷方式保留在“我的快捷方式”中。


创建快捷方式


为了进行开发,我们将需要Xcode 10和iOS12。在编写本文时,它们都处于Beta阶段。


可以通过NSUserActivityIntent创建快捷方式。


第一种情况:


用户单击快捷方式,该快捷方式会将带有参数( NSUserActivity )的命令传递给我们的应用程序,并决定应如何处理此命令(打开当前USD汇率的窗口,或我们最喜欢的披萨的订购窗口)。 这是众所周知的很好的旧Spotlight快捷方式,但Siri巧妙地提供了这种快捷方式。


第二种情况:


通过Intent创建的快捷方式更有趣-它们使您无需启动应用程序即可在Siri界面中立即执行命令。 以前,这组Intent对Apple来说很难:转账,发送消息 。 现在,我们的开发人员有机会创建我们的Intent


无论快捷方式是如何创建的,它都会经历生命周期的三个阶段:


  1. 公告( 定义
  2. 交付给系统( 捐赠
  3. 按应用程序处理( 句柄


我的研究表明,一个应用程序最多可以为系统提供20个快捷方式。


此外,我们将考虑如何使我们的应用程序具有创建快捷方式的能力以及如何在其中使用快捷方式。


通过NSUserActivity创建快捷方式


让我们分析通过NSUserActivity打开的第一种简单类型的快捷方式。


例如,在移动银行应用程序中,我们有一个ATM搜索屏幕,我经常在寻找它们。 为了进入带有ATM卡的屏幕,我必须启动该应用程序,转到选项卡中的“更多”选项卡,选择“信息”部分,然后单击那里的“ ATM”按钮。
如果我们创建了可立即转到该屏幕的快捷方式,则当Siri将其提供给他时,例如在锁定屏幕上,用户将可以一键进入它。


声明快捷方式


第一步是在NSUserActivity声明一个类似于我们的NSUserActivity的类型(可以说这是它的标识符):


 <key>NSUserActivityTypes</key> <array> <string>ru.tinkoff.demo.show-cashMachine</string> </array> 

宣布了。


将快捷方式传递给系统(捐赠)


声明之后,我们可以在应用程序的代码中使用上面在info.playlist中设置的类型创建NSUserActivity


 let activity = NSUserActivity(activityType: "ru.tinkoff.demo.show-cashMachine") 

为了使活动进入系统快捷方式列表,需要将其设置为title ,并将isEligibleForSearch属性设置为true 。 其他属性对于添加到快捷方式不是必需的,但是它们的存在使快捷方式更具可读性和用户友好性。


  //   (     ,   ) activity.title = " " if #available(iOS 12.0, *) { // ,             activity.suggestedInvocationPhrase = "  " //           activity.isEligibleForPrediction = true // (     ,   ) activity.isEligibleForSearch = true } //       let attributes = CSSearchableItemAttributeSet(itemContentType: "NSUserActivity.searchableItemContentType") ///    if let image = UIImage(named: "siriAtmIcon") { attributes.thumbnailData = UIImagePNGRepresentation(image) } ///   attributes.contentDescription = "     " ///     activity.contentAttributeSet = attributes 

火! NSUserActivity是将其交付给系统的最后一步。


ViewConroller具有userActivity属性,我们需要向其分配上面创建的activity


 self.userActivity = activity 

一旦执行了此行,就会从该活动中创建一个快捷方式。 它会被传送到系统并显示在Siri设置中( 设置→Siri和搜索 )。 然后,Siri将能够将其提供给用户,并且用户将能够为其分配语音命令。


注意 :Apple文档说,无需将活动分配给视图控制器,只需在becomeCurrent()上调用becomeCurrent()方法即可。 但是,此操作未将活动传递给我的系统,并且快捷方式未出现在列表中


接下来,调用用户活动对象上的becomeCurrent()方法以将其标记为当前,该活动将活动捐赠给Siri。 或者,您可以将对象附加到UIViewController或UIResponder对象,这也将活动标记为当前。

要检查一切是否正常,请打开“设置”>“ Siri”并搜索 -基于我们活动的快捷方式应在列表中。


按应用程序处理快捷方式(句柄)


当用户从通知中心浏览快捷方式或通过语音激活快捷方式时,应用程序将启动,我们必须处理此快捷方式。


activity AppDelegate方法抛出给我们:


 func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if userActivity.activityType == "ru.tinkoff.demo.show-cashMachine" { //   ,     handleShowCashMachineActivity() return true } return false } 

合计


NSUserActivity NSUserActivity创建如下:


  1. NSUserActivity声明NSUserActivity的类型(标识符)。
  2. 我们在代码中创建NSUserActivity并配置
  3. viewController'

从应用程序创建语音命令


因此,如果用户打开“设置”>“ Siri”并进行搜索 ,他将看到由各种应用程序(包括我们的应用程序)创建的快捷方式列表。 通过单击“ + ”,用户可以创建任何语音命令并将其与选定的快捷方式关联。 然而,每次输入设置对于用户来说都是不方便的,许多人甚至没有意识到这种可能性。


您可以直接在应用程序内部将语音命令附加到特定操作,这很酷。


假设用户执行了某项操作并将其传递到系统,他想保存该操作。 我们可以在应用程序的屏幕上添加按钮“ 将动作添加到Siri ”(您可以随意命名和绘制按钮),然后用户单击它就可以将该动作与应用程序中的语音命令相关联,而无需进行设置。


通过单击按钮,应该已打开用于将语音命令添加到Siri INUIAddVoiceShortcutViewController的快捷方式的屏幕,或用于编辑语音命令INUIEditVoiceShortcutViewController的屏幕(如果已创建)。 这种按钮的未反应action大致如下:


  @IBAction func addToSiriAction() { // 1.   ,       INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (shortcuts, error) in guard error == nil, let shortcuts = shortcuts else { // TODO: Handle error return } // 2.     ,      let donatedShortcut: INVoiceShortcut? = shortcuts.first(where: { (shorcut) -> Bool in return shorcut.__shortcut.userActivity?.activityType == "com.ba" }) if let shortcut = donatedShortcut { // 3.     -     . //         let editVoiceShortcutViewController = INUIEditVoiceShortcutViewController(voiceShortcut: shortcut) editVoiceShortcutViewController.delegate = self self.present(editVoiceShortcutViewController, animated: true, completion: nil) } else { // 4.         let shortcut = INShortcut(userActivity: self.userActivity!) let addVoiceShortcutViewController = INUIAddVoiceShortcutViewController(shortcut: shortcut) addVoiceShortcutViewController.delegate = self } } } 

因此,用于添加和编辑Siri快捷方式的语音命令的屏幕如下所示:



我们还必须实现这些viewController的委托方法,在这些方法中,它们需要隐藏dismiss(animated: true, completion: nil)并在必要时更新当前屏幕。 例如,如果较早的屏幕上有一个“添加语音命令”按钮,则在添加语音命令后,该按钮应消失或变为“编辑语音命令”。


意向捷径


到目前为止,我们仅讨论了打开应用程序并将其中的NSUserActivity数据传递给NSUserActivity快捷方式。


但是回到通过Intent创建的快捷方式,它们使您无需打开应用程序即可执行某些操作。 从这里开始乐趣。


想象一个用户订购他最喜欢的披萨。 他会在需要时多次订购该产品,甚至在该披萨的快捷方式中添加了语音命令-这简化了他的生活。 但是我们可以为他做更多的事情-我们可以通过发出Siri语音命令来确保系统不会将其扔到应用程序中,而是立即在Siri界面中显示订单信息和比萨饼订单! 当用户不需要打开应用程序本身来执行某些操作时,就是这种情况。


首先,转到项目设置,选择主要目标,“ Capabilities选项卡并启用对Siri的访问。


我们的应用程序可以与Siri交互,但这不会在应用程序的主代码中发生,而是在单独的target-extensions中扩展。


首先,必须创建此目标: 文件→新建→目标 ,选择意图扩展 。 Xcode将为在Siri中显示您的操作的窗口创建另一个目标扩展,如果有需要,我们表示同意。



声明快捷方式


iOS 12中SiriKit的主要创新之处在于能够创建您的Inetnts ,而以前的Inetnts则没有。



为此,请创建一个新文件: File→New→File ,从Resource部分中选择SiriKit Intent Definition File



结果, 出现扩展名为.intentdefinition的文件,您可以在其中创建自己的Intents 。 我们打开文件,文件底部显示“ No Intents ”( 无意图 ),然后单击“ + ”图标。 “ 新意图 ”。 意向将出现在列表中,您可以在其中添加参数。 如果要订购披萨,则可以添加订购的披萨数量和披萨类型作为参数。 对于数量,我们选择Integer类型,对于披萨类型,我们选择Custom类型,这在代码中将由INObjectINObject


现在有几行沮丧:


用户将无法将不同的参数传输到同一已保存的语音命令。 las!



什么是参数:


假设您创建一个实体“ Show rate %currency ”,其中currency是一个实体参数。 这并不意味着用户可以说短语“显示美元汇率”,“显示比特币汇率”等。 开箱即用,这将无法正常工作。 但这意味着,如果用户查看美元汇率,则会创建“显示美元汇率”快捷方式,然后,当他查看比特币汇率时,将创建“显示BTC汇率”快捷方式,依此类推。 换句话说,他可能有几个基于相同意图但具有不同参数的Shorkata。 每个快捷方式,用户将能够询问他的语音命令。


好了,通过在.intentdefinition文件中创建一个意图, Xcode会自动为该意图生成一个类(注意:它不会出现在项目文件中,但可以使用)。这个自动生成的文件将仅在拥有.intentdefinition文件的那些目标中。


在.intentdefinition文件中创建了意图之后我们可以在代码中创建意图。


 let intent = OrderPizzaIntent() 

将快捷方式传递给系统(捐赠)


为了将此实体包含在快捷方式列表中,您需要将其嵌入。 为此,将使用您的意图实例创建一个INInteraction对象,并在此.donate调用.donate方法


 let intent = OrderPizzaIntentf() // ...   let interaction = INInteraction(intent: intent, response: nil) interaction.donate { (error) in // ...   /  } 

执行此代码后,基于意图的快捷方式将被传送到系统并显示在Siri设置中。


我们处理快捷方式应用程序(句柄)


下一步是当用户在Siri的最上端单击该意图或使用语音命令对其进行调用时,对其进行处理。


我们已经为Siri创建了一个目标扩展,它具有一个预先创建的IntentHandler类,该类具有一个单一方法-``handle(for intent)``


  class IntentHandler: INExtension { override func handler(for intent: INIntent) -> Any { guard intent is OrderPizzaIntent else { fatalError("Unhandled intent type: \(intent)") } return OrderPizzaIntentHandler() } } 

注意:如果编译器看不到您的意图类,则您尚未为Siri添加.intentdefinition目标扩展文件。

在此方法中,我们确定传入意图的类型,并为每种类型创建一个处理该意图的处理程序对象。 为我们的OrderPizzaIntent创建一个处理程序,并在其中实现OrderPizzaIntentHandling协议,该协议在OrderPizzaIntentHandling中创建了Intent后已经自动生成。


该协议包含confirmhandle两种方法。 首先,在所有数据都被检查并检查动作的可用性的地方调用确认。 然后handle将在短时间内动作。


 public class OrderPizzaIntentHandler: NSObject, OrderPizzaIntentHandling { public func confirm(intent: OrderPizzaIntent, completion: @escaping (OrderPizzaIntentResponse) -> Void) { //      // ... completion(OrderPizzaIntentResponse(code: OrderPizzaIntentResponseCode.ready, userActivity: nil)) } public func handle(intent: OrderPizzaIntent, completion: @escaping (OrderPizzaIntentResponse) -> Void) { //     // ... completion(OrderPizzaIntentResponse(code: OrderPizzaIntentResponseCode.success, userActivity: nil)) } } 

这两个方法都必须使用OrderPizzaIntentResponse响应(它也是自动生成的)来调用completion OrderPizzaIntentResponse ,否则Siri将等待很长时间然后给出错误。


来自Siri的更多详细答案


有一个标准的,自动生成的响应代码集- enum OrderPizzaIntentResponseCode ,但是对于友好的界面enum OrderPizzaIntentResponseCode ,它们可能还不够。 例如,在confirm阶段,可能会发生几个不同的错误-披萨已用完,比萨店此时不工作,等等。 用户应该了解这些事实,而不是标准消息“应用程序错误”。 还记得我们在.intentdefinition文件中创建了Intent吗? 连同意图本身一起, Response了它的Response ,您可以在其中添加自己的错误和成功答案选项,并使用参数进行配置:



现在我们可以告诉用户更多有用的错误和答案:


 public func confirm(intent: OrderPizzaIntent, completion: @escaping (OrderPizzaIntentResponse) -> Void) { guard let pizzaKindId = intent.kind?.identifier else { //         -     completion(OrderPizzaIntentResponse(code: .failure, userActivity: nil)) return } if pizzeriaManager.isPizzeriaClosed == true { ///     -     completion(OrderPizzaIntentResponse(code: .failurePizzeriaClosed, userActivity: nil)) return } else if pizzeriaManager.menu.isPizzaUnavailable(identifier: pizzaKindId) { ///      -     completion(OrderPizzaIntentResponse(code: .failurePizzaUnavailable(kind: intent.kind), userActivity: nil)) return } //     -    completion(OrderPizzaIntentResponse(code: .ready, userActivity: nil)) } 

Intent渲染


如果创建了Intent Extension UI目标扩展,则可以在Siri中为所需的意图绘制自定义视图。 我们有MainInterface.storyboardIntentViewController在其中可以草绘它们的设计。 该视图控制器实现INUIHostedViewControlling协议,并且该视图在configureView方法中进行configureView


 // Prepare your view controller for the interaction to handle. func configureView(for parameters: Set<INParameter>, of interaction: INInteraction, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set<INParameter>, CGSize) -> Void) { // Do configuration here, including preparing views and calculating a desired size for presentation. completion(true, parameters, self.desiredSize) } var desiredSize: CGSize { return self.extensionContext!.hostedViewMaximumAllowedSize } 

要调用此方法,您需要将我们的意图名称添加到IntentsSupported的数组NSExtension > NSExtensionAttributes > IntentsSupported该数组引用扩展目标Intents UI


 <key>NSExtension</key> <dict> <key>NSExtensionAttributes</key> <dict> <key>IntentsSupported</key> <array> <string>OrderPizzaIntent</string> </array> </dict> 

根据Siri中视图的设计以及该方法中的interaction.intent ,可以按所需方式绘制此视图。 以下是我们在搜索和锁定屏幕上使用Siri的意图时的屏幕截图。



值得考虑的是,用户将无法与视图上的按钮,滚动和其他控件进行交互,因为该方法是使用参数interactiveBehavior = .none ,因此这肯定会带来许多限制。


合计


基于Intent的快捷方式可以在siri界面或通知中心中呈现,并且无需打开应用程序即可执行操作。 要创建它,您需要:


  1. 启用使用Siri的功能
  2. 创建意图扩展意图扩展UI
  3. 创建SiriKit意向定义文件
  4. 我们在此文件中创建Intent并为其分配参数。
  5. 创建一个IntentHandler在其中我们实现hanldehanlde

推荐建议


Siri扩展目标和主要应用程序中的通用代码


如果您的代码同时在Siri的目标和主项目的目标中使用,则有两种方法可以解决此问题:


  1. 突出显示通用类,将它们添加到两个目标中。 ( 查看→实用工具→显示文件检查器 'e,在“ 目标成员身份”部分中,向需要访问所选文件的目标添加复选标记)
  2. 创建一个或多个目标框架,然后在此处获取常规代码。

后一种方法比较可取,因为您可以在其他扩展和项目中使用这些框架。 还值得注意的是,对于这些框架,建议设置Allow app extension API only标志,然后在开发框架时,如果您尝试使用在开发扩展中非法的API(例如UIApplication ),则编译器会发誓。


可以通过应用组在目标之间翻阅共享资源


侦错


测试快捷方式将有助于:


  1. 电话设置设置→开发人员在锁定屏幕开关 显示最近的快捷方式显示捐赠


  1. 要测试Intens,您可以通过在Xcode中指定Siri打开时使用的短语来立即启动目标扩展。 为此,请为目标扩展Siri选择方案


单击此目标,单击编辑方案...



Siri Intent Query字段中,输入一个短语,Siri将以该短语开始,就像您已经说过的一样。


合计


我建议停止并总结我们所做的事情:


  1. 可以通过NSUserActivityINIntent创建快捷方式
  2. 快捷方式需要声明(声明),报告给系统(捐赠)和处理(处理)。
  3. 您可以将“ 添加到Siri ”按钮添加到应用程序,方法是单击该按钮,用户可以在其中添加该动作的短语,然后用他的声音对其进行调用。
  4. 除了内置之外,您还可以创建自己的Intents
  5. 通过基于Intent的Intents您可以创建将通过Siri界面执行的操作(在锁定屏幕上或在搜索中),而无需打开应用程序本身。

在Apple文档中,有一个指向Demo项目的链接,该链接对于在开发过程中进行下载和重点关注很有用。


我想强调的是,在撰写本文时,它是处于beta阶段的API。 而且我经常发现问题和错误。 在工作期间,我定期遇到以下问题:


  • , Intent Siri, .
  • Siri .
  • Siri.

参考文献


  1. WWDC 2018, session 211: Introduction to Siri Shortcuts
  2. WWDC 2018, session 214: Building for Voice with Siri Shortcuts
  3. Apple Developer: SiriKit
  4. Apple Developer: INUIHostedViewControlling
  5. Demo Soup Chef Apple

Source: https://habr.com/ru/post/zh-CN414677/


All Articles