iOS 12 新特性 Sirishortcut(捷径)调研(二)
通过Intents创建捷径
intents的方式实现捷径,可以做更深一步的定制,甚至无需打开APP使用应用的一些功能
首先创建Intents.intentdefinition文件
通过File -> New File, 然后选择 “SiriKit Intent Definition File.”创建自定义的intent文件,步骤如下图:
Sirishortcut_new_intent.jpg创建好的intent文件如下:
Sirishortcut_intent_content.jpg每一个intent都对应一个response如下图:
Sirishortcut_response_content.jpg创建完成后,编译Intents.intentdefinition关联的target xcode会自动生成对应的类文件,但是在工程中找不到类文件,可以通过如下方法查看:
Sirishortcut_look_info.jpg编译后xcode自动生成的类文件如下:
Sirishortcut_Intent_class_info.jpg
类文件包含QuickOrderCoffeeIntent类实现以及 QuickOrderCoffeeIntentHandling协议和 QuickOrderCoffeeIntentResponse类等所需要的内容。
下面就可以在工程中直接 #import "QuickOrderCoffeeIntent.h"使用。
这里我们想要在用户再点击来一单的时候,创建快捷下单的shortcut
- (void)donateInteraction {
if (@available(iOS 12.0, *)) {
QuickOrderCoffeeIntent *quickOrderIntent = [[QuickOrderCoffeeIntent alloc] init];
quickOrderIntent.name = @"CafeAmericano";
quickOrderIntent.kind = @"Americano";
INInteraction *interaction = [[INInteraction alloc] initWithIntent:quickOrderIntent response:nil];
[interaction donateInteractionWithCompletion:^(NSError * _Nullable error) {
}];
}
}
自定义的Intent如果没有关联到主工程的target,要先进行关联
自定义intent的内容到此就结束了,下面需要结合Intent Extension 和Intent UI Extension深度定制捷径。
添加Intents Extension 和Intents Extension UI
通过target添加Siri扩展 new -> target 如下图:
Sirishortcut_new_target.jpg这里新建名为QuickOrder的target 同时勾选 UI Extentsion如下图
Sirishortcut_new_ui_target.jpg这时就会同时创建Siri Extentsion 和 Siri ExtentSion UI 两个target如下图:
Sirishortcut_target_list.jpg接下来需要修改Extentsion 和Extentsion UI 目录下的info.plist 将之前自定义的type注册进去,如下图
Sirishortcut_infoPlist_type_string.jpg下面来看一下创建的Extentsion 和Extentsion UI
IntentHandler 类,导入自定义的Intent 遵循协议
#import "QuickOrderCoffeeIntent.h"
NS_ASSUME_NONNULL_BEGIN
@interface QuickOrderIntentHandler : INExtension<QuickOrderCoffeeIntentHandling>
@end
NS_ASSUME_NONNULL_END
实现如下两个方法
- (void)confirmQuickOrderCoffee:(QuickOrderCoffeeIntent *)intent completion:(void (^)(QuickOrderCoffeeIntentResponse *response))completion NS_SWIFT_NAME(confirm(intent:completion:)) {
NSLog(@"%s",__func__);
if (!isLogin) {//如果没有登录 则返回之前定义的code QuickOrderCoffeeIntentResponseCodeFaiureUnLogin
QuickOrderCoffeeIntentResponse *unLoginResponse = [[QuickOrderCoffeeIntentResponse alloc] initWithCode:QuickOrderCoffeeIntentResponseCodeFaiureUnLogin userActivity:nil];
completion(unLoginResponse);
} else {
if (haveProduct) {//如果有库存 返回ready
QuickOrderCoffeeIntentResponse *readyResponse = [[QuickOrderCoffeeIntentResponse alloc] initWithCode:QuickOrderCoffeeIntentResponseCodeReady userActivity:nil];
completion(readyResponse);
} else { //抛出没有库存的code返回
QuickOrderCoffeeIntentResponse *outOfResponse = [[QuickOrderCoffeeIntentResponse alloc] initWithCode:QuickOrderCoffeeIntentResponseCodeFailureOutOfStock userActivity:nil];
completion(outOfResponse);
}
}
QuickOrderCoffeeIntentResponse *defaultResponse = [[QuickOrderCoffeeIntentResponse alloc] initWithCode:QuickOrderCoffeeIntentResponseCodeFailure userActivity:nil];
completion(defaultResponse);
}
- (void)handleQuickOrderCoffee:(nonnull QuickOrderCoffeeIntent *)intent completion:(nonnull void (^)(QuickOrderCoffeeIntentResponse * _Nonnull))completion {
NSLog(@"%s",__func__);
QuickOrderCoffeeIntentResponse *quickOrderresonse = [[QuickOrderCoffeeIntentResponse alloc] initWithCode:QuickOrderCoffeeIntentResponseCodeSuccess userActivity:nil];
completion(quickOrderresonse);
}
下面讲解一下上述代码做了什么
Intent处理程序涉及三个步骤:
- 解析(Resolve)
- 确认(Confirm)
-
处理(Handle)
shortcut 没有了Siri解析的过程,会直接进行 confirm 和 handle
这里对应confirmQuickOrderCoffee 和 handleQuickOrderCoffee
confirm阶段
预订咖啡首先确认登录状态,如果没有登录 可以抛出自定义未登录的code QuickOrderCoffeeIntentResponseCodeFaiureUnLogin 提供给UI层做相关处理你可以根据程序所处的状态码,渲染对应的UI,如果是未登录可以打开APP进行登录(QuickOrderCoffeeIntentResponseCodeFailureRequiringAppLaunch)
在进行库存的判断 如果没有库存 抛出 QuickOrderCoffeeIntentResponseCodeFailureOutOfStock
一系列检查之后,下单的条件满足了 我们可以返回 QuickOrderCoffeeIntentResponseCodeReady
handle阶段
可以进行下单的请求,处理完成后,如果下单成功,可以回调 QuickOrderCoffeeIntentResponseCodeSuccess
以上就是下单的大概流程
IntentViewController 类 作为自定义UI的视图控制器,可以根据下单的步骤,做相关的UI渲染。这里创建了两种状态的视图,分别显示商品信息,和预订结果
Sirishortcut_UI_Class@2x.jpg// Prepare your view controller for the interaction to handle.
- (void)configureViewForParameters:(NSSet <INParameter *> *)parameters ofInteraction:(INInteraction *)interaction interactiveBehavior:(INUIInteractiveBehavior)interactiveBehavior context:(INUIHostedViewContext)context completion:(void (^)(BOOL success, NSSet <INParameter *> *configuredParameters, CGSize desiredSize))completion {
// Do configuration here, including preparing views and calculating a desired size for presentation.
if (interaction.intentHandlingStatus == INIntentHandlingStatusReady) {
[self.view addSubview:self.quickOrderVC.view];
[self.quickOrderVC.view setTranslatesAutoresizingMaskIntoConstraints:NO];
//设置宽高
[[self.quickOrderVC.view.widthAnchor constraintEqualToAnchor:self.view.widthAnchor] setActive:YES];
[[self.quickOrderVC.view.heightAnchor constraintEqualToAnchor:self.view.heightAnchor] setActive:YES];
[[self.quickOrderVC.view.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor] setActive:YES];
[[self.quickOrderVC.view.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:YES];
[self.quickOrderVC didMoveToParentViewController:self];
if (completion) {
completion(YES, parameters, [self desiredSize]);
}
} else if (interaction.intentHandlingStatus == INIntentHandlingStatusSuccess) {
[self.view addSubview:self.orderResultVC.view];
[self.orderResultVC.view setTranslatesAutoresizingMaskIntoConstraints:NO];
//设置宽高
[[self.orderResultVC.view.widthAnchor constraintEqualToAnchor:self.view.widthAnchor] setActive:YES];
[[self.orderResultVC.view.heightAnchor constraintEqualToAnchor:self.view.heightAnchor] setActive:YES];
[[self.orderResultVC.view.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor] setActive:YES];
[[self.orderResultVC.view.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:YES];
[self.orderResultVC didMoveToParentViewController:self];
if (completion) {
completion(YES, parameters, [self desiredSize]);
}
}
else {
if (completion) {
completion(NO, parameters, [self desiredSize]);
}
}
}
以上方法 根据 IntentHandler类中返回的状态code,做对应的UI渲染
code 如果是系统自动创建的,会和 interaction.intentHandlingStatus 相互对应。如果是自定义的code,他们的 intentHandlingStatus 都对应INIntentHandlingStatusSuccess
可以参考如下代码做相关处理
if ([interaction.intentResponse isKindOfClass:[QuickOrderCoffeeIntentResponse class]]) {
QuickOrderCoffeeIntentResponse *quickOrderResponse = (QuickOrderCoffeeIntentResponse *)interaction.intentResponse;
if (quickOrderResponse.code == QuickOrderCoffeeIntentResponseCodeFaiureUnLogin) {
///MARK:未登录操作
} else if (quickOrderResponse.code == QuickOrderCoffeeIntentResponseCodeFailureOutOfStock) {
///MARK:没有库存
}
}
核心的功能到这里就算完成了。回到最开始的需求,我们希望在用户点击再来一单的时候,生成快捷下单的shortcut
donate 的代码如下:
- (void)donateInteraction {
if (@available(iOS 12.0, *)) {
QuickOrderCoffeeIntent *quickOrderIntent = [[QuickOrderCoffeeIntent alloc] init];
quickOrderIntent.name = @"CafeAmericano";
quickOrderIntent.kind = @"Americano";
INInteraction *interaction = [[INInteraction alloc] initWithIntent:quickOrderIntent response:nil];
[interaction donateInteractionWithCompletion:^(NSError * _Nullable error) {
}];
}
}
我们还可以引导用户将快捷下单的shortcut添加到Siri短语
- (void)addShortcutsToSiri {
if (@available(iOS 12.0, *)) {
QuickOrderCoffeeIntent *quickOrderIntent = [[QuickOrderCoffeeIntent alloc] init];
INShortcut *shortcut = [[INShortcut alloc] initWithIntent:quickOrderIntent];
INUIAddVoiceShortcutViewController *vc = [[INUIAddVoiceShortcutViewController alloc] initWithShortcut:shortcut];
vc.delegate = self;
[self presentViewController:vc animated:YES completion:nil];
}
}
通过Intent的方式定义Sirishortcut到这里就算完成了。