SiriKit测试全攻略
需求来源:
我的iwatch上安全的楼兰宝盒,平时虽然可以用语音来打开这个app,但是打开后主界面上的按钮操作实在是蛋疼,这不仅仅是这个app的问题,iwatch操作只是面实在是小,用手指望点击很容易误按,本来想启动车了,有时却会按了开锁窗。于是想到用语音来控制 ,但是Siri只能打开主程序,无法和应用程序中功能进行交互,把这个意见提给楼兰宝盒的开发者,回复是暂时实现不了。于是我抽空对SiriKit做了一番测试。
测试过程:
关于基本SiriKit的介绍,网上有太多的介绍,www.cocoachina.com上和developer.apple.com都可以搜索到,基本原理的生命周期等不用再介绍了。在我的调试过程中可以证实,很多文章都是浅尝辄止,或者拿别人的文章改来的,有的文章中引用的代码竟然一段是objectc,一段是swift,关键点几乎没有说清楚的。
我直接写测试过程,关键点我会详细说明我测试的过程。
环境:mac os 10.12.4 Beta 4/xcode 8.3 beta4/iphone7/IOS 10.3 beta 4
因为不同版本的swift语法和类库不兼容,很多已经不存在方法需要自己用其它方法代替。
一.新建一个single view application,在info中把localiztion改成china。(很多文章中提示要改很多info.plist文件,其实默认的设置已经支持。)
在Capabilites中把Siri权限打开:
在主视图中加一个label,内容随意。编译,在真机上运行,使这个初始的app部署到真机上(希望你看到主视图上的Label内容能完全显示)。
点击TARGET左下角的+弹出新的TARGET选择窗口,选中IntentsExtension
给它起个名字同时选中IntentsUI Extension,完成。同时把localiztion都设成China
现在进入编码阶段,按照文档介绍,IntentsExtension只支持6种场景,但是实际上,只要其中的SendMessage协议,就可以完成各种功能,你把要输入的内容放在消息体中,然后在应用中解析消息体,什么样的参数完成不了呢?比如我想解决的问题就是打开车助理这个应用然后接受“启动”,“停止”,“开门窗”,“锁门窗”这些命令,你只要判断消息体中的内容对应到调用应用中的各个功能就行了。
另外SendMessage协议的recipients参数还可以帮助你准确校验命令目标,也就是说你可以确认当前Siri的交互是发给你这个应用中存在的联系人的。
关于INVocabulary关键词,为了帮助siri准确识别,把关键词放在INVocabulary中是一个好方法,特别是发送目标。为了准确判断Siri是否识别出发送目标,你应该起一个通讯录中不存在的并且发音识别度高的关键词放在INVocabulary止境中,如我为车助理这个应用内置了两个消息接受者:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//内置联系人
let orderSet = NSMutableOrderedSet()
orderSet.add("助理小妹")
orderSet.add("助理小哥")
INVocabulary.shared().setVocabularyStrings(orderSet, of:.contactName)
//请求Siri的访问权限
INPreferences.requestSiriAuthorization{
authorizationStatus in
switch authorizationStatus {
case .authorized:
print("Authorized")
default:
print("Not Authorized") //弹出窗口提醒用户去 设置->Siri->允许的程序->打开车助理的对Siri权限
}
}
return true
}
编译重新运行主程序。
对于其它事件是否要处理要看每个应用的需求,我这个应用就是要接受Siri发给应用的内容,所以在IntentsExtension说可以发送了吗?你说发送。
SiriIntentsUIExtension就会调用handle方法,你在获取语音捕获的内容就在这里处理,Apple建议是,因为你的
IntentsExtension的中调用的功能绝大多数在主程序中同样会调用,所以建议你把这段逻辑代码写在EmbeddedFramework中,当然这是代码组织的问题,在测试阶段我们不太关注。
所以我们直接在handle中实现:
func handle(sendMessage intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
// Implement your application logic to send a message here.
var dict:[String:String] = Dictionary()
if let rs = intent.recipients, rs.count > 0{
let person = rs[0]
dict["name"] = person.displayName
}
if let text = intent.content,!text.isEmpty{
dict["message"] = text
}
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
userActivity.userInfo = dict
userActivity.becomeCurrent()
print("向\(dict["name"]!)发消息:\(dict["message"]!)")
if (dict["message"]!.range(of: "测试失败") != nil) { // 利用userActivity与主程序交互数据
let response = INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
completion(response)
}else{
let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity)
completion(response)
}
}
这里有个关键点,如果你仅在INVocabulary中添加了关键词“助手小妹”,在你说“车助理发消息给助手小妹”时,可以帮助Siri准确分析语法结构,但是如果通讯录中没有助手小妹这个联系人,
intent.recipients[0]这个对象中所有字段是空的,在默认的
IntentsUIExtension交互界面上也不会显示To:的内容,如果你不介意接受人,只介意消息内容,可以不要建立一个真实的通讯录,但这样仍然有意义,Siri在分析“车助理发消息给助手小妹”时可以准确地从INVocabulary关键词中找到并知道它是一个宾语而不是内容。你可以在测试时把这个关键词从INVocabulary中去掉,试一下识别率。
真实的处理过程仅是这一句:
print("向\(dict["name"]!)发消息:\(dict["message"]!)"),你在调试时如果看到控制台显示这一行,说明它已经正确工作,这和你调用任何启动命令的行为没有区别。
调试关键点:要调用handle方法,你应该选择IntentsExtension,而不是
IntentsUIExtension,也不是主程序。才能看到print的输出,你要看哪个target的输出就要选择哪
target调试。调试主程序时,主程序在手上打开后,自己调出Siri,调
IntentsExtension和
IntentsUIExtension时,选择连接主程序。
关于
NSUserActivity,很多人不知道SiriKit中
NSUserActivity如何和主程序交互,也没有一篇文章中写到,其实
NSUserActivity的功能和Handoff中一样,只是
IntentsUI如何转到主视图的问题。
上面所有的事件中,只要你告诉
IntentsUIExtension当前处理失败,返回
.failureRequiringAppLaunch这个code,
IntentsUI就会把控制权交给主程序来处理,这时
NSUserActivity就可以用来传递上下文了,上面的例子中,我专门加了一断测试逻辑,只要你发送的内容是“测试失败”,Siri就会让你打开主程序处理:
要在主程序中接受
NSUserActivity上下文,需要在
AppDelegate中增加相应的委托函数:
//Siri发送失败会打开APP处理会调用些方法。
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
let alert = UIAlertController();
alert.title = "Handle userActivity"
let dict:[String:String] = userActivity.userInfo as! Dictionary
alert.message = "name: \(dict["name"]!) \r\n message: \(dict["message"]!)"
alert.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.cancel, handler: nil))
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
return true
}
我在这里只是把这handle中捕获的内容弹出来了:
其它的调试注意点:我为IntentsUI定制了一个界面,默认的界面是保留IntentsUIExtension中留给你的(用户界面),然后在下面绘制系统默认界面进行交互。很难看,所以我把系统默认隐藏了,只要让
IntentViewController类继承INUIHostedViewSiriProviding并实现
var displaysMessage: Bool {
return true
}这个代理方法,就可以隐藏默认系统界面,然后我在用户界面上自己加了控件显示交互内容:
自定义界面的代码:
// Prepare your view controller for the interaction to handle.
func configure(with interaction: INInteraction!, context: INUIHostedViewContext, completion: ((CGSize) -> Void)!) {
// Do configuration here, including preparing views and calculating a desired size for presentation.
let intent = interaction.intent as! INSendMessageIntent
name.text = intent.recipients?[0].displayName
messageText.text = intent.content
if let completion = completion {
completion(self.desiredSize)
}
}
把交互介面底色改成蓝色,防止在调试时误识别,如果你说“车助理发消息给助手小妹”,“车助理”三个字说早了,Siri只识别了“发消息给助手小妹”,实际上它是发短信给“助手小妹”,这时其实并不是调用你“车助理”主程序内置的Intents,界面就不是你设置的这个。用蓝色可以分清楚它到底是不是在和你的应用在交互。
SiriKit测试全攻略相关推荐
- 安卓单元测试全攻略,让代码测试一劳永逸
本文讲的是 安卓单元测试全攻略,让代码测试一劳永逸, 安卓单元测试,只看这一篇就足够啦.真正的完全解析,真正的从0到1,Junit结合Mockito与Robolectric实现从M到V再到P,Jaco ...
- 组策略分发软件全攻略
组策略分发软件全攻略 在规模比较大的网络环境里面,为了对服务器和客户机上的软件.系统补丁进行集中统一的管理,我们可能会用到SUS.WSUS.SMS等.SUS.WSUS管理系统更新,不在本文讨论,请参考 ...
- [RHEL5企业级Linux服务攻略]--第3季 DHCP服务全攻略
1 DHCP原理 1.1 DHCP概述 DHCP(Dynamic Host Configuration Protocal)就是动态主机配置协议哈,可以自动配置主机的IP地址.子网掩码.网关及DNS等 ...
- iSCSI存储技术全攻略
什么是iSCSI iSCSI(iSCSI = internet Small Computer System Interface )是由IEETF开发的网络存储标准,目的是为了用IP协议将存储设备连接在 ...
- Gradle脚本基础全攻略
转载http://blog.csdn.net/yanbober/article/details/49314255 [工匠若水 http://blog.csdn.net/yanbober 转载请注明出处 ...
- [RHEL5企业级Linux服务攻略]--第6季 Vsftpd服务全攻略之常规配置
1.vsftpd服务软件包 vsftpd-2.0.5-10.el5.i386.rpm:vsftpd主程序包 2.vsftpd相关文档 /etc/vsftpd/vsftpd.conf:vsftpd的核心 ...
- [RHEL5企业级Linux服务攻略]--第2季 Samba服务全攻略答疑贴
回答mlk0416第一个问题: 现在我以joy用户登录samba,如果不用重启系统或注销用户的方法解决多用户切换问题,关闭后重新登录samba还是会以joy帐号登录.现在我们用windows命令提示符 ...
- Windows Home Server中文版使用全攻略
新一代数字家庭中枢<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /& ...
- Linux下的文件共享全攻略系列之一:Samba服务器简介与快速配置指南
目前,研究者们开发了许多网络服务和协议来完成资源共享的功能.对于网络用户和管理员来说,他们希望得到免费和高效的服务,而这种需求的最好选择莫过于当前开源的操作系统Linux下的产品了.本专题从多个应用服 ...
- synergy在Windows和Linux下使用全攻略(多台PC共享一套键盘鼠标)
synergy在Windows和Linux下使用全攻略(多台PC共享一套键盘鼠标) 标签: windowslinuxkeyboard服务器测试 2011-11-26 14:51 6882人阅读 评论( ...
最新文章
- python用django连接mysql_三分钟了解Django如何连接Mysql数据库
- SSH secure shell 权威指南(转载)
- 【论文笔记】CNN for NLP
- Github 开源项目(一)websocketd (实战:实时监控服务器内存信息)
- x264 struct 学习
- 理解C#泛型运作原理
- python中的线程之semaphore信号量
- rose顺序图转换为协作图_如何用GX Developer编程软件编写SFC顺序功能图?
- 继承(引用~析构~virtual)
- Unity 之 自定义编辑器布局
- 国产WMS仓库管理系统排名
- Machine Learning读书会 面试算法讲座 创业活动 算法班 历届汇总
- Android名片识别
- 某人将1000元存入银行 某公司需用一台设备 某企业为了建一项目 建设期3年,共贷款700万元
- 比林志玲cute的katee
- 如果通过这次面试我们单位录用了你,但工作一段时间却发现你根本不适合这个职位,你怎么办?
- web项目经理手册-项目经理需要铭记在心的话
- PMP项目管理—质量情景题
- 山外KL26的J-link连接与程序下载
- 妈,别再乱买保健品了!