随着iOS10.0发布脚步的临近,作为开发者,相信很多人也和我一样,可以提前体验一些新系统的新功能,也更关注新版iOS系统带来的技术开发方面的新特性。
对新事物,尤其是软件开发新技术的好奇心驱使我最近尝试了一些iOS10.0推出的新特性的开发,其中就包括本次iOS系统更新开发的SiriKit接入。下面,我就详细讲述一下SiriKit接入的相关开发过程及注意事项,个人见解,如有错误,欢迎大家交流指正。
关于SiriKit
SiriKit是Apple历经4年时间,不断打磨优化,第一次开发给开发者的一份关于Siri功能的礼物。利用SiriKit,第三方开发者可以像一些系统应用一样,通过语音完成第三方应用希望完成的一些功能,比如用户可以直接通过语音直接告诉Siri打车、锻炼、寻找美食、寻找相册中的照片、甚至付给朋友AA的账单费用,以及控制家里智能家居等。
概念概览
- Domain - 被苹果划分的不同业务领域,每个领域中可以执行不同的任务
- Intent - 领域中的任务或意图指令
实现机制
和Android的语音接入Service类似,SiriKit中,将不同的类型的需求统一汇总为若干个Domain,然后在每个Domain中再次细分为不同的Intent。系统通过语音识别获取到的Domain信息以及Intent信息,下发到已注册的Domain中进行处理,然后用户解析处理不同的Intent,来实现自定义的操作。
SiriKit的接入方式和Watch OS的接入有相似之处,都是以Extension形式存在,该Extension会声明本应用所能处理的Domain内容及Intent种类,告知Siri该应用所能处理的功能范畴.这样,即便你的应用当前并没有打开或在后台运行,通过Siri也可以唤醒你的应用,处理相关逻辑。
Domains
- VoIP Calling - 语音通信相关,如微信、Facebook Messager、Line等;
- Payments - 适用于支付类应用,如PayPal、支付宝等;
- Photo - 与图库相关应用,如Instagram等;
- Workouts - 运动健康类应用,如Strava、NikeRun、野兽骑行等;
- Ride booking - 适用于出行类应用,如Uber、滴滴打车等;
- CarPlay(automotive vendors only)- 车载及自动驾驶相关,尚不明晰
- Restarurant reservations(Requires addtional support from Apple) - 酒店服务类,具体需求上不明晰
Intents
- VoIP Calling
- Start an audio call -开始语音通话
- Start a video call - 开始视频通话
- Search the user’s call history -搜寻通话历史
- Messaging
- Send a message
- Search for messages
- Set attributes on a message
- Photos
- Search for photos
- Play a photo slideshow
- Payments
- Send a payment to another user
- Request a payment from another user
- Workouts
- Start a workout
- Pause a workout
- Resume a workout
- End a workout
- Cancel a workout
- Ride Booking
- Get a list of available rides (Maps only)
- Book a ride
- Get the status of a booked ride
- CarPlay
- Change the audio source
- Change the climate control settings
- Change the defroster settings
- Saving vehicle settings to a profile
- Restoring vehicle settings from a profile
- Change the seat temperature
- Change the radio station
- Restaurant Reservations
- Get the user’s current restaurant reservations
- Get information about the user to associate with a booking.
- Get default values to use when requesting reservation times.
- Get the reservation times that are currently available.
- Book a reservation for the user.
注意,经过开发实践,每种Domain及Intents都有固定或相似的语法形式,为增加识别度,可以参考官网说明,按照说明中自然语言语法形式唤起相应的Siri功能,例如:”用XXX开始跑步“ 要比仅仅说 “XXX开始跑步”能更好的识别出应用XXX。
由于Siri所使用的识别技术及语言模型是在云端训练并完成数据解析,可能还有些不完善。在我们刚开始开发时,就遇到了这样的问题,中文普通话的有些Intent会有支持不完整的情况,导致语音识别不出来,无法完成语义解析,相关回调方法不被调用等问题,如有大家也遇到了类似问题,建议使用英文进行测试。
对于此问题,也已经向Apple Siri开发团队反应,相关训练任务已经在进行中,很惊喜的是,在之后的开发中,有些之前不支持的Intent语义识别也已经可以使用了。
开发流程
上面简述了SiriKit的一些背景知识及实现机制,下面简单介绍一下我在接入SiriKit时使用的开发流程.
开发环境
- MacbookPro. OSX EI Capitan 10.11.6
- xCode 8.0 beta6(8S201h)
- iPhone6 iOS10.0 beta6
- iTerms2, git, etc.
支持类型
对于向现有iOS应用中加入对Siri的支持有两种形式,一种是Intents Extension,另一种是Intents UI Extension,前者没有UI界面,类似一个后台逻辑处理服务,这项Extension是必选的。
而Siri UI Extensions是可选内容,用于展示一些确认信息等操作。经负责该功能的Apple SiriKit工程师确认,UI Siri Extensions的声明周期比较短,部分应用可根据实际需要采用,但Workout类应用目前试用范围较宅,本例中未采用。
接入步骤
本次接入SiriKit开发中,采用Workout类应用,此处Workout并不是Apple WatchOS 3.0中新增的Workout App, 而是指一类运动锻炼相关应用,例如,Strava, Nike Run一类。由于Apple SiriKit中限制了7类不同的Domain,如果需要接入其他类别的应用,如天气,支付等,可以参考官方接入指南进行相关开发。
Step1. 打开应用的Siri Capability
在Xcode8中,选中应用Target,选择Capability标签,打开Siri Capability打开,如图所示:
打开该属性后,工程文件的Entitlements文件会增加如下内容:
Step2: 创建支持Siri的Intents Extension
首先选中App工程,为该App添加对Siri的Intents Extension支持的Target,如图:
选择Intents Extension
命名并选择是否UI Extension,可根据自我业务需求确定,本例中,对UI Extension不进行支持。
创建完成后,Siri Intents Extension的target会显示在Target列表中。
Step3: 定义所支持的Intent
该步骤相当于告诉Siri及iOS系统,本应用中所支持的Domain和Intents都有哪些,这样iOS系统及Siri就知道你的应用可以处理哪些请求,不能处理哪些请求。
打开Siri Intents Extension工程中的Info.plist文件,选中NSExtension,修改所支持的Intent类型,具体类型字符串可以查询开发文档,添加后的内容如下:
IntentsRestrictedWhileLocked是可选项,用来控制锁屏状态下,对不同命令的响应行为。
Step4:添加对主应用Siri权限支持
在主应用Info.plist文件中添加NSSiriUsageDescription Key,该key用来标记应用与Extension所沟通的数据类型,用于告知用户,你的应用为何要支持Siri的描述信息,比如Workout类应用,我可以这么写:“健身锻炼的信息会发送到Siri,更快捷的记录健身数据”
仅仅添加NSSiriUsageDescription这个Key到Info.plist文件中是不够的,当应用第一次启动时,我们需要用户打开应用的Siri支持权限,默认情况下,这个权限是关闭的,所以,需要我们通过调用INPreferences的类方法equestSiriAuthorization: 来通知用户打开Siri权限支持。
Step5. 添加逻辑
OK,到此为止,我们已经把所有接入Siri Intents Extension的前期准备工作都已经完成,现在我们要进入正题,实现相应的逻辑处理代码编写工作。
修改IntentHandling.swift文件,根据需要,修改所需要实现的协议内容:
class IntentHandler:INExtension,INStartWorkoutIntentHandling,INPauseWorkoutIntentHandling,INResumeWorkoutIntentHandling,INEndWorkoutIntentHandling{
...
}
OK,到此为止,我们已经把所有接入Siri Intents Extension的前期准备工作都已经完成,现在我们要进入正题,实现相应的逻辑处理代码编写工作。
修改IntentHandling.swift文件,根据需要,修改所需要实现的协议内容:
class IntentHandler:INExtension,INStartWorkoutIntentHandling
,INPauseWorkoutIntentHandling,INResumeWorkoutIntentHandling
,INEndWorkoutIntentHandling{
...
}
修改所需要实现的不同场景下的逻辑回调部分,调用所在iOS App的相关业务逻辑,满足产品需求,例如
开始锻炼
/*!
@briefhandlingmethod
@abstractExecutethetaskrepresentedbytheINStartWorkoutIntentthat
'spassedin
@discussionThismethodiscalledtoactuallyexecutetheintent
.Theappmustreturnaresponseforthisintent.
@paramstartWorkoutIntentTheinputintent
@paramcompletionTheresponsehandlingblocktakesaINStartWorkoutIn
tentResponsecontainingthedetailsoftheresultofhavingexecutedthe
intent
@seeINStartWorkoutIntentResponse
*/
public func handle(startWorkoutintent:INStartWorkoutIntent,completion
:@escaping(INStartWorkoutIntentResponse)->Swift.Void){
print("HereisStartworkoutcalledbySiri");
}
暂停锻炼
//MARK:-INPauseWorkoutIntentHandling
/*!
@briefhandlingmethod
@abstractExecutethetaskrepresentedbytheINPauseWorkoutIntentthat
'spassedin
@discussionThismethodiscalledtoactuallyexecutetheintent
.Theappmustreturnaresponseforthisintent.
@parampauseWorkoutIntentTheinputintent
@paramcompletionTheresponsehandlingblocktakesaINPauseWorkoutIn
tentResponsecontainingthedetailsoftheresultofhavingexecutedthe
intent
@seeINPauseWorkoutIntentResponse
*/
public func handle(pauseWorkoutintent:INPauseWorkoutIntent,completion
:@escaping(INPauseWorkoutIntentResponse)->Swift.Void){
print("HereisPauseworkoutcalledbySiri");
}
恢复锻炼
//MARK:-INResumeWorkoutIntentHandling
/*!
@briefhandlingmethod
@abstractExecutethetaskrepresentedbytheINResumeWorkoutIntenttha
t'spassedin
@discussionThismethodiscalledtoactuallyexecutetheintent
.Theappmustreturnaresponseforthisintent.
@paramresumeWorkoutIntentTheinputintent
@paramcompletionTheresponsehandlingblocktakesaINResumeWorkoutI
ntentResponsecontainingthedetailsoftheresultofhavingexecutedth
eintent
@seeINResumeWorkoutIntentResponse
*/
public func handle(resumeWorkoutintent:INResumeWorkoutIntent
,completion:@escaping(INResumeWorkoutIntentResponse)->Swift.Void){
print("HereisResumeworkoutcalledbySiri");
}
结束锻炼
//MARK:-INEndWorkoutIntentHandling
/*!
@briefhandlingmethod
@abstractExecutethetaskrepresentedbytheINEndWorkoutIntentthat's
passedin
@discussionThismethodiscalledtoactuallyexecutetheintent
.Theappmustreturnaresponseforthisintent.
@paramendWorkoutIntentTheinputintent
@paramcompletionTheresponsehandlingblocktakesaINEndWorkoutInte
ntResponsecontainingthedetailsoftheresultofhavingexecutedthei
ntent
@seeINEndWorkoutIntentResponse
*/
public func handle(endWorkoutintent:INEndWorkoutIntent,completion
:@escaping(INEndWorkoutIntentResponse)->Swift.Void){
print("HereisEndworkoutcalledbySiri");
}
总结
以上就完成了所有接入SiriKit的工作,剩余的应用就是测试成果,此处有一些技巧,
- 技巧一:由于SiriKit首次发布,有些语言支持尚不完善,建议使用英文进行测试,这样可以避开一些Intent在不同语言中支持不完整的问题。
- 技巧二:iPhone6s及以后设备已经支持了通过“Hey, Siri”语音唤醒的特性,所以用iPhone6s可以方便的不用按住Home就可进行测试,很方便,对于iPhone6 & iPhone6P,插入数据线,连接电脑,也可以通过“Hey, Siri”唤醒,直接进行相应业务需求测试。
以上就是接入SiriKit的背景知识点以及基本流程,在接入过程中,与Apple SiriKit开发团队接触过程中,发现了若干问题,也收获了许多知识,在后续文章中,我会单独抽出一篇来与大家分享。文章中间如有错误之处,欢迎大家批评指正,共同成长。
Enjoy your Siri trip.
2017年2月
参考文献