在這里放出原文鏈接地址
Part 3.1: 分析與操作 Intents
Intents Extension
的的入口是 INExtension
對(duì)象. 在你的 Extension
中, 你經(jīng)常會(huì)用到3種對(duì)象來(lái)對(duì) Intents
進(jìn)行操作:
- 一個(gè)包含了
Siri
從用戶(hù)那里收集了信息的Intent
對(duì)象. - 一個(gè)你用來(lái)分析、確認(rèn)、處理
Intent
的自定義對(duì)象. - 一個(gè)
Response
對(duì)象. 該對(duì)象包含了你對(duì)Intent
的響應(yīng).
當(dāng) SiriKit
有一個(gè)需要處理的 Intent
對(duì)象, 那么它將會(huì)要求 INExtension
來(lái)提供一個(gè)擁有處理能力的對(duì)象( Handler Objects, 在這里我們暫時(shí)將這個(gè)對(duì)象稱(chēng)之為處理者對(duì)象). 處理者對(duì)象遵守了指定的協(xié)議, 所有的處理Intent
的協(xié)議都有著相同的結(jié)構(gòu), 解析违帆、確認(rèn)豪直、處理等方法.
當(dāng)你在實(shí)現(xiàn)一個(gè)處理者對(duì)象的時(shí)候, 你必須實(shí)現(xiàn)處理Intent
的方法, 其他所有的方法都是可選的, 但是我推薦你去實(shí)現(xiàn)他們. 在你處理Intent
之前, 你可以使用分析和確認(rèn)方法對(duì)你的Intent
進(jìn)行內(nèi)容的驗(yàn)證. 在這個(gè)過(guò)程中, Siri
可以幫助你和用戶(hù)進(jìn)行交互來(lái)收集、獲取你所需要的信息.
下圖中闡述了 SiriKit
和 處理者對(duì)象的交互流程. 當(dāng)用戶(hù)使用 Siri
對(duì)你的 App 生成了一個(gè)請(qǐng)求, SiriKit
將會(huì)創(chuàng)建一個(gè)合適的 Intent
對(duì)象. 如果這個(gè) Intent
對(duì)象包含了需要解析的參數(shù), SiriKit
將會(huì)告知處理者對(duì)象對(duì)參數(shù)進(jìn)行逐一的解析. 當(dāng)所有的參數(shù)都被解析完成后, SiriKit
將會(huì)通知你確認(rèn)準(zhǔn)備處理Intent
. 如果你表示已經(jīng)準(zhǔn)備好了, 那么SiriKit
將會(huì)讓你來(lái)處理這個(gè) Intent
. 此時(shí)你就可以根據(jù) Intent
中所描述的信息內(nèi)容來(lái)執(zhí)行你的任務(wù)了.
Part 3.2: 為 Intent 分配對(duì)應(yīng)的處理者對(duì)象
當(dāng)你的 App 接受到了一個(gè) Intent
對(duì)象時(shí), SiriKit
會(huì)要求 Intents Extension
提供一個(gè)合適的處理者對(duì)象. 此時(shí) SiriKit
將會(huì)調(diào)用你自定義的 INExtension
的子類(lèi)中的 handlerForIntent:
方法(該方法就是你 App Extension 的切入點(diǎn)). 在該方法中, 你需要返回一個(gè)可以解析荤懂、確認(rèn)茁裙、處理 Intent
的處理者對(duì)象.
下面這個(gè)代碼塊中實(shí)現(xiàn)了一個(gè) handlerForIntent:
方法, 用來(lái)提供不同場(chǎng)景中使用的處理者對(duì)象, 其中包含了消息場(chǎng)景和網(wǎng)絡(luò)電話場(chǎng)景.
- (nullable id) handlerForIntent:(INIntent*)intent {
id handler = nil;
// You can substitute other objects for self based on the specific intent
if ([intent isKindOfClass:[INSendMessageIntent class]] ||
[intent isKindOfClass:[INSearchForMessagesIntent class]] ||
[intent isKindOfClass:[INSetMessageAttributeIntent class]]) {
handler = [[MyMessageHandler alloc] init];
}
else if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
handler = [[MyAudioCallHandler alloc] init];
}
else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
handler = [[MyAudioCallHandler alloc] init];
}
return handler;
}
無(wú)論你需要處理什么樣的 Intent
, 上面這個(gè)方法的結(jié)構(gòu)都是相同的. 首先你需要確認(rèn) Intent
對(duì)象的類(lèi)型, 然后返回一個(gè)擁有處理該類(lèi)型 Intent
的處理者對(duì)象. 你可以使用一個(gè)擁有了處理所有場(chǎng)景能力的處理者對(duì)象, 或者你也可以創(chuàng)建許多不同的處理者對(duì)象來(lái)處理不同場(chǎng)景的 Intent
. 在上文這個(gè)例子中, MyMessageHandler
這個(gè)處理者對(duì)象就同時(shí)擁有處理 INSendMessageIntent
、INSearchForMessagesIntent
和 INSetMessageAttributeIntent
場(chǎng)景的能力, 而 MyAudioCallHandler
和 MyAudioCallHandler
處理者則只能處理與其對(duì)應(yīng)的某一場(chǎng)景.
在處理一個(gè) Intent
的過(guò)程中, SiriKit
通常會(huì)多次向你獲取處理者對(duì)象. 處理Intent
的過(guò)程通常會(huì)花費(fèi)一些時(shí)間, 尤其是當(dāng) Siri
向用戶(hù)獲取更清晰的信息或附加信息的時(shí)候, 所以你必須確保處理者對(duì)象是一個(gè)有效的對(duì)象. 也就是說(shuō), 你返回的這個(gè)處理者對(duì)象不能改依賴(lài)于一些緩存的數(shù)據(jù), 取而代之的, 應(yīng)該是以個(gè)全新的處理者對(duì)象.
在 INIntentHandlerProviding
協(xié)議的文檔中, 對(duì)該方法也有闡述:
When creating handler objects in this method, use the provided intent object only to determine which handler to create. Do not use the intent object to initialize your handler object and do not store a reference to the intent object for later use. SiriKit updates the intent object during the processing of the request to incorporate any new information provided by the user.
當(dāng)你在該方法中創(chuàng)建一個(gè)處理者對(duì)象時(shí), 僅僅使用該方法提供的
Intent
對(duì)象來(lái)確定你需要?jiǎng)?chuàng)建哪個(gè)處理者. 不要使用Intent
對(duì)象來(lái)初始化你的處理者對(duì)象, 當(dāng)然也不要為了在將來(lái)能用到這個(gè)Intent
對(duì)象而對(duì)其保存一個(gè)引用.SiriKit
在這個(gè)過(guò)程中, 都會(huì)對(duì)Intent
對(duì)象進(jìn)行更新, 來(lái)讓Intent
對(duì)象包含任何用戶(hù)所提供的信息.
了解如何實(shí)現(xiàn) handlerForIntent:
方法的更多信息, 請(qǐng)查看: INIntentHandlerProviding Protocol Reference
Part 3.3: 解析階段 - 對(duì) Intent 對(duì)象進(jìn)行解析
你可以使用這個(gè)階段來(lái)驗(yàn)證Intent
對(duì)象中包含的參數(shù)并確認(rèn)Intent
對(duì)象中是否包含了你接下來(lái)需要的信息. 因?yàn)檫@些請(qǐng)求信息都是來(lái)自 Siri
對(duì)用戶(hù)語(yǔ)言的解析, 所以最初的信息很有可能不夠完整或者不夠清晰, 那么在解析階段, 你可以查看每一個(gè)參數(shù), 并且告知 SiriKit
這些參數(shù)是否是你需要的, 你還可以讓 Siri
向用戶(hù)獲取一些更進(jìn)一步的信息.
大多數(shù)的 Intent
對(duì)象都包含至少一條參數(shù)來(lái)讓你的處理者對(duì)象對(duì)其進(jìn)行解析. 當(dāng)你實(shí)現(xiàn)一個(gè)處理者對(duì)象的時(shí)候, 我推薦你為Intent
對(duì)象實(shí)現(xiàn)所有的解析方法. 盡管有可能你不會(huì)用到這個(gè)參數(shù), 你仍然可以實(shí)現(xiàn)相應(yīng)的方法來(lái)告知SiriKit
你沒(méi)有用到這個(gè)參數(shù). 告知SiriKit
你沒(méi)有用到這個(gè)參數(shù), 可以避免SiriKit
向用戶(hù)詢(xún)問(wèn)有關(guān)該參數(shù)的問(wèn)題.
你實(shí)現(xiàn)每一個(gè)解析方法時(shí), 都應(yīng)該遵循以下這種結(jié)構(gòu):
- 獲取
Intent
對(duì)象中的參數(shù). - 驗(yàn)證這個(gè)參數(shù)是不是你 App 中需要用到的.
- 根據(jù)你的驗(yàn)證結(jié)果, 實(shí)例化一個(gè)合適的
INIntentResolutionResult
子類(lèi)對(duì)象. - 執(zhí)行方法中傳入的 Block, 并將你實(shí)例化的結(jié)果對(duì)象以參數(shù)的形式傳入這個(gè) Block 中.
Intent Framework
為每一種類(lèi)型都定義了一個(gè) INIntentResolutionResult
的子類(lèi). 每一個(gè)子類(lèi)都包含了一個(gè)類(lèi)方法, 讓你來(lái)指定你是解析成功了還是需要更多的信息. 父類(lèi)INIntentResolutionResult
中包含了方法, 你可以使用該方法來(lái)指定一些常見(jiàn)的處理結(jié)果. 下面表格中列出了一些你可能用得到的處理結(jié)果.
處理結(jié)果 | 舉例說(shuō)明 |
---|---|
解析成功 | 當(dāng)你指定這個(gè)處理結(jié)果時(shí), 說(shuō)明你可以使用提供的值對(duì)Intent 對(duì)象進(jìn)行處理. 例如: 如果用戶(hù)指定了一個(gè)人的名字為 John , 并且通訊錄中只有一個(gè)叫John 的人, 那么你就可以指定解析成功. |
不需要的值 | 當(dāng)你指定這個(gè)處理結(jié)果時(shí), 說(shuō)明你不需要用到這個(gè)值. 使用 notRequired 類(lèi)方法來(lái)初始化處理結(jié)果對(duì)象. |
需要更加清晰的值 | 當(dāng)你指定這個(gè)處理結(jié)果時(shí), 說(shuō)明你可能鑒別出了兩個(gè)或兩個(gè)以上可能得結(jié)果, 但是你沒(méi)有辦法確定哪個(gè)結(jié)果是更準(zhǔn)確的. 這可能會(huì)導(dǎo)致和用戶(hù)產(chǎn)生一個(gè)附加的交互, 讓用戶(hù)來(lái)選擇他想要的結(jié)果. |
需要用戶(hù)確認(rèn)的值 | 當(dāng)你指定這個(gè)處理結(jié)果時(shí), 說(shuō)明你需要用戶(hù)來(lái)最終確認(rèn)你的選擇, 當(dāng)你鑒別出了一個(gè)值, 但是你不能完全確定這個(gè)值是否是正確值得時(shí)候, 你可以使用這個(gè)處理結(jié)果. |
需要一個(gè)值 | 當(dāng)你指定這個(gè)處理結(jié)果時(shí), 說(shuō)明你解析參數(shù)的時(shí)候并沒(méi)有解析到你需要的值. 例如: 當(dāng)支付過(guò)程中, 你并沒(méi)有解析到支付金額的時(shí)候. 使用needsValue 類(lèi)方法來(lái)初始化處理結(jié)果對(duì)象. |
不支持的值 | 當(dāng)你指定這個(gè)處理結(jié)果時(shí), 說(shuō)明你的 App 不支持這個(gè)特殊的值 或者 這個(gè)值和其他的參數(shù)發(fā)生了沖突的時(shí)候. 例如: 當(dāng)支付過(guò)程中, 用戶(hù)提供的是瑞士法郎 , 但是你的 App 只支持歐元或美元的時(shí)候. 使用unsupported 類(lèi)方法來(lái)初始化處理結(jié)果對(duì)象. |
在解析參數(shù)的時(shí)候, 你需要盡可能快的得出結(jié)果. 向用戶(hù)詢(xún)問(wèn)更多的信息會(huì)帶來(lái)額外的交互, 過(guò)多的交互會(huì)給用戶(hù)帶來(lái)負(fù)擔(dān), 也會(huì)使用戶(hù)體驗(yàn)非常不好. 所以你可以基于用戶(hù)的習(xí)慣和喜好選擇一個(gè)最為可能得值作為結(jié)果.
下面這個(gè)代碼塊中是一個(gè)旅行預(yù)訂 App 驗(yàn)證飛機(jī)降落地點(diǎn)的 Demo. 當(dāng)飛機(jī)降落的地點(diǎn)在你 App 提供服務(wù)的范圍區(qū)域內(nèi), 則該方法會(huì)返回成功, 反之該方法將會(huì)告知 Siri
不支持這個(gè)值. 然而當(dāng)值不存在的時(shí)候, 這個(gè)方法也會(huì)告知Siri
, 并讓Siri
向用戶(hù)詢(xún)問(wèn)一個(gè)值.
- (void)resolveDropOffLocationForRequestRide:(INRequestRideIntent *)intent
withCompletion:(void (^)(INPlacemarkResolutionResult *resolutionResult))completion {
CLPlacemark* location = intent.dropOffLocation;
INPlacemarkResolutionResult* result = nil;
if (location) {
// If the locaiton is valid, use it; otherwise,
// let the user know it is not supported
if ([self locationIsInsideServiceArea:location])
result = [INPlacemarkResolutionResult successWithResolvedPlacemark:location];
else
result = [INPlacemarkResolutionResult unsupported];
}
else {
// Ask for the drop-off location.
result = [INPlacemarkResolutionResult needsValue];
}
// Return the result.
completion(result);
}
Part 3.4: 確認(rèn)階段
利用該階段對(duì)Intent
的參數(shù)進(jìn)行最后的驗(yàn)證, 并且確認(rèn)你已經(jīng)準(zhǔn)備好處理Intent
了. 在所有參數(shù)都被解析成功, SiriKit
將會(huì)讓你來(lái)執(zhí)行處理Intent
的操作. 你可以在確認(rèn)方法( confirm method ) 中執(zhí)行最后的檢查. 例如: 如果你的 App 服務(wù)是基于網(wǎng)絡(luò)請(qǐng)求的, 在這個(gè)方法中你可以確保網(wǎng)絡(luò)請(qǐng)求服務(wù)是否正常. 確認(rèn)方法(confirm method ) 不是必須實(shí)現(xiàn)的方法, 但是我們強(qiáng)烈推薦你實(shí)現(xiàn)它.
在你的確認(rèn)方法(confirm method ) 中, 執(zhí)行所有的確認(rèn)操作, 然后創(chuàng)建一個(gè)響應(yīng)對(duì)象(response object ), 該對(duì)象包含了你在處理 Intent
時(shí)需要用到的一些數(shù)據(jù). 如果需要提示用戶(hù)來(lái)確認(rèn)一些信息時(shí), Siri
將會(huì)在確認(rèn)頁(yè)面中展示你在相應(yīng)對(duì)象( response object )中的詳細(xì)信息. Siri
只有在重要的時(shí)候才會(huì)提示用戶(hù)進(jìn)行確認(rèn), 例如: 金錢(qián)相關(guān)的操作 和 某些不可逆的操作. 其他時(shí)候 Siri
是不會(huì)提示用戶(hù)進(jìn)行確認(rèn)操作的.
下面代碼塊中, 展示了一個(gè)確認(rèn)方法( confirm method ) 的簡(jiǎn)單實(shí)現(xiàn). 是一個(gè)鍛煉( workout ) 相關(guān)的 App. 該方法中提供了一個(gè)響應(yīng)對(duì)象( response object )標(biāo)識(shí)著 App 是否已經(jīng)準(zhǔn)備好了. userActivity
參數(shù)傳入 nil, 將會(huì)導(dǎo)致 SiriKit
創(chuàng)建一個(gè)默認(rèn)的user activity
對(duì)象.
- (void)confirmStartWorkout:(INStartWorkoutIntent *)startWorkoutIntent
completion:(void (^)(INStartWorkoutIntentResponse * _Nonnull))completion {
// Make sure the app is ready.
// Provide the response.
INStartWorkoutIntentResponse* response = [[INStartWorkoutIntentResponse alloc]
initWithCode:INStartWorkoutIntentResponseCodeReady userActivity:nil];
completion(response);
}
Part 3.5: 處理階段
處理階段是整個(gè)流程的最后一個(gè)階段, 該階段將會(huì)執(zhí)行 Intent
對(duì)象所關(guān)聯(lián)的任務(wù). 處理階段有兩個(gè)重要的職責(zé):
- 執(zhí)行
Intent
對(duì)象所關(guān)聯(lián)的任務(wù). - 返回一個(gè)響應(yīng)對(duì)象 ( response object ).
如何對(duì)一個(gè)Intent
對(duì)象進(jìn)行處理是基于Intent
對(duì)象本身的. 盡可能的直接使用 Intents Extension
對(duì)Intent
對(duì)象進(jìn)行處理. 有些Intent
是需要向你的 App 傳入一個(gè)控制的(However, some intents involve passing control back to your app). 例如: 成功的處理一個(gè)開(kāi)始鍛煉的Intent
還涵蓋了啟動(dòng)你的 App 并且開(kāi)始鍛煉的過(guò)程. 當(dāng)執(zhí)行完任務(wù)之后, 在 Block 中傳入一個(gè)相應(yīng)對(duì)象( response object )返回給SiriKit
, 該對(duì)象包含了你 App 都做了哪些操作的信息.
下面代碼塊中展示了開(kāi)始一個(gè)新的鍛煉任務(wù)的實(shí)現(xiàn)部分. 在這種情況下, 處理者對(duì)象( handler object ) 告知 Siri
開(kāi)始相關(guān)的 App 并開(kāi)始鍛煉任務(wù). 其他的Intent
可能需要為相應(yīng)對(duì)象( response object ) 提供一些額外的數(shù)據(jù).
- (void)handleStartWorkout:(INStartWorkoutIntent *)startWorkoutIntent
completion:(void (^) (INStartWorkoutIntentResponse * _Nonnull))completion {
INStartWorkoutIntentResponse* response = [[INStartWorkoutIntentResponse alloc]
initWithCode:INStartWorkoutIntentResponseCodeContinueInApp userActivity:nil];
completion(response);
}
每一個(gè)響應(yīng)者對(duì)象( response object ) 都支持一個(gè)可選的 NSUserActivity
對(duì)象, 該對(duì)象承載著如何啟動(dòng)你的 App 的信息. 如果你沒(méi)有指定這個(gè)對(duì)象, SiriKit
將會(huì)為你創(chuàng)建一個(gè)默認(rèn)的NSUserActivity
對(duì)象, 其中涵蓋了一個(gè) INInteraction
對(duì)象, 該對(duì)象包含了 Intent
對(duì)象和響應(yīng)者對(duì)象( response object ). 如果你希望添加更多的附加信息, 你可以自己聲明一個(gè) NSUserActivity
對(duì)象, 在改對(duì)象的 userInfo
字典中添加更多的信息.
在操作 Intent
的過(guò)程中, 你做出的任何修改都應(yīng)該反應(yīng)在你的 App 界面中, 作為執(zhí)行任務(wù)的一部分, 你的Intents Extension
應(yīng)該傳達(dá)一切相關(guān)的信息到它的源 App 中. 如果你的 Intents Extension
成功的處理了 Intent
, 那么 user activity
對(duì)象將永遠(yuǎn)都不會(huì)傳送給你的 App 了. 所以為了確保修改的信息能夠被送達(dá)你的 App, 你應(yīng)該更新被分享數(shù)據(jù)的業(yè)務(wù)邏輯(update any shared data structures), 使用后臺(tái) App 刷新节仿、 靜默推送或其他一些技術(shù)來(lái)將修改的信息傳達(dá)給你的 App.
Part 3.6: 處理 Intent 的技巧
當(dāng)你在確認(rèn)方法 ( confirm method ) 或 處理方法 ( handle method ) 中返回一個(gè)響應(yīng)對(duì)象 ( response object )時(shí), 你需要考慮如下幾點(diǎn):
- 盡可能多的在你的響應(yīng)對(duì)象 ( response object ) 中添加信息: 為你的響應(yīng)對(duì)像 ( response object ) 添加更多的詳細(xì)信息來(lái)告知用戶(hù)你的 App 都做了哪些操作, 這會(huì)給用戶(hù)一個(gè)更好的體驗(yàn).
-
在分配給響應(yīng)對(duì)象 ( response object ) 之前, 盡可能充分的配置你的數(shù)據(jù)對(duì)象: 對(duì)于大多數(shù)的響應(yīng)對(duì)像 ( response object ) 而言, 它的屬性都是使用
Copy
修飾的. 所以當(dāng)你從屬性中獲取了一個(gè)數(shù)據(jù)對(duì)象并且對(duì)其進(jìn)行了修改時(shí), 實(shí)際上是修改了 copy 出來(lái)的對(duì)象, 而并不是對(duì)其本身進(jìn)行了修改, 所以你應(yīng)該充分的對(duì)你的數(shù)據(jù)對(duì)象進(jìn)行修改后再賦值. -
在盡可能短的時(shí)間內(nèi)返回你的相應(yīng)對(duì)象 (response object ): 因?yàn)?
Siri
和Maps
是與用戶(hù)實(shí)時(shí)聯(lián)系的, 所以你應(yīng)該盡可能快的返回相應(yīng)對(duì)象 (response object ). 如果你返回相應(yīng)對(duì)象需要一段時(shí)間的話, 你應(yīng)該先返回一個(gè)帶有"正在進(jìn)行中..."類(lèi)似提示語(yǔ)的相應(yīng)對(duì)象 (response object )來(lái)告訴用戶(hù)你正在進(jìn)行操作.
當(dāng)可以使用通訊錄的時(shí)候, SiriKit
會(huì)利用通訊錄中的信息來(lái)對(duì)參數(shù)進(jìn)行填充, 如果你的 App 被拒絕訪問(wèn)通訊錄了, 那么Intent
對(duì)象中就將不會(huì)包含任何與通訊錄相關(guān)的信息了. 例如: INPerson
對(duì)象將不會(huì)包含任何聯(lián)系人的內(nèi)容. 拒絕訪問(wèn)通訊錄也同樣會(huì)拒絕SiriKit
訪問(wèn)通訊錄中的地址, 如此一來(lái), 如果用戶(hù)對(duì) Siri 說(shuō): 我需要使用 <App> 導(dǎo)航騎車(chē)回家
時(shí), 將會(huì)導(dǎo)致 Intent
對(duì)象中不包含目的地. 如果 App 被用戶(hù)拒絕了訪問(wèn)通訊錄, 你可能需要提示用戶(hù): 開(kāi)啟通訊錄權(quán)限將會(huì)為您帶來(lái)更完美的用戶(hù)體驗(yàn).
Lemon龍說(shuō):
如果您在文章中看到了錯(cuò)誤 或 誤導(dǎo)大家的地方, 請(qǐng)您幫我指出, 我會(huì)盡快更改
如果您有什么疑問(wèn)或者不懂的地方, 請(qǐng)留言給我, 我會(huì)盡快回復(fù)您
如果您覺(jué)得本文對(duì)您有所幫助, 您的喜歡是對(duì)我最大的鼓勵(lì)
如果您有好的文章, 可以投稿給我, 讓更多的 iOS Developer 在簡(jiǎn)書(shū)這個(gè)平臺(tái)能夠更快速的成長(zhǎng)