iOS-Siri喚起銀行類app (語音轉(zhuǎn)賬)

前言

最近公司App要實(shí)現(xiàn)下圖這樣一個(gè)功能,對(duì)iPhone手機(jī)喊 " 嘿,Siri,余額 ”或者 " 嘿,Siri,轉(zhuǎn)賬 ” 出現(xiàn)下面的列表,結(jié)果列表中展示我們的APP。

列表.png

百度了很久,沒有找到這個(gè)是什么功能,有大佬指點(diǎn)我到官網(wǎng)查詢一下,通過查閱發(fā)現(xiàn)官網(wǎng)有一個(gè)這樣的文檔 Adding User Interactivity with Siri Shortcuts and the Shortcuts App ,但是通過查閱配置步驟,貌似感覺講的像是設(shè)置如何捷徑,感覺自己這個(gè)需求又不像是Siri Shortcuts(捷徑)功能。最后有一個(gè)朋友給我指點(diǎn),應(yīng)該是Siri語音轉(zhuǎn)賬類。

image.png

至此,找對(duì)了方向開始調(diào)研。

步驟:

一、 工程基本配置

創(chuàng)建一個(gè)普通的xcode工程,然后進(jìn)行如下配置
1、 在工程的 Signing&Capabilities 中,點(diǎn)擊 +Capability ,添加Siri

image01.png

image02.png

2、 添加siri權(quán)限申請(qǐng)
Privacy - Siri Usage Description
使用siriKit,進(jìn)行快捷轉(zhuǎn)賬

image03.png

3、 在 AppDelegate 中,導(dǎo)入 #import <Intents/Intents.h> 頭文件,添加如下代碼

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [INPreferences requestSiriAuthorization:^(INSiriAuthorizationStatus status) {
        NSLog(@"%ld", (long)status);
    }];
    
    return YES;
}

此時(shí)運(yùn)行工程,會(huì)出現(xiàn)下圖申請(qǐng)權(quán)限的界面

image04.png

點(diǎn)擊,至此,基本工程配置完畢。

二、 添加Siri擴(kuò)展

1、 點(diǎn)擊 Xcode -> File -> New -> Target ,選擇 iOS -> Intents Extension 。

image05.png

image06.png
image07.png

填寫 Product Name 點(diǎn)擊 Finish 完成操作, 此時(shí)會(huì)彈出提示框,選擇 Active 。

image08.png

至此,會(huì)新增兩個(gè) Target , SiriExtensionSiriExtensionUI

image09.png
三、 Target , SiriExtensionSiriExtensionUI 配置

1、 對(duì) SiriExtension -> info.plist -> NSExtension -> NSExtensionAttributes 中的鍵值對(duì)進(jìn)行調(diào)整,調(diào)整前和調(diào)整后如下所示:

image10.png

調(diào)整后為:INSendPaymentIntent

image11.png

2、 對(duì) SiriExtensionUI 也進(jìn)行相同的配置, SiriExtensionUI 只需要配置 IntentsSupported ,調(diào)整后如下:

image12.png

調(diào)整后為:INSendPaymentIntent

image13.png

基本準(zhǔn)備完成,接下來進(jìn)入代碼編寫。

四、 SiriExtension 編寫代碼

1、 在SiriExtension目錄下,創(chuàng)建一個(gè)SiriExtensionIntentHandler : NSObject的類。

image14.png

2、接下來我們編寫 SiriExtensionIntentHandler類中的代碼

@implementation SiriExtensionIntentHandler

////Resolve - payee 解析收款人 iOS10.0
//- (void)resolvePayeeForSendPayment:(INSendPaymentIntent *)intent withCompletion:(void (^)(INPersonResolutionResult * _Nonnull))completion {
//    if (intent.payee == nil || !intent.payee.displayName.length) {
//        //如果收款人為空,那么請(qǐng)求siri,需要收款人
//        INPersonResolutionResult *resolutionResult = [INPersonResolutionResult notRequired];
//        completion(resolutionResult);
//    }
//    else {
//        //收款人不為空,確認(rèn)收款人信息
//        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 needsValue];
//        completion(resolutionResult);
//    }
//    else {
//        INSendPaymentPayeeResolutionResult *resolutionResult = [INSendPaymentPayeeResolutionResult successWithResolvedPerson:intent.payee];
//        completion(resolutionResult);
//    }
//}
//

//Resolve - currency 解析貨幣 iOS10.0
- (void)resolveCurrencyAmountForSendPayment:(INSendPaymentIntent *)intent withCompletion:(void (^)(INCurrencyAmountResolutionResult * _Nonnull))completion {
    INCurrencyAmount *currencyAmount = intent.currencyAmount;
    if (currencyAmount == nil) {
        //金額為空,請(qǐng)求siri,需要轉(zhuǎn)賬金額
        INCurrencyAmountResolutionResult *resolutionResult = [INCurrencyAmountResolutionResult needsValue];
        completion(resolutionResult);
    }
    else if ([currencyAmount.currencyCode isEqualToString:@"CNY"]) {
        //如果幣種不是人民幣,將接收到的幣種轉(zhuǎn)化為人民幣
        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"]) {
        //貨幣格式轉(zhuǎn)化為人民幣
        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 - 確認(rèn)金額信息
- (void)confirmSendPayment:(INSendPaymentIntent *)intent completion:(void (^)(INSendPaymentIntentResponse * _Nonnull))completion {
    NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendPaymentIntent class])];
    userActivity.title = @"轉(zhuǎn)賬";
    userActivity.userInfo = @{@"displayName": intent.payee.displayName?:@"",
                              @"amount": intent.currencyAmount.amount};
    
    //確認(rèn)支付貨幣,否則系統(tǒng)會(huì)默認(rèn)展示USD(美元)
    INPaymentMethod *paymentMethod = [INPaymentMethod applePayPaymentMethod];
    //status字段決定了siri轉(zhuǎn)賬頁面底部的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 - 轉(zhuǎn)賬處理
- (void)handleSendPayment:(INSendPaymentIntent *)intent completion:(void (^)(INSendPaymentIntentResponse * _Nonnull))completion {
    NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendPaymentIntent class])];
    userActivity.title = @"轉(zhuǎn)賬";
    userActivity.userInfo = @{@"displayName": intent.payee.displayName?:@"",
                              @"amount": intent.currencyAmount.amount};
    INSendPaymentIntentResponse *sendPaymentIntentResponse = [[INSendPaymentIntentResponse alloc] initWithCode:(INSendPaymentIntentResponseCodeInProgress) userActivity:userActivity];
    completion(sendPaymentIntentResponse);
}

@end

3、 編寫SiriExtensionUI --> IntentViewController 類中代碼的實(shí)現(xiàn)
3.1 首先編寫SiriExtensionUI --> MainInterface.storyboard的UI界面

image15.png

3.2 編寫IntentViewController類中代碼的實(shí)現(xiàn):

#import "IntentViewController.h"
#import <Intents/Intents.h>

@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 <INParameter *> *)parameters ofInteraction:(INInteraction *)interaction interactiveBehavior:(INUIInteractiveBehavior)interactiveBehavior context:(INUIHostedViewContext)context completion:(void (^)(BOOL success, NSSet <INParameter *> *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

至此,所有代碼的編寫已經(jīng)完成,此時(shí)我們跑一下真機(jī),可以看到APP已經(jīng)安裝到我們手機(jī),對(duì)iPhone手機(jī)喊 " 嘿,Siri,余額 ”或者 " 嘿,Siri,轉(zhuǎn)賬 ” 出現(xiàn)下面的列表,結(jié)果列表中展示我們的APP了。

image16.png

Demo下載

注意:

  1. 我們的Demo中有 SiriExtensionSiriExtensionUI擴(kuò)展,所以這兩個(gè)擴(kuò)展的bundleID要和主工程保持一樣的規(guī)范。
    例:
    主工程bundleID: com.ddq.siriextension
    SiriExtension擴(kuò)展的bundleID: com.ddq.siriextension.SiriExtension
    SiriExtensionUI擴(kuò)展的bundleID: com.ddq.siriextension.SiriExtensionUI
    要在主工程的后面,添加對(duì)應(yīng)的點(diǎn) “.”后綴名。
    證書的創(chuàng)建亦是如此:
    image.png

2.主工程的bundleID需要開啟證書的Siri功能;

image.png

3.如果小伙伴下載我的demo運(yùn)行報(bào)錯(cuò),需要替換主工程和target為你們自己的bundleID。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容