前言
前一阵公司App要实现Siri语音转账的功能,百度了很久,没有找到详细的教程,于是在查阅了多年WWDC视频资料后,终于实现了,今天就这一个功能详细的讲解一下,希望对大家有所帮助。
一、工程基本配置
创建一个普通的xcode工程,然后进行如下配置
1、在工程的 Signing&Capabilities
中,点击 +Capability
,添加Siri
添加完成后是这个样子的
2、添加siri权限申请
3、在
AppDelegate
中,导入 #import
头文件,添加如下代码
#import "AppDelegate.h"
#import
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[INPreferences requestSiriAuthorization:^(INSiriAuthorizationStatus status) {
NSLog(@"%ld", (long)status);
}];
return YES;
}
此时运行工程,会出现Siri权限申请,点击好
。
至此,工程基本配置完成,接下来进行第二步。
二、添加Siri扩展
1、依次点击 Xcode
-> File
-> New
-> Target
,选择 iOS
-> Intents Extension
。
填写
Product Name
点击 Finish
完成操作。 Include UI Extension
默认勾选,不要取消选中,后续需要用到。
此时会弹出提示框,选择
Active
。
至此,会新增两个
Target
, SIRIPaymentExtension
和 SIRIPaymentExtensionUI
,接下来我们进行第三步。
三、 SIRIPaymentExtension
、 SIRIPaymentExtensionUI
配置
1、对 SIRIPaymentExtension
-> info.plist
-> NSExtension
-> NSExtensionAttributes
中的 IntentsRestrictedWhileLocked
以及 IntentsSupported
进行调整,删除现有的配置,添加 INSendPaymentIntent
(转账),调整后如下
IntentsSupported
表示所支持的功能,IntentsRestrictedWhileLocked
表示解锁手机才能使用的功能,因为转账属于隐私的功能,所以需要解锁手机才能使用。
2、对
SIRIPaymentExtensionUI
也进行相同的配置, SIRIPaymentExtensionUI
只需要配置 IntentsSupported
,调整后如下。
准备工作基本完成,接下来进入代码编写。
四、 SIRIPaymentExtension
编写代码
1、 SIRIPaymentExtension
目录,创建一个继承自 NSObject
的转账处理类,这里我们命名为 SIRISendPaymentIntentHandler
,点击 Next
,Targets选择 SIRIPaymentExtension
。
2、调整默认创建的
IntentHandler
代码结构,删除无用代码,调整后的代码如下
//
// IntentHandler.m
// SIRIPaymentExtension
//
// Created by 宋法键 on 2020/9/24.
// Copyright © 2020 SFJ. All rights reserved.
//
#import "IntentHandler.h"
#import "SIRISendPaymentIntentHandler.h"
@interface IntentHandler ()
@end
@implementation IntentHandler
- (id)handlerForIntent:(INIntent *)intent {
// 如果是转账请求,那么返回我们自己创建的转账处理类 SIRISendPaymentIntentHandler
if ([intent isKindOfClass:[INSendPaymentIntent class]]) {
return [[SIRISendPaymentIntentHandler alloc] init];
}
return self;
}
@end
3、接下来我们编写 SIRISendPaymentIntentHandler
中的代码
为实现Siri转账,首先我们要添加
代理方法
#import "SIRISendPaymentIntentHandler.h"
#import
@interface SIRISendPaymentIntentHandler ()
@end
然后实现以下几个功能
- 解析收款人(
Resolve
)
@implementation SIRISendPaymentIntentHandler
//Resolve - payee 解析收款人 iOS10.0
- (void)resolvePayeeForSendPayment:(INSendPaymentIntent *)intent withCompletion:(void (^)(INPersonResolutionResult * _Nonnull))completion {
if (intent.payee == nil || !intent.payee.displayName.length) {
//如果收款人为空,那么请求siri,需要收款人或者不处理
//Siri对中文名字的识别有些问题,所以最好写notRequired
INPersonResolutionResult *resolutionResult = [INPersonResolutionResult notRequired];
completion(resolutionResult);
}
else {
//收款人不为空,确认收款人信息
INPersonResolutionResult *resolutionResult = [INPersonResolutionResult successWithResolvedPerson:intent.payee];
completion(resolutionResult);
}
}
//Resolve - payee 解析收款人 iOS11.0
- (void)resolvePayeeForSendPayment:(INSendPaymentIntent *)intent completion:(void (^)(INSendPaymentPayeeResolutionResult * _Nonnull))completion API_AVAILABLE(ios(11.0)){
if (intent.payee == nil) {
INSendPaymentPayeeResolutionResult *resolutionResult = [INSendPaymentPayeeResolutionResult notRequired];
completion(resolutionResult);
}
else {
INSendPaymentPayeeResolutionResult *resolutionResult = [INSendPaymentPayeeResolutionResult successWithResolvedPerson:intent.payee];
completion(resolutionResult);
}
}
- 解析货币以及金额(
Resolve
)
//Resolve - currency 解析货币 iOS10.0
- (void)resolveCurrencyAmountForSendPayment:(INSendPaymentIntent *)intent withCompletion:(void (^)(INCurrencyAmountResolutionResult * _Nonnull))completion {
INCurrencyAmount *currencyAmount = intent.currencyAmount;
if (currencyAmount == nil) {
//金额为空,请求siri,需要转账金额
INCurrencyAmountResolutionResult *resolutionResult = [INCurrencyAmountResolutionResult needsValue];
completion(resolutionResult);
}
else if ([currencyAmount.currencyCode isEqualToString:@"CNY"]) {
//如果币种不是人民币,将接收到的币种转化为人民币
INCurrencyAmount *newCurrencyAmount = [[INCurrencyAmount alloc] initWithAmount:currencyAmount.amount currencyCode:@"CNY"];
INCurrencyAmountResolutionResult *resolutionResult = [INCurrencyAmountResolutionResult successWithResolvedCurrencyAmount:newCurrencyAmount];
completion(resolutionResult);
}
else {
INCurrencyAmountResolutionResult *resolutionResult = [INCurrencyAmountResolutionResult successWithResolvedCurrencyAmount:currencyAmount];
completion(resolutionResult);
}
}
//Resolve - currency 解析货币单位 iOS11.0
- (void)resolveCurrencyAmountForSendPayment:(INSendPaymentIntent *)intent completion:(void (^)(INSendPaymentCurrencyAmountResolutionResult * _Nonnull))completion API_AVAILABLE(ios(11.0)){
INCurrencyAmount *currencyAmount = intent.currencyAmount;
if (currencyAmount == nil || currencyAmount.amount == nil) {
INSendPaymentCurrencyAmountResolutionResult *resolutionResult = [INSendPaymentCurrencyAmountResolutionResult needsValue];
completion(resolutionResult);
}
else if (![currencyAmount.currencyCode isEqualToString:@"CNY"]) {
//货币格式转化为人民币
INCurrencyAmount *newCurrencyAmount = [[INCurrencyAmount alloc] initWithAmount:currencyAmount.amount currencyCode:@"CNY"];
INSendPaymentCurrencyAmountResolutionResult *resolutionResult = [INSendPaymentCurrencyAmountResolutionResult successWithResolvedCurrencyAmount:newCurrencyAmount];
completion(resolutionResult);
} else {
INSendPaymentCurrencyAmountResolutionResult *resolutionResult = [INSendPaymentCurrencyAmountResolutionResult successWithResolvedCurrencyAmount:currencyAmount];
completion(resolutionResult);
}
}
- 确认货币以及金额(
Confirm
)
//Confirm - 确认金额信息
- (void)confirmSendPayment:(INSendPaymentIntent *)intent completion:(void (^)(INSendPaymentIntentResponse * _Nonnull))completion {
NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendPaymentIntent class])];
userActivity.title = @"转账";
userActivity.userInfo = @{@"displayName": intent.payee.displayName?:@"",
@"amount": intent.currencyAmount.amount};
//确认支付货币,否则系统会默认展示USD(美元)
INPaymentMethod *paymentMethod = [INPaymentMethod applePayPaymentMethod];
//status字段决定了siri转账页面底部的UI
INPaymentRecord *paymentRecord = [[INPaymentRecord alloc] initWithPayee:intent.payee payer:nil currencyAmount:intent.currencyAmount paymentMethod:paymentMethod note:intent.note status:(INPaymentStatusPending)];
INSendPaymentIntentResponse *sendPaymentIntentResponse = [[INSendPaymentIntentResponse alloc] initWithCode:(INSendPaymentIntentResponseCodeReady) userActivity:userActivity];
sendPaymentIntentResponse.paymentRecord = paymentRecord;
completion(sendPaymentIntentResponse);
}
- 处理请求(
Handle
)
//Handle - 转账处理
- (void)handleSendPayment:(INSendPaymentIntent *)intent completion:(void (^)(INSendPaymentIntentResponse * _Nonnull))completion {
NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendPaymentIntent class])];
userActivity.title = @"转账";
userActivity.userInfo = @{@"displayName": intent.payee.displayName?:@"",
@"amount": intent.currencyAmount.amount};
INSendPaymentIntentResponse *sendPaymentIntentResponse = [[INSendPaymentIntentResponse alloc] initWithCode:(INSendPaymentIntentResponseCodeInProgress) userActivity:userActivity];
completion(sendPaymentIntentResponse);
}
4、编写 IntentViewController
中的代码
//
// IntentViewController.m
// MobileBankSiriExtensionUI
//
// Created by 宋法键 on 2020/7/29.
// Copyright © 2020 Alibaba. All rights reserved.
//
#import "IntentViewController.h"
#import
// As an example, this extension's Info.plist has been configured to handle interactions for INSendMessageIntent.
// You will want to replace this or add other intents as appropriate.
// The intents whose interactions you wish to handle must be declared in the extension's Info.plist.
// You can test this example integration by saying things to Siri like:
// "Send a message using "
@interface IntentViewController ()< INUIHostedViewSiriProviding>
@property (weak, nonatomic) IBOutlet UILabel *amountLabel;
@end
@implementation IntentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark - INUIHostedViewSiriProviding
- (BOOL)displaysPaymentTransaction {
return YES;;
}
#pragma mark - INUIHostedViewControlling
- (void)configureWithInteraction:(INInteraction *)interaction context:(INUIHostedViewContext)context completion:(void (^)(CGSize))completion {
if ([interaction.intent isKindOfClass:[INSendPaymentIntent class]] && (interaction.intentHandlingStatus == INIntentHandlingStatusReady)) {
INSendPaymentIntent *sendPaymentIntent = (INSendPaymentIntent *)interaction.intent;
self.amountLabel.text = [NSString stringWithFormat:@"¥%.2f", sendPaymentIntent.currencyAmount.amount.doubleValue];
if (completion) {
completion(CGSizeMake([self desiredSize].width, 200));
}
} else {
if (completion) {
completion(CGSizeZero);
}
}
}
// Prepare your view controller for the interaction to handle.
- (void)configureViewForParameters:(NSSet *)parameters ofInteraction:(INInteraction *)interaction interactiveBehavior:(INUIInteractiveBehavior)interactiveBehavior context:(INUIHostedViewContext)context completion:(void (^)(BOOL success, NSSet *configuredParameters, CGSize desiredSize))completion API_AVAILABLE(ios(11.0)){
// Do configuration here, including preparing views and calculating a desired size for presentation.
if ([interaction.intent isKindOfClass:[INSendPaymentIntent class]] &&
(interaction.intentHandlingStatus == INIntentHandlingStatusReady) &&
[[parameters anyObject].parameterKeyPath isEqualToString:@"currencyAmount"]) {
INSendPaymentIntent *sendPaymentIntent = (INSendPaymentIntent *)interaction.intent;
self.amountLabel.text = [NSString stringWithFormat:@"¥%.2f", sendPaymentIntent.currencyAmount.amount.doubleValue];
completion(YES, parameters, CGSizeMake([self desiredSize].width, 200));
} else {
completion(YES, parameters, CGSizeZero);
}
}
- (CGSize)desiredSize {
return [self extensionContext].hostedViewMaximumAllowedSize;
}
@end
MainInterface.storyboard
中的截图如下
至此,Siri转账的处理完成,我们来开一下运行的效果
但是,我们还没有接收Siri传递过来的转账信息,所以,我们需要在 AppDelegate
中接收以下传递过来的信息。
五、转账信息接收与处理
Siri打开App以后我们需要接收以下传递过来的收款人以及金额,因此,在AppDelegate
添加如下代码
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> * _Nullable))restorationHandler
{
if (@available(iOS 10.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([userActivity.interaction.intent isKindOfClass:[INSendPaymentIntent class]]) {
INSendPaymentIntent *sendPaymentIntent = (INSendPaymentIntent *)(userActivity.interaction.intent);
NSString *displayName = sendPaymentIntent.payee.displayName;
NSString *amount = [NSString stringWithFormat:@"%@", sendPaymentIntent.currencyAmount.amount];
NSLog(@"displayName: %@ -- amount: %@", displayName, amount);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSString *title = [NSString stringWithFormat:@"收款人:%@", displayName];
NSString *message = [NSString stringWithFormat:@"金额:%@", amount];
UIAlertView *alt = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:@"好" otherButtonTitles: nil];
[alt show];
});
}
});
}
return YES;
}
到这里,你已经可以拿到所需要的收款人姓名以及金额了,接下来就看自己的业务需要,然后进行处理了
小技巧
选中 SIRIPaymentExtension
target 然后 Edit Scheme
,在 Run
-> Info
-> Siri Intent Query
添加你想要说的信息,比如“给张帆转账100元”,然后直接运行,即可,就不需要傻傻的对着手机说了。
附
https://github.com/belink-QD/SIRIPayment