用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳!

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看421 回复回复5 收藏收藏2 分享淘帖 转播转播 分享分享2 微信
查看: 421|回复: 5
收起左侧

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳!

[复制链接]
1起来 发表于 2016-6-20 15:35:31 | 显示全部楼层 |阅读模式
快来登录
获取最新的苹果动态资讯
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码
本帖最后由 1起来 于 2016-8-2 13:25 编辑

在6月14日凌晨的WWDC2016大会上,苹果提出iOS10是一次里程碑并且推出了十个新特性,大部分的特性是基于iPhone自身的原生应用的更新,具体的特性笔者不在这里再次叙述,请看客们移步WWDC2016下载自行观赏,也可以查看敏捷大拇指纪要总结《苹果开发者大会纪要Swift 3.0 print("WWDC@2016");》。要说里程碑在笔者看来有些夸大其实了,不过新增的通知中心联动3D Touch确实为人机交互带来新的发展,另外一个最大的亮点在于Siri的接口开放。在iOS10中提供了SiriKit框架在用户使用Siri的时候会生成INExtension对象来告知我们的应用,通过实现方法来让Siri获取应用想要展示给用户的内容。

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 1

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 1


在iOS10之后,苹果希望Siri能够给用户带来更多的功能体验,基于这个出发点,新增了SiriKit框架。Siri通过语言处理系统对用户发出的对话请求进行解析之后生成一个用来描述对话内容的Intents事件,然后通过SiriKit框架分发给集成框架的应用程序以此来获取应用的内容,比如完成类似通过文字匹配查找应用聊天记录、聊天对象的功能,此外它还支持为用户使用苹果地图时提供应用内置服务等功能。通过官方文档我们可以看到SiriKit框架支持的六类服务分别是:

  • 语音和视频通话
  • 发送消息
  • 收款或者付款
  • 图片搜索
  • 管理锻炼
  • 行程预约


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

  • Resolve阶段。在Siri获取到用户的语音输入之后,生成一个INIntent对象,将语音中的关键信息提取出来并且填充对应的属性。这个对象在稍后会传递给我们设置好的INExtension子类对象进行处理,根据子类遵循的不同服务protocol来选择不同的解决方案
  • Confirm阶段。在上一个阶段通过handler(for intent: )返回了处理intent的对象,此阶段会依次调用confirm打头的实例方法来判断Siri填充的信息是否完成。匹配的判断结果包括Exactly one match、Two or more matches以及No match三种情况。这个过程中可以让Siri向用户征求更具体的参数信息
  • 在confirm方法执行完成之后,Siri进行最后的处理阶段,生成答复对象,并且向此intent对象确认处理结果然后执显示结果给用户看


用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 2

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 2


具体的执行过程请参考文档讲解视频




1、创建Intents Extension

SiriKit通过添加App Extension的方式来完成集成,这是一种独立于应用本身运行的代码结构,作为应用的扩展功能,只有在需要的时候系统会唤醒这些Extension代码来执行任务,然后在执行完毕之后将其杀死。另一方面,这些Extension在运行过程中的可占用内存是较少的,并且由于调用时机的限制,我们也无法在运行期间做一些坏事。

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 3

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 3


现阶段集成SiriKit的条件是需要将开发工具升级到Xcode8,需要使用开发者账号到官方网站去下载Xcode8_beta版,并且需要将一台测试设备升级到iOS10系统。选中我们的应用,进入项目总览界面,新增一个TARGET。

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 4

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 4


用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 5

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 5


如上图所示,我创建的Intents Extension被我命名为LXDSiriExtension。记住在创建好一个Extension的时候,会询问你是否激活这个扩展,勾选是。另外还会提示你是否连同Intents UI Extension一并创建了,我们同样选是。这样我们在项目下面总共创建了LXDSiriExtension和LXDSiriExtensionUI两个TARGET,这两个文件目录下面分别存在着一个新的info.plist文件,这个文件用来设置intent事件发生时我们设置的处理类。这里借用WWDC在讲解时的一张ppt来了解:

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 6

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 6


按图中的层次展开,IntentsSupported和IntentsRestrictedWhileLocked分别是两个字符串数组,每一个字符串表示的是应用扩展处理的intent事件的类名。前者表示支持的事件类型,后者表示在非锁屏状态下执行的事件类型。文件默认是workout类型的事件,在这里笔者改成了发送消息INSendMessageIntent。除此之外,NSExtensionPrincipalClass对应的是INExtension子类类名,这个类用来获取处理intent事件的类。

![plist设置]

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 7

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 7


另外,官方讲解中提到了Embedded frameworks,在session中苹果开发人员通过一个消息聊天应用来示例集成SiriKit。由于应用扩展自身的运行机制和应用本身的运行机制不同,彼此之间创建的类是不能访问使用的。因此把我们需要的类开发成frameworks的方式导入我们的应用之后就能够在两种之中都使用到这些类。本文未使用frameworks导入功能,而是模拟了一个类用来管理事件处理过程中的部分逻辑,但是Embedded frameworks这个使用的准则需要记住。这个模拟类的具体代码如下:

[Swift] 纯文本查看 复制代码
import Intents

class LXDMatch {
    var handle: String?
    var displayName: String?
    var contactIdentifier: String?

    convenience init(handle: String, _ displayName: String, _ contactIdentifier: String) {
        self.init()
        self.handle = handle
        self.displayName = displayName
        self.contactIdentifier = contactIdentifier
    }

    func inPerson() -> INPerson {
        return INPerson(handle: handle!, displayName: displayName, contactIdentifier: contactIdentifier)
    }
}

class LXDAccount {
    private static let instance = LXDAccount()

    private init() {
        print("only call share() to get an instance of LXDAccount")
    }

    class func share() -> LXDAccount {
        return LXDAccount.instance
    }

    func contact(matchingName: String) -> [LXDMatch] {
        return [LXDMatch(handle: NSStringFromClass(LXDSendMessageIntentHandler.classForCoder()), matchingName, matchingName)]
    }

    func send(message: String, to recipients: [INPerson]) -> INSendMessageIntentResponseCode {
        print("Send a message: \"\(message)\" to \(recipients)")
        return .success
    }
}


在完成这些需要的工作之后,我们还需要对应用本身的Info.plist配置文件进行设置,新增一个关键字为NSSiriUsageDescription的字符串对象,对应填写的字符串将在我们征询用户Siri权限的时候显示给用户看。比如Siri想要访问您的应用信息之类的提示语。然后通过INPreferences类方法向用户请求Siri访问权限

[Swift] 纯文本查看 复制代码
import Intents

INPreferences.requestSiriAuthorization {
    switch $0 {
    case .authorized:
        print("用户已授权")
        break

    case .notDetermined:
        print("未决定")
        break

    case .restricted:
        print("权限受限制")
        break

    case .denied:
        print("拒绝授权")
        break
    }
}





2、代码实现

首先我们需要一个INExtension的子类,你也可以在默认创建的子类中实现代码。在方法中,我们通过判断intent的类型来创建对应的处理者实例,然后返回。在本文的示例中,假设我们对Siri说出这么一句话 Siri,在微信上告诉我的家人们今天我不回去吃饭了:

[Swift] 纯文本查看 复制代码
class LXDIntentHandler: INExtension {
    override func handler(for intent: INIntent) -> AnyObject? {

        if intent is INSendMessageIntent {
            return LXDSendMessageIntentHandler()
        }
        //  这里可以判断更多类型来返回
        return nil
    }
}


通过判断intent事件是发送消息的聊天事件后,笔者创建了一个用来处理事件的LXDSendMessageIntentHandler类对象,并且返回。在对象创建完成之后需要完成Resolve、Confirm和Handle三个步骤,具体操作需要子类遵循实现INSendMessageIntentHandling协议来完成:



2.1、Resolve阶段

这个阶段需要我们找到消息的具体接收者。在这个过程中,可能会出现三种情况:Exactly one match、Two or more matches以及No matches,对于这三种情况的处理分别如下:

[Swift] 纯文本查看 复制代码
  func resolveRecipients(forSendMessage intent: INSendMessageIntent, with completion: ([INPersonResolutionResult]) -> Void) {
        if let recipients = intent.recipients {
            var resolutionResults = [INPersonResolutionResult]()
            for  recipient in recipients {
                let matches = LXDAccount.share().contact(matchingName: recipient.displayName)
                switch matches.count {
                case 2...Int.max:    //两个或更多匹配结果
                    let disambiguations = matches.map { $0.inPerson() }
                    resolutionResults.append(INPersonResolutionResult.disambiguation(with: disambiguations))
                break

                case 1:  //一个匹配结果
                    let recipient = matches[0].inPerson()
                resolutionResults.append(INPersonResolutionResult.success(with: recipient))
                    break

                case 0:  //无匹配结果
                    resolutionResults.append(INPersonResolutionResult.unsupported(with: .none))
                    break

                default:
                    break
                }
            }
            completion(resolutionResults)
        } else {
            //未从用户语音中提取到信息,需要向用户征询更多关键信息
            completion([INPersonResolutionResult.needsValue()])
        }
    }


上面的代码用来确认出消息中的我的家人们指代的是哪些人,其中每个联系人最终用一个INPerson的对象来表示。接着我们需要匹配消息的内容:

[Swift] 纯文本查看 复制代码
  func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Void) {
      if let text = intent.content where !text.isEmpty {
          completion(INStringResolutionResult.success(with: text))
      } else {
          //向用户征询发送的消息内容
          completion(INStringResolutionResult.needsValue())
      }
  }


在匹配完消息接收者跟消息内容之后,对于intent事件的处理就会进入第二阶段Confirm确认值是否正确。



2.2、Confirm阶段

在这个阶段intent对象本身的信息预计是已经完成填充的,我们通过获取这些填充值来判断是否符合我们的要求。同时在这个阶段,Siri会尝试唤醒应用来准备完成最后的处理操作。前面说了为了保证在应用和应用拓展之间能够进行通信,最好使用frameworks的方式来标记应用是否被启动,再进行相应操作。

[Swift] 纯文本查看 复制代码
  func confirm(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) {
        /// let content = intent.content
        /// let recipients = intent.recipients
        /// do or judge in content & recipients
        completion(INSendMessageIntentResponse(code: .success, userActivity: nil))
        /// Launch your app to do something like store message record
        /// Use a singleton in frameworks to remark if the app has launched
        /// if not launched, use the code following
        /// completion(INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: nil))
    }


Confirm阶段是我们最后可以尝试修改intent事件中传递的数值的时候。要记住一点,完全精确的内容固然是最好的答案,但是过多的让Siri询问用户参数的详细信息也会导致用户的抵触。



2.3、Handle阶段

Handle阶段不需要做太多额外的工作,判断一下消息接收者或消息内容是否存在,如果存在,执行类似保存/发送的工作,然后完成。否则告诉Siri本次的intent事件处理处理失败,我们还可以通过配置NSUserActivity对象来告诉Siri失败的原因。

[Swift] 纯文本查看 复制代码
  func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) {
      if intent.recipients != nil && intent.content != nil {
          /// do some thing success send message
          let success = LXDAccount.share().send(message: intent.content!, to: intent.recipients!)
          completion(INSendMessageIntentResponse(code: success, userActivity: nil))
      } else {
          let userActivity = NSUserActivity(activityType: String(INSendMessageIntent))
          userActivity.userInfo = [NSString(string: "error") : String("AppDidNotLaunch")]
          completion(INSendMessageIntentResponse(code: .failure, userActivity: userActivity))
      }
  }





3、事件UI

可以看到上面的代码主要集中在事件处理的逻辑上,那么在和Siri交互的过程中,我们同样可以让Siri展示响应的自定义界面:

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 8

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 8


在我们创建Intents Extension的时候,同时Xcode也询问我们是否创建Intents UI Extension。在后者的文件目录下也有一个Info.plist,有着跟前面类似的键值对,差别在于后者只有一个状态的设置。

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 9

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! - 敏捷大拇指 - 用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳! 9


在这个文件目录下存在一个故事板MainInterface,这个故事板就是Siri和应用交互时展示给用户看的界面。通过修改这个故事板的界面元素,就可以实现上图中的效果了。此外,在这个界面将要展示之前,我们可以修改类文件中的代码完成界面信息填充的操作:

[Swift] 纯文本查看 复制代码
func configure(with interaction: INInteraction!, context: INUIHostedViewContext, completion: ((CGSize) -> Void)!) {
    //这里执行界面设置的代码,完成之后执行completion代码就会让界面展示出来
    if let completion = completion {
        completion(self.desiredSize)
    }
}

var desiredSize: CGSize {
    return self.extensionContext!.hostedViewMaximumAllowedSize
}





4、尾言

在观看WWDC2016的新特性的时候,最开始给Siri和应用的交互惊艳到了。但是后来阅读文档发现这种交互仍然存在着过多的限制,整体而言并没有对Siri的使用带来更明显的提升。但是毫无疑问,这种交互如果苹果能继续对其进行补充发展,可以给我们的应用带来更多的新活力。




相关内容

用SiriKit框架开发了一个Swift应用,快来一起尝鲜Siri的惊艳!

SiriKit 编程指南 SiriKit Programming Guide




作者:林欣达,iOS民工一枚

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 1金钱 +10 贡献 +10 专家分 +10 收起 理由
Anewczs + 10 + 10 + 10 32个赞!专家给力!

查看全部评分

本帖被以下淘专辑推荐:

广告主 发表于 2016-6-20 16:54:14 | 显示全部楼层
想象一下Siri做的广告
JoyceChu 发表于 2016-6-20 17:18:57 | 显示全部楼层
动作这么快!学习了~
我是90后 发表于 2016-6-20 17:22:29 | 显示全部楼层
我司应用还不在这6个服务范围内,期待尽快放开
baddy 发表于 2016-6-21 14:06:14 | 显示全部楼层
跟着摸了一把,不错~
小开开 发表于 2016-6-21 14:12:34 | 显示全部楼层
值得收藏分享!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表