引言
- 在6月14日凌晨的WWDC2016大會(huì)上嘉裤,蘋果提出iOS10是一次里程碑并且推出了十個(gè)新特性,homekit、messageapp等等毅待,大部分是基于iPhone原生應(yīng)用的更新馒闷。其中最大的亮點(diǎn)之一是Siri的接口開放酪捡,在iOS10中提供了SiriKit框架在用戶使用Siri的時(shí)候生成INExtension對(duì)象來告知我們的應(yīng)用,我們可以通過SiriKit提供的API展示給用戶更多的內(nèi)容纳账。
- Siri通過語言處理系統(tǒng)對(duì)用戶發(fā)出的對(duì)話請(qǐng)求進(jìn)行解析之后生成一個(gè)用來描述對(duì)話內(nèi)容的Intents事件逛薇,然后通過SiriKit框架分發(fā)給集成框架的應(yīng)用程序以此來獲取應(yīng)用的內(nèi)容,比如通過文字匹配查找應(yīng)用聊天記錄疏虫、聊天對(duì)象等功能永罚,此外還支持為用戶使用蘋果地圖時(shí)提供應(yīng)用內(nèi)置服務(wù)等功能。
SiriKit六類服務(wù)
sirikit 服務(wù) | 對(duì)應(yīng)的INintent |
---|---|
語音和視頻通話 VoIP calling | INStartVideoCallIntent卧秘、INStartAudioCallIntent |
發(fā)送消息 Messaging | INSendMessageIntent |
收款或者付款 Payments | INSendPaymentIntent呢袱、INRequestPaymentIntent |
圖片搜索 Photo search | INSearchForPhotosIntent |
管理鍛煉 Workouts | INEndWorkoutIntent、INPauseWorkoutIntent 翅敌、INStartWorkoutIntent 羞福、 INResumeWorkoutIntent 、INCancelWorkoutIntent |
行程預(yù)約 Ride booking | INRequestRideIntent蚯涮、INGetRideStatusIntent治专、 INListRideOptionsIntent、 INGetRideStatusIntent |
更加詳細(xì)的說明參見Intents Domains
也就是說當(dāng)我們使用sirikit的時(shí)候遭顶,可以使用以上列表對(duì)應(yīng)的六種服務(wù)看靠,比如說你的應(yīng)用是聯(lián)系人可以發(fā)送消息,這時(shí)候你就可以使用INSendMessageIntent服務(wù)液肌,在鎖屏或者主屏幕喚起siri挟炬,然后比方說說出“嘿,siri嗦哆,在xxx應(yīng)用上發(fā)送一條消息給劍劍”谤祖,然后siri就能響應(yīng)并同時(shí)提供回調(diào)給你的應(yīng)用siri extension。具體的流程我們會(huì)在下一節(jié)舉例說明老速。
Siri執(zhí)行流程
- 示例流程圖
-
獲取詞匯邏輯
siri_cihui.png
從詞匯中可以看到intent(暫且翻譯成意圖粥喜?)對(duì)應(yīng)著siri給我們的響應(yīng),這是app通過處理intent對(duì)應(yīng)的reslove橘券,confirm额湘,handle來做相關(guān)響應(yīng)跃洛。
intents
1.intents說明
- Action to be performed 定義將要響應(yīng)的動(dòng)作,即reslove壹罚,confirm恋日,handle
- Zero-to-many parameters 意圖會(huì)有對(duì)應(yīng)的參數(shù)來響應(yīng)某些事件
- Classified into a domain 意圖劃分的種類
Siri通過Intents extension的擴(kuò)展方式和我們的應(yīng)用進(jìn)行交互,其中INExtension扮演著Intents extension擴(kuò)展中直接協(xié)同Siri共同響應(yīng)用戶的角色毯焕。當(dāng)我們實(shí)現(xiàn)了Intents extension擴(kuò)展并產(chǎn)生了一個(gè)Siri請(qǐng)求事件時(shí)衍腥,Intents事件的處理過程分Resolve、Confirm和Handle三個(gè)步驟纳猫。
2.LifeCycle for an intent
一個(gè)intent的完整周期如下圖:
- Resolve階段婆咸。在Siri獲取用戶的語音輸入之后,生成一個(gè)INIntent對(duì)象芜辕,將語音中的關(guān)鍵信息提取出來并且填充對(duì)應(yīng)的屬性尚骄,該對(duì)象會(huì)傳遞給我們?cè)O(shè)置好的INExtension子類對(duì)象進(jìn)行處理,根據(jù)子類遵循的不同protocol來選擇不同的解決方案侵续。在上一個(gè)階段通過handler(for intent:)返回了處理intent的對(duì)象乖仇,此階段會(huì)依次調(diào)用confirm打頭的實(shí)例方法來判斷Siri填充的信息是否完成。
resolve階段用戶大致分為以下說明階段:
助SIri明白用戶的含義
影響Siri的行為
提供resolution response
successWithResolvedPerson:成功找到匹配的人
disambiguationWithPeopleToDisambiguate:還需要挑選
confirmationRequiredWithPersonToConfirm:還需要確認(rèn)下
needMoreDetailsForPerson:還需要更具體的信息询兴,需要Siri進(jìn)行詢問
unsupportedWithReason:無法使用指定值
needsValue:需要某些必需值
notRequired:應(yīng)用并沒有要求某些值
- Confirm階段乃沙。確認(rèn)信息。檢查必要的狀態(tài)等诗舰。Siri進(jìn)行最后的處理階段警儒,生成答復(fù)對(duì)象,并且向此intent對(duì)象確認(rèn)處理結(jié)果眶根。
告訴Siri預(yù)期結(jié)果
檢查必要的狀態(tài)
提供Intent response
Siri提供必要的確認(rèn)提示,參考如下連圖例
- Handle階段:在Confirm方法執(zhí)行完成之后蜀铲,然后顯示結(jié)果給用戶看
執(zhí)行請(qǐng)求操作
提供有關(guān)結(jié)果足夠精確的信息
如果結(jié)果耗時(shí)的話還可提供loading
實(shí)現(xiàn)一個(gè)Siri Kit應(yīng)用
- 升級(jí)到Xcode8,一臺(tái)升級(jí)到iOS10的測(cè)試設(shè)備
-
Intents extension : resolve属百、confirm记劝、handle流程
新建extension如下圖:
siri_ex2.png
通常SiriUI也會(huì)配置一起創(chuàng)建,入后配置info.plist文件
上圖中IntentsSupported是指你的擴(kuò)展支持的intent類型族扰,如果你的代碼想支持某種intent必須在這里配置厌丑。
IntentsRestrictedWhileLocked是指在鎖屏狀態(tài)下能用siri喚起的intent,如果你想在鎖屏下能喚起siri的擴(kuò)展功能必須配置渔呵。
創(chuàng)建之后怒竿,查看plist文件
- 1.增加xxxintent
- 2.NSExtensionPrincipalClass,這里必須在plist聲明,它是INExtension的子類扩氢,INIntentHandleProviding耕驰,handleForIntent,handleClass must conform to specific intent handling protocol等录豺,詳細(xì)說明參見Intents Domains
主項(xiàng)目plist 增加NSSiriUsageDescription 這個(gè)是請(qǐng)求Siri權(quán)限時(shí)提示的文案
使用Siri時(shí)朦肘,用戶必須說出App的名字饭弓,也就是Bundle display name
以發(fā)送消息為例:當(dāng)siri第一次確認(rèn)的時(shí)候,需要應(yīng)用授權(quán)媒抠,授權(quán)代碼和提示如下圖:
具體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 提供界面自定義等內(nèi)容
通常確認(rèn)界面的UI蘋果會(huì)提供默認(rèn)的樣式,默認(rèn)的樣式如下圖:
但是如果你想自定義UI领舰,如下圖所示夫嗓,你就必須配置Intents UI extension迟螺,在MainInterface.storyboard配置相關(guān)UI冲秽,然后在對(duì)應(yīng)的控制器中配置config函數(shù)。
圖
參考configs函數(shù)如下:
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擴(kuò)展可能會(huì)和你的應(yīng)用有數(shù)據(jù)共享锉桑,而且可能會(huì)有同樣的邏輯代碼,因此apple建議相關(guān)業(yè)務(wù)的邏輯代碼可用framwork的形式窍株,這樣擴(kuò)展和宿主app都可以使用民轴。