公司的項目正好是鍛煉類的,也能趕上潮流用上Siri呼喚App了瞧栗。
看過多篇資料迹恐,鋪天蓋地都是一樣的殴边,且并沒有明確的鍛煉類詳解锤岸,哪些地方能用哪些地方無法實現(xiàn)是偷,只能自己結合資料蛋铆、官方文檔慢慢摸索栗恩,終于還是完成了磕秤。
本篇不對SiriKit進行分析市咆,只對如何構建SiriApp進行整理蒙兰,如下:
一、前提
首先针炉,Siri目前支持以下幾個方面:
1篡帕、語音通話
2镰烧、發(fā)送茉唉、搜索信息
3、支付
4赌渣、照片搜索
5、開始斜姥、暫停缚忧、恢復闪水、結束球榆、取消鍛煉
6、打車
如果你的APP正好支持以上篱昔,就能用Siri控制啦空执。
二穗椅、創(chuàng)建
1脆烟、打開程序,在主程序下點擊+房待,如圖:
2邢羔、選擇iOS -> Intents Extension:
3、填寫名字Appname+SiriIntent桑孩、選擇swift或者oc等設置拜鹤,如圖默認勾選UI Extension:
4、選好后會生成一些示例代碼流椒,有些教程生成的是鍛煉類的代碼敏簿,有些也和我一樣生成的是發(fā)送消息搜索消息的代碼:
5绣硝、配置plist
點擊第一個plist文件够傍,在
NSExtension -> NSExtensionAttributes -> IntensSupported (常規(guī)狀態(tài)下呼喚Siri)/ IntensRestrictedWhileLocked(鎖屏狀態(tài)下呼喚Siri)中添加字段
(如:默認的會生成INSendMessageIntent安聘、INSearchForMessagesIntent囱桨、INSetMessageAttributeIntent翠语,不需要的可以刪掉)
按照你所需要的Intent來添加酣难,這里用到的是鍛煉類的:INStartWorkoutIntent菜谣、INEndWorkoutIntent待笑、INCancelWorkoutIntent齐邦、INResumeWorkoutIntent丐吓、INPauseWorkoutIntent。
6勘伺、打開Siri缅帘、并配置主程序plist添加Siri權限,權限也是iOS10新加的,不會的另搜索:
三按价、代碼
1框产、首先添加協(xié)議,管理鍛煉用到的協(xié)議有:
@interface IntentHandler () <INStartWorkoutIntentHandling, INEndWorkoutIntentHandling, INCancelWorkoutIntentHandling, INResumeWorkoutIntentHandling, INPauseWorkoutIntentHandling>
@end
@implementation IntentHandler
2窑多、這里只說INStartWorkoutIntentHandling遥巴,其他類似。
按住command鍵點擊INStartWorkoutIntentHandling進入携栋,可看到有三個階段的方法:handling method鸯隅、Confirmation method蝌以、Resolution methods碍彭。
2.1庇忌、先看解析階段Resolution methods晃酒,可根據(jù)需要來選擇返回哪種類型的參數(shù):
//resolve 解析 Intent 參數(shù)
//鍛煉類型
- (void)resolveWorkoutNameForStartWorkout:(INStartWorkoutIntent *)intent withCompletion:(void (^)(INSpeakableStringResolutionResult * _Nonnull))completion{
NSLog(@"start workoutName = %@",intent.workoutName);
INSpeakableString *text = intent.workoutName;
NSString *workoutName = [NSString stringWithFormat:@"%@",text];
if (text && ![workoutName isEqualToString:@""])
{
completion([INSpeakableStringResolutionResult successWithResolvedString:text]);
} else {
completion([INSpeakableStringResolutionResult needsValue]);
}
}
//鍛煉 是否 受限制
- (void)resolveIsOpenEndedForStartWorkout:(INStartWorkoutIntent *)intent withCompletion:(void (^)(INBooleanResolutionResult * _Nonnull))completion{
NSLog(@"isOpenEnded = %@",intent.isOpenEnded);
NSNumber *text = intent.isOpenEnded;
if (text && ![workoutName isEqualToString:@""]) {
// completion([INBooleanResolutionResult confirmationRequiredWithValueToConfirm:text]);
completion([INBooleanResolutionResult successWithResolvedValue:text]);
} else {
completion([INBooleanResolutionResult needsValue]);
}
}
//鍛煉目標
- (void)resolveGoalValueForStartWorkout:(INStartWorkoutIntent *)intent withCompletion:(void (^)(INDoubleResolutionResult * _Nonnull))completion{
NSLog(@"goalValue = %@",intent.goalValue);
NSNumber *text = intent.goalValue;
if (text && ![text isKindOfClass:[NSNull class]]) {
//confirmation方法會對用戶進行確認詢問 是否根據(jù)此目標進行鍛煉
// completion([INDoubleResolutionResult confirmationRequiredWithValueToConfirm:text]);
completion([INDoubleResolutionResult successWithResolvedValue:[text doubleValue]]);
} else {
completion([INDoubleResolutionResult needsValue]);
}
}
//鍛煉時間
- (void)resolveWorkoutGoalUnitTypeForStartWorkout:(INStartWorkoutIntent *)intent withCompletion:(void (^)(INWorkoutGoalUnitTypeResolutionResult * _Nonnull))completion{
NSLog(@"workoutGoalUnitType = %ld",(long)intent.workoutGoalUnitType);
INWorkoutGoalUnitType text = intent.workoutGoalUnitType;
if (text) {
// completion([INWorkoutGoalUnitTypeResolutionResult confirmationRequiredWithValueToConfirm:INWorkoutGoalUnitTypeMinute]);
completion([INWorkoutGoalUnitTypeResolutionResult successWithResolvedValue:INWorkoutGoalUnitTypeMinute]);
} else {
completion([INWorkoutGoalUnitTypeResolutionResult needsValue]);
}
}
//室內或室外
- (void)resolveWorkoutLocationTypeForStartWorkout:(INStartWorkoutIntent *)intent withCompletion:(void (^)(INWorkoutLocationTypeResolutionResult * _Nonnull))completion{
NSLog(@"workoutLocationType = %ld",(long)intent.workoutLocationType);
INWorkoutLocationType text = intent.workoutLocationType;
if (text) {
// completion([INWorkoutLocationTypeResolutionResult confirmationRequiredWithValueToConfirm:INWorkoutLocationTypeOutdoor]);
completion([INWorkoutLocationTypeResolutionResult successWithResolvedValue:INWorkoutLocationTypeOutdoor]);
} else {
completion([INWorkoutLocationTypeResolutionResult needsValue]);
}
}
2.2处渣、確認階段 Confirmation method:
//confirm 確認請求 并將 UI展示
- (void)confirmStartWorkout:(INStartWorkoutIntent *)intent completion:(void (^)(INStartWorkoutIntentResponse * _Nonnull))completion{
NSLog(@"start confirm");
NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartWorkoutIntent class])];
INStartWorkoutIntentResponse *response = [[INStartWorkoutIntentResponse alloc] initWithCode:INStartWorkoutIntentResponseCodeReady userActivity:userActivity];
completion(response);
}
2.3伶贰、處理階段 handling method,此方法是必須的罐栈,對Siri返回的運動類型進行判斷做出相應操作黍衙。
//handle 處理請求
- (void)handleStartWorkout:(INStartWorkoutIntent *)intent completion:(void (^)(INStartWorkoutIntentResponse * _Nonnull))completion{
NSLog(@"start handle");
NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartWorkoutIntent class])];
NSString *workoutName = [NSString stringWithFormat:@"%@",intent.workoutName];
NSLog(@"start workoutName = %@",workoutName);
//存儲相應的字段給NSUserActivity 為后續(xù)與主程序通信做準備
if ([workoutName isEqualToString:@"跑步"] || [workoutName isEqualToString:@"run"])
{
userActivity.title = @"start_Running";
} else if ([workoutName isEqualToString:@"cycle"])
{
userActivity.title = @"start_Cycling";
} else if ([workoutName isEqualToString:@"walk"])
{
userActivity.title = @"start_Walking";
}
//code 參數(shù) 選擇跳到 APP 中 或者其他
INStartWorkoutIntentResponse *response = [[INStartWorkoutIntentResponse alloc] initWithCode:INStartWorkoutIntentResponseCodeContinueInApp userActivity:userActivity];
completion(response);
}
另外
這里的INStartWorkoutIntentResponse,按住command鍵點擊進入查看發(fā)現(xiàn)有多種返回類型荠诬,這里需要我們自己根據(jù)APP情況判斷后琅翻,設置相應的code參數(shù),讓Siri做出相應操作柑贞,否則會出現(xiàn)【抱歉方椎,你需要在應用中繼續(xù)操作】等問題。
例如 :
1钧嘶、正常情況進入APP可選擇 ContinueInApp
2棠众、若已有一項運動在開始扔用Siri呼叫開始可選擇 FailureOngoingWorkout
四、如何與主程序通信
輕量級通信可采用這個辦法:
1有决、在handling階段在NSUserActivity存儲相應的信息闸拿,字典或字符串都行(如上handling階段代碼)。
2书幕、在AppDelegate新荤,獲取NSUserActivity所存儲的信息。
3台汇、發(fā)送通知給所需要的地方苛骨。
4、接收到通知后對主程序進行相應操作励七。
AppDelegate
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{
NSArray *workoutArr = [userActivity.title componentsSeparatedByString:@"_"];
NSString *workoutName = workoutArr[1];
NSString *workoutStatus = workoutArr[0];
NSDictionary *workout = [[NSDictionary alloc]initWithObjectsAndKeys:
workoutName, @"workoutName",
workoutStatus, @"workoutStatus",
nil];
//創(chuàng)建一個消息對象
NSNotification *notice = [NSNotification notificationWithName:@"siri" object:nil userInfo:workout];
//發(fā)送消息
[[NSNotificationCenter defaultCenter]postNotification:notice];
return YES;
}
- (void)getWorkoutInSiri{
//接收通知
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
//添加觀察者
[center addObserver:self selector:@selector(getSiriInfo:) name:@"siri" object:nil];
}
- (void)getSiriInfo:(NSNotification*)info{
NSLog(@"getSiriInfo = %@",info.userInfo);
}
若集成插件智袭,通知也同樣適用:
- (void)getWorkoutInSiri:(CDVInvokedUrlCommand *)command{
self.callBackId = command.callbackId;
//接收通知
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
//添加觀察者
[center addObserver:self selector:@selector(getSiriInfo:) name:@"siri" object:nil];
}
- (void)getSiriInfo:(NSNotification*)info{
NSLog(@"getSiriInfo = %@",info.userInfo);
// send js callback
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info.userInfo];
[result setKeepCallbackAsBool:YES];
[self.commandDelegate sendPluginResult:result callbackId:self.callBackId];
}
五、編譯
先Run主程序掠抬,再Run->Intent吼野,否則不會進入Intent(控制臺不會打印log、斷點也無法進入)两波,但實際上擴展已經到位了瞳步。
在run->Intent時,會彈出一個窗口讓你選擇想要在哪個App上運行你的擴展腰奋,選擇你的App即可单起。
六劣坊、關于默認勾選的UI Extension
看過多篇資料嘀倒,都有說可以呼出UI界面,也可改變默認界面。但試了多次都無效测蘑,查看官方文檔發(fā)現(xiàn)灌危,好像只支持地圖、支付與發(fā)送消息三種:
但也有資料說鍛煉也會出現(xiàn)碳胳,并配有圖文證明勇蝙,這一點就比較懵逼,希望有懂的大神能指點指點挨约!
七味混、關于【抱歉,你需要在應用中繼續(xù)操作】
有些資料也會說當遇到 【對不起诫惭,你需要在應用里繼續(xù)】翁锡,是因為“我們還需要在工程里添加 CoreLocation 庫,確保能添加到我們編譯過的 Swift 工程中贝攒〉撂埽”
然而添加后也還是會出現(xiàn)时甚,這時候其實是你沒有在三個階段(handling隘弊、Confirmation、Resolution)做出相應的操作荒适,Siri不知道該如何進行下去梨熙。
尾言
以上就是在通過查看資料集成SiriKit實戰(zhàn)中,遇到的一些問題與解決辦法刀诬,希望能幫到和我一樣正在iOS路上摸索前進的程序猿/媛盆友們~如有理解錯誤之處望大神指正咽扇!