##SiriKit 有关介绍

引言

  • 在6月14日凌晨的WWDC2016大会上,苹果提出iOS10是一次里程碑并且推出了十个新特性,homekit、messageapp等等,大部分是基于iPhone原生应用的更新。其中最大的亮点之一是Siri的接口开放,在iOS10中提供了SiriKit框架在用户使用Siri的时候生成INExtension对象来告知我们的应用,我们可以通过SiriKit提供的API展示给用户更多的内容。
  • Siri通过语言处理系统对用户发出的对话请求进行解析之后生成一个用来描述对话内容的Intents事件,然后通过SiriKit框架分发给集成框架的应用程序以此来获取应用的内容,比如通过文字匹配查找应用聊天记录、聊天对象等功能,此外还支持为用户使用苹果地图时提供应用内置服务等功能。

SiriKit六类服务

sirikit 服务 对应的INintent
语音和视频通话 VoIP calling INStartVideoCallIntent、INStartAudioCallIntent
发送消息 Messaging INSendMessageIntent
收款或者付款 Payments INSendPaymentIntent、INRequestPaymentIntent
图片搜索 Photo search INSearchForPhotosIntent
管理锻炼 Workouts INEndWorkoutIntent、INPauseWorkoutIntent 、INStartWorkoutIntent 、 INResumeWorkoutIntent 、INCancelWorkoutIntent
行程预约 Ride booking INRequestRideIntent、INGetRideStatusIntent、 INListRideOptionsIntent、 INGetRideStatusIntent

更加详细的说明参见Intents Domains
也就是说当我们使用sirikit的时候,可以使用以上列表对应的六种服务,比如说你的应用是联系人可以发送消息,这时候你就可以使用INSendMessageIntent服务,在锁屏或者主屏幕唤起siri,然后比方说说出“嘿,siri,在xxx应用上发送一条消息给剑剑”,然后siri就能响应并同时提供回调给你的应用siri extension。具体的流程我们会在下一节举例说明。

Siri执行流程

  • 示例流程图
siri1.png
siri2.png
  • 获取词汇逻辑


    siri_cihui.png

    从词汇中可以看到intent(暂且翻译成意图?)对应着siri给我们的响应,这是app通过处理intent对应的reslove,confirm,handle来做相关响应。

intents

1.intents说明

  • Action to be performed 定义将要响应的动作,即reslove,confirm,handle
  • Zero-to-many parameters 意图会有对应的参数来响应某些事件
  • Classified into a domain 意图划分的种类

Siri通过Intents extension的扩展方式和我们的应用进行交互,其中INExtension扮演着Intents extension扩展中直接协同Siri共同响应用户的角色。当我们实现了Intents extension扩展并产生了一个Siri请求事件时,Intents事件的处理过程分Resolve、Confirm和Handle三个步骤。

2.LifeCycle for an intent

一个intent的完整周期如下图:

siri_life.png
  • Resolve阶段。在Siri获取用户的语音输入之后,生成一个INIntent对象,将语音中的关键信息提取出来并且填充对应的属性,该对象会传递给我们设置好的INExtension子类对象进行处理,根据子类遵循的不同protocol来选择不同的解决方案。在上一个阶段通过handler(for intent:)返回了处理intent的对象,此阶段会依次调用confirm打头的实例方法来判断Siri填充的信息是否完成。
    resolve阶段用户大致分为以下说明阶段:

助SIri明白用户的含义
影响Siri的行为

提供resolution response
successWithResolvedPerson:成功找到匹配的人
disambiguationWithPeopleToDisambiguate:还需要挑选
confirmationRequiredWithPersonToConfirm:还需要确认下
needMoreDetailsForPerson:还需要更具体的信息,需要Siri进行询问
unsupportedWithReason:无法使用指定值
needsValue:需要某些必需值
notRequired:应用并没有要求某些值

  • Confirm阶段。确认信息。检查必要的状态等。Siri进行最后的处理阶段,生成答复对象,并且向此intent对象确认处理结果。

告诉Siri预期结果
检查必要的状态
提供Intent response
Siri提供必要的确认提示,参考如下连图例

siri3_confirm.png
siri_confirm2.png
  • Handle阶段:在Confirm方法执行完成之后,然后显示结果给用户看

    执行请求操作
    提供有关结果足够精确的信息
    如果结果耗时的话还可提供loading

siri_handle.png

实现一个Siri Kit应用

  • 升级到Xcode8,一台升级到iOS10的测试设备
  • Intents extension : resolve、confirm、handle流程
    新建extension如下图:


    siri_ex2.png

通常SiriUI也会配置一起创建,入后配置info.plist文件


siri_info2.png

上图中IntentsSupported是指你的扩展支持的intent类型,如果你的代码想支持某种intent必须在这里配置。
IntentsRestrictedWhileLocked是指在锁屏状态下能用siri唤起的intent,如果你想在锁屏下能唤起siri的扩展功能必须配置。


siri_info.png

创建之后,查看plist文件
  • 1.增加xxxintent
  • 2.NSExtensionPrincipalClass,这里必须在plist声明,它是INExtension的子类,INIntentHandleProviding,handleForIntent,handleClass must conform to specific intent handling protocol等,详细说明参见Intents Domains
    主项目plist 增加NSSiriUsageDescription 这个是请求Siri权限时提示的文案
    使用Siri时,用户必须说出App的名字,也就是Bundle display name
sendmessage.png

以发送消息为例:当siri第一次确认的时候,需要应用授权,授权代码和提示如下图:

siri_auth.png

具体intent的声明周期代码参考苹果官方demo,代码示例如下:

// MARK: 1. Resolve
    func resolveRecipients(forSendMessage intent: INSendMessageIntent, with completion: ([INPersonResolutionResult]) -> Swift.Void) {
        
        
        if let recipients = intent.recipients {
            var resolutionResults = [INPersonResolutionResult]()
            
            for recipient in recipients {
                let matchingContacts = UCAddressBookManager().contacts(matchingName: recipient.displayName)
                
                switch matchingContacts.count {
                    case 2 ... Int.max:
                        // We need Siri's help to ask user to pick one from the matches.
                        let disambiguationOptions: [INPerson] = matchingContacts.map { contact in
                            return contact.inPerson()
                        }

                        resolutionResults += [INPersonResolutionResult.disambiguation(with: disambiguationOptions)]
                        
                    case 1:
                        let recipientMatched = matchingContacts[0].inPerson()
                        resolutionResults += [INPersonResolutionResult.success(with: recipientMatched)]
                        
                    case 0:
                        resolutionResults += [INPersonResolutionResult.unsupported(with: INIntentResolutionResultUnsupportedReason.none)]
                    
                    default:
                        break
                }
            }
            
            completion(resolutionResults)
            
        } else {
            // No recipients are provided. We need to prompt for a value.
            completion([INPersonResolutionResult.needsValue()])
        }
    }
        
    func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Swift.Void) {
        if let text = intent.content where !text.isEmpty {
            completion(INStringResolutionResult.success(with: text))
        }
        else {
            completion(INStringResolutionResult.needsValue())
        }
    }
    
    // MARK: 2. Confirm
    func confirm(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Swift.Void) {
        
        if UCAccount.shared().hasValidAuthentication {
            completion(INSendMessageIntentResponse.init(code: INSendMessageIntentResponseCode.success, userActivity: nil))
        }
        else {
            // Creating our own user activity to include error information.
            let userActivity = NSUserActivity.init(activityType: String(INSendMessageIntent))
            userActivity.userInfo = [NSString(string: "error"):NSString(string: "UserLoggedOut")]
            
            completion(INSendMessageIntentResponse.init(code: INSendMessageIntentResponseCode.failureRequiringAppLaunch, userActivity: userActivity))
        }
    }
    
    // MARK: 3. Handle
    func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Swift.Void) {
        if intent.recipients != nil && intent.content != nil {
            // Send the message.
            let success = UCAccount.shared().sendMessage(intent.content, toRecipients: intent.recipients)
            completion(INSendMessageIntentResponse.init(code: success ? .success : .failure, userActivity: nil))
        }
        else {
            completion(INSendMessageIntentResponse.init(code: INSendMessageIntentResponseCode.failure, userActivity: nil))
        }
    }

  • Intents UI extension 提供界面自定义等内容
    通常确认界面的UI苹果会提供默认的样式,默认的样式如下图:
style_default.png

但是如果你想自定义UI,如下图所示,你就必须配置Intents UI extension,在MainInterface.storyboard配置相关UI,然后在对应的控制器中配置config函数。

style_custom.png

参考configs函数如下:

func configure(with interaction: INInteraction!, context: INUIHostedViewContext, completion: ((CGSize) -> Void)!) {
        var size: CGSize
        
        // Check if the interaction describes a SendMessageIntent.
        if interaction.representsSendMessageIntent {
            // If it is, let's set up a view controller.
            let chatViewController = UCChatViewController()
            chatViewController.messageContent = interaction.messageContent

            let contact = UCContact()
            contact.name = interaction.recipientName
            chatViewController.recipient = contact
            
            switch interaction.intentHandlingStatus {
                case INIntentHandlingStatus.unspecified, INIntentHandlingStatus.inProgress,INIntentHandlingStatus.ready:
                    chatViewController.isSent = false
                case INIntentHandlingStatus.done:
                    chatViewController.isSent = true
            }
            
            present(chatViewController, animated: false, completion: nil)
            
            size = desiredSize
        }
        else {
            // Otherwise, we'll tell the host to draw us at zero size.
            size = CGSize.zero
        }
        
        completion(size)
    }

  • Embedded frameworks
    由于你的程序是extension,因此你的siri扩展可能会和你的应用有数据共享,而且可能会有同样的逻辑代码,因此apple建议相关业务的逻辑代码可用framwork的形式,这样扩展和宿主app都可以使用。

参考

推荐阅读更多精彩内容

  • 关于SiriKit 在6月14日凌晨的WWDC2016大会上,苹果提出iOS10是一次里程碑并且推出了十个新特性,...
    sindri的小巢阅读 17,784评论 40 76
  • 概览 最新的WWDC2016大会上,苹果提出iOS10并推出了十个新特性,homekit、messageapp等等...
    cuagain阅读 1,195评论 0 5
  • 介绍SiriKit SiriKit是让你的内容通过Siri展示的一个框架库。当用户向Siri请求特别类型的服务时,...
    孢子菌阅读 2,809评论 1 5
  • 在这里放出原文链接地址 Part 3.1: 分析与操作 Intents Intents Extension 的的入...
    李国安阅读 576评论 0 4
  • 一、SiriKit介绍 Siri是一款苹果 iOS 系统提供的智能语音助手软件,它的全名是 Speech Inte...
    Haofree阅读 3,641评论 15 17