在最新的iOS 12系统中,苹果在Siri中加入了一项新的特性-Shortcuts,这项特性允许应用程序暴露一些APP的关键功能给Siri。有了Shortcuts,可以:
- 在Siri提供的建议中不仅可以打开APP,还可以打开APP内部的一些功能
- 会借助shortcuts分析用户行为习惯,在合适的时机锁屏状态下给用户提供合理化的建议和提醒
- 在Shortcuts APP中用户可以自定义一条语音,在对Siri发出语音指令时,Siri会执行预先设定好的一系列操作。
使用Shortcuts
如何给自己的APP创建Shortcuts呢?有两种创建Shortcuts的方式,其一是使用NSUserActivity,其二是使用intents
NSUserActivity
如果我们只想要通过Shortcuts打开APP的某些内容或者是在spotlight索引的内容中增加Shortcuts,那么可以简单地通过NSUserActivity实现
- 在info.plist里添加useractivity的type
<key>NSUserActivityTypes</key>
<array>
<string>com.myapp.name.my-activity-type</string>
</array>
复制代码
- donate shortcuts
NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:@"Bobin.SiriShortCut"];
[userActivity setEligibleForSearch:YES];
[userActivity setEligibleForPrediction:YES];
userActivity.title = [NSString stringWithFormat:@"购买%ld杯%@",model.productNum,model.productName];
userActivity.suggestedInvocationPhrase = @"下单";
userActivity.delegate = self;
self.userActivity = userActivity;
复制代码
- handle shortcuts
我们可以在app delegate中处理shortcuts
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
if([userActivity.activityType isEqual: @"Bobin.SiriShortCut"])
{
self.wkWebViewController = [[WKWebViewController alloc] init];
self.wkWebViewController.loadUrl = userActivity.userInfo[@"url"];
UINavigationController *pushNavigationController = [[UINavigationController alloc] initWithRootViewController:self.wkWebViewController];
self.wkWebViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(dismissPushController)];
[(UINavigationController *)self.window.rootViewController presentViewController:pushNavigationController animated:YES completion:nil];
}
}
复制代码
intents
使用intent实现shortcuts可以做到更加深度定制,实现更复杂的功能,例如无需打开APP使用应用的一些功能。
- 创建自定义intent文件
我们可以在xcode里通过File -> New File, 然后选择 “SiriKit Intent Definition File.”创建自定义的intent文件,如下图
图中category表示intent的类别,编辑器会根据我们创建的这个文件自动生成对应的intent类以及intent处理协议@interface PayIntent : INIntent
@property (readwrite, copy, nullable, nonatomic) NSString *productName;
@property (readwrite, copy, nullable, nonatomic) NSNumber *quantity;
@end
// 处理协议
@protocol PayIntentHandling <NSObject>
@required
- (void)handleShopping:(PayIntent *)intent completion:(void (^)(PayIntentResponse *response))completion NS_SWIFT_NAME(handle(intent:completion:));
@optional
- (void)confirmShopping:(PayIntent *)intent completion:(void (^)(PayIntentResponse *response))completion NS_SWIFT_NAME(confirm(intent:completion:));
@end
复制代码
- donate shortcuts
我们通过前面的intent类创建对应的intent类对象,然后通过INInteraction类的donate方法将动作写入shortcuts
self.payIntent = [[PayIntent alloc] init];
self.payIntent.productName = model.productName;
self.payIntent.quantity = [NSNumber numberWithInteger:model.productNum];
self.interaction = [[INInteraction alloc] initWithIntent:self.payIntent response:nil];
[self.interaction donateInteractionWithCompletion:^(NSError * _Nullable error) {
if(error)
{
NSLog(@"%@",error);
}
else
{
NSLog(@"donate success");
}
}];
复制代码
- handle shortcuts
同样,我们可以在app delegate回调里处理shortcuts
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
PayIntent *intent = (PayIntent *)userActivity.interaction.intent;
if(intent)
{
self.mainViewController = [[MainViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.mainViewController];
self.window.rootViewController = navigationController;
self.shoppingViewController = [[ShoppingViewController alloc] init];
//跳转并根据Intent内容来更新购物车内容
[self.mainViewController.navigationController pushViewController:self.shoppingViewController animated:YES];
}
}
复制代码
如果我们希望做到不打开app的情况下就执行一些app的功能,我们需要创建相应的intent extension,如果我们想要定制打开shortcuts的页面,在创建intent extension的时候可以一并选择创建intent UI extension
创建了intent extension之后,xcode会自动生成intentHandle类来处理我们之前定义的shortcuts
@interface IntentHandler () <PayIntentHandling>
@end
@implementation IntentHandler
-(void)handleShopping:(nonnull PayIntent *)intent completion:(nonnull void (^)(PayIntentResponse * _Nonnull))completion
{
if(intent.quantity == 0)
{
completion([PayIntentResponse failureIntentResponseWithProductName:intent.productName]);
}
completion([PayIntentResponse successIntentResponseWithProductName:intent.productName]);
}
-(void)confirmShopping:(PayIntent *)intent completion:(void (^)(PayIntentResponse *response))completion NS_SWIFT_NAME(confirm(intent:completion:))
{
completion([[PayIntentResponse alloc] initWithCode:PayIntentResponseCodeReady userActivity:nil]);
}
@end
复制代码
如果我们创建了intent UI extension,xcode还会自动生成IntentViewController类,这个类用来展示点击shortcuts后弹出的自定义UI界面
- (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 (completion) {
completion(YES, parameters, [self desiredSize]);
}
if(interaction.intentHandlingStatus == INIntentHandlingStatusSuccess)
{
self.alertLabel.text = @"下单成功";
}
}
复制代码
如此我们便实现了不用打开app,后台执行shortcuts的功能。
苹果建议,即便我们创建了intent extension来处理shortcuts,我们还是应该在app delegate回调里添加对shortcuts的处理
删除shortcuts
苹果提供了方便的API允许我们删除创建的shortcuts
- 对于NSUseractivity实现的shortcuts
NSUserActivityPersistentIdentifier identifier = @"persistentIdentifier";
userActivity.persistentIdentifier = identifier;
// 删除指定shortcut
[NSUserActivity deleteSavedUserActivitiesWithPersistentIdentifiers:identifier completionHandler:^() {
}];
// 删除所有shortcuts
[NSUserActivity deleteAllSavedUserActivitiesWithCompletionHandler:^() {
}];
复制代码
- 对于intent实现的shortcuts
self.interaction = [[INInteraction alloc] initWithIntent:self.payIntent response:nil];
self.interaction.identifier = @"identifier";
self.interaction.groupIdentifier = @"groupIdentifier";
[INInteraction deleteInteractionsWithIdentifiers:self.interaction.identifier completion:nil];
[INInteraction deleteInteractionsWithGroupIdentifier:self.interaction.groupIdentifier completion:nil];
[INInteraction deleteAllInteractionsWithCompletion:nil];
复制代码
如何让Siri提供最优化建议
首先,Siri是怎样提供建议的呢?
- 对于NSUseractivity实现的shortcuts
useractivity提供了一个属性requiredUserInfoKeys,它描述了最少的必要的用户信息以供Siri分析。
- 对于intent实现的shortcuts
我们在定义intent文件的时候可以设置多种不同的参数组合,例如
Siri在收到用户的订单的时候,会基于上面的组合将其分解,以分析其中的规律
关于如何做到一个好的donation,苹果提供了一些建议:
- 动作尽可能具有重复性
- 所有的donation应当具有一致性
- 不要包含具体的时间信息
- 每种用户动作只需donate一次
- 参数尽可能不要潜在依赖关系