iOS 推播語音播報(類似支付寶微信的收款提醒)

2020-10-01 14:00:07

專案需求:

近期專案有個需求,實現類似支付寶微信收款後的語音播報如:支付寶到賬xx元。要求是APP在前臺執行、鎖屏、殺死程序後都會有語音播報。

預想方案:

1.通過UIBackgroundTaskIdentifier不斷向程式索要處理時間(這種方案不知道以前可行,現在好像是最多隻能保持3分鐘的時間,一般30s左右)-fail

2.後臺播放無聲音,保持APP一直執行,但是上架APPStore一般不是音樂類的都無法過審--fail

 

因此,我們現在選擇了遠端推播實現需求:(附DEMO

3.通過遠端推播,在iOS10的時候,釋出了UNNotificationServiceExtension擴充套件,關於此擴充套件,可以網上選擇一些資料iOS10 推播extension之 Service Extension,主要的核心思想就是,在遠端推播到底裝置之前,給你一個修改的機會,我們知道,推播體是有限制的,而且推播體大小也會影響推播的效率,藉助這個,我們可以修改標題、內容,也可以從網路上請求到內容,再去合成一個新的推播。

接下來就是實現手機接收到通知之後播報語音了,關於這個功能的實現在iOS10以後蘋果新增了「推播拓展」UNNotificationServiceExtension,我們可以在這裡操作,在這裡我用的是蘋果官方的AVSpeechSynthesizerAVSpeechUtterance來將接收到的推播內容轉換成語音播報

貌似沒啥問題,但是iOS12.1以後,不在允許在UNNotificationServiceExtension中播放語音了,只有系統提示音,阿歐。。。心好累。。。,沒辦法只好先在想辦法,上網查詢資料發現前輩們果然有解決辦法,哈哈。。。

1.設定遠端推播

2.在收到遠端推播時,呼叫本地推播

3.把播報金額拆分成,一、二、三,四、五...千、百、萬、點、元等一個個音訊檔,根據推播過來的金額進行進行篩選然後按照順序放入陣列,具體的在下面有介紹(caculateNumber方法處理)

4.迴圈(遞迴)傳送本地推播播放專案中的音樂檔案

重點:

功能實現

1.設定 UNNotificationServiceExtension

具體的設定可參考檔案:iOS10 推播extension之 Service Extension你玩過了嗎?,這裡不在囉嗦了

2.設定好之後,然後我們在AppDelegate裡面進行通知的註冊,核心程式碼

// 註冊推播
- (void)registerRemoteNotification{
    UIApplication *application = [UIApplication sharedApplication];
    application.applicationIconBadgeNumber = 0;
    
    if([application respondsToSelector:@selector(registerUserNotificationSettings:)])
    {
        UIUserNotificationType notificationTypes = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];
        [application registerUserNotificationSettings:settings];
    }
    
#if !TARGET_IPHONE_SIMULATOR
    //iOS8 註冊APNS
    if ([application respondsToSelector:@selector(registerForRemoteNotifications)]) {
        [application registerForRemoteNotifications];
    }else{
        UIRemoteNotificationType notificationTypes = UIRemoteNotificationTypeBadge |
        UIRemoteNotificationTypeSound |
        UIRemoteNotificationTypeAlert;
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];
    }
#endif
}

3.然後我們在NotificationService.m檔案內寫入收到json資料解析,獲取推播金額並處理,得到語音檔案的陣列,並播放語音(本地推播 -音訊)檔案

程式碼如下:


#import "NotificationService.h"
#import "XSAudioManager.h"
#import <AVFoundation/AVFoundation.h>
#define kFileManager [NSFileManager defaultManager]

typedef void(^PlayVoiceBlock)(void);

@interface NotificationService ()<AVAudioPlayerDelegate,AVSpeechSynthesizerDelegate>
{
    AVSpeechSynthesizer *synthesizer;
}
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
// AVSpeechSynthesisVoice 播放完畢之後的回撥block
@property (nonatomic, copy)PlayVoiceBlock finshBlock;


//聲音檔案的播放器
@property (nonatomic, strong)AVAudioPlayer *myPlayer;
//聲音檔案的路徑
@property (nonatomic, strong) NSString *filePath;

@end

@implementation NotificationService

/*
 *後臺推播的json案例
 {"aps":{"alert":"錢到啦收款10000元","badge":1,"mutable-content":1,"amount":10000, "sound":"default"}}
 */

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    // Modify the notification content here...
    
    
    //step1: 推播json解析,獲取推播金額
    NSMutableDictionary *dict = [self.bestAttemptContent.userInfo mutableCopy] ;
    NSDictionary *extras =  [dict objectForKey:@"aps"] ;
    BOOL playaudio =  [[extras objectForKey:@"amount"] boolValue] ;
    if(playaudio) {
        
        //step2:先處理金額,得到語音檔案的陣列,並播放語音(本地推播 -音訊)
        NSString *amount = [extras objectForKey:@"amount"] ;//10000
        NSArray *musicArr = [[XSAudioManager sharedInstance] getMusicArrayWithNum:amount];
        __weak __typeof(self)weakSelf = self;
        [[XSAudioManager sharedInstance] pushLocalNotificationToApp:0 withArray:musicArr completed:^{
            // 播放完成後,通知系統
            weakSelf.contentHandler(weakSelf.bestAttemptContent);
        }];
        
    } else {
        //系統通知
        self.contentHandler(self.bestAttemptContent);
    }
}

// 30s的處理時間即將結束時,該方法會被呼叫,最後一次提醒使用者去做處理
- (void)serviceExtensionTimeWillExpire {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    self.contentHandler(self.bestAttemptContent);
}



@end

 

我們定義了一個聲音處理的中間類XSAudioManager,因為擴充套件和APP本身都會使用這個類,所以新建這個檔案的時候,注意勾選Targets

XSAudioManager.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

// 處理完成的callback
typedef void (^XSNotificationPushCompleted)(void) ;

@interface XSAudioManager : NSObject

+ (instancetype)sharedInstance;

//先處理金額,得到語音檔案的陣列
-(NSArray *)getMusicArrayWithNum:(NSString *)numStr;

//迴圈呼叫本地通知,播放音訊檔
-(void)pushLocalNotificationToApp:(NSInteger)index withArray:(NSArray *)tmparray completed:(XSNotificationPushCompleted)completed;


/*
 **系統的語音播報(紅包訊息)
 *AVSpeechSynthesizer(iOS10.0-12.0),之後不支援播報
 */
- (void)speechWalllentMessage:(NSString *)numStr;


@end

NS_ASSUME_NONNULL_END

4.APP在前臺的時候是沒有聲音的,前臺需要走正常的推播解析,走正常的語音播報,呼叫系統的播報方法。

//語音播報紅包訊息
- (void)speechWalllentMessage:(NSString *)numStr {
    
    //播放語音
    // 合成器 控制播放,暫停
    AVSpeechSynthesizer *_synthesizer;
    // 範例化說話的語言,說中文、英文
    AVSpeechSynthesisVoice *_voice;
    _voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh_CN"];
    // 要朗誦,需要一個語音合成器
    _synthesizer = [[AVSpeechSynthesizer alloc] init];
    AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:[NSString stringWithFormat:@"XX到賬%@元",numStr]];
    //指定語音,和朗誦速度
    utterance.voice = _voice;
//    utterance.rate = AVSpeechUtteranceDefaultSpeechRate;
    utterance.rate = 0.55;
    utterance.pitchMultiplier = 1.0f;  //改變音調
//    utterance.volume = 1;
    //啟動
    [_synthesizer speakUtterance:utterance];
    
}

至此,語音播報算是完成了。

 

在iOS12.1以上,遠端推播+本地推播,前臺可以正常播放,後臺、退出的情況下,播放收款到賬提醒。

 

注意事項:
1、在專案target-Capabilities-Background Modes中要記得勾選Remote notifications 這樣設定才可以正常接收推播。並且在設定推播的時候,一定要帶上這個欄位:"mutable -content" ,只有將該欄位設定為1,才可以正常實現功能。

2.音樂檔案要匯入的時候,在target和NotificationServiceExtension的:Build Phases --> Copy BumdleResources-->add資原始檔

 

3.模擬推播可以選擇pusher工具模擬測試,附Git地址:pusher,需要選擇推播證書和手機的DeviceToken

因為之前沒有做過此類功能,也是借鑑了很多大牛的解決方案,每個借鑑都有帶的連結,如果有侵權請聯絡我刪除。目前就總結這麼多,有更好的想法希望可以在評論裡一起交流。

 

參考檔案:

iOS 12.1 語音播報問題

iOS開發 - 語音播報功能的實現

 

測試程式碼DEMO