該功能為推送高級(jí)功能,主要講解app殺死進(jìn)程或者后臺(tái)情況下收到推送后募强,播放語(yǔ)音坠非。實(shí)際該需求呢希柿,就是類似于支付寶在沒有運(yùn)行的情況下收到推送消息腐芍,然后播報(bào)收錢的功能,接下來(lái)給大家講解一下具體配置以及代碼實(shí)現(xiàn)被啼。
一帜消、首先需要大家了解下iOS 10 UNNotificationServiceExtension 這個(gè)類。
1.選擇創(chuàng)建“NotificationServiceExtension”
第一步浓体,先選擇新建 NEW——>Target
第二步泡挺,選擇UNNotificationServiceExtension擴(kuò)展類,不是左邊的汹碱,他們的用途不一樣,UNNotificationContentExtensior主要用戶自定義推送UI
第三步荞估,擴(kuò)展類的名字可以隨便起咳促,根據(jù)自己的愛好
創(chuàng)建完成之后,項(xiàng)目中會(huì)多一個(gè)VocalPush目錄勘伺,且Scheme中也同樣增加VocalPush
二跪腹、配置擴(kuò)展
1.添加功能支持
2.info.plist中:添加“App Transport Security Settings”字典 ,字典中添加“Allow Arbitrary Loads”設(shè)置為YES
3.把擴(kuò)展里的Deployment Target 改成 10.0之后版本飞醉,因?yàn)?0.0之后才支持推送擴(kuò)展
4.基本完成配置工作冲茸,要想體驗(yàn)效果,先運(yùn)行一下擴(kuò)展缅帘,然后再選擇運(yùn)行一下項(xiàng)目轴术。
5.后端發(fā)推送消息的時(shí)候,改方式一定要注意,在aps里一定要有"mutable -content"這個(gè)字段,值為1钦无。不設(shè)置mutable -content = 1逗栽,不能實(shí)現(xiàn)該方式推送處理。
{
"aps" : {
"content-available" : 1,
"alert" : {
"title" : "通知",
"body" : "XTH到賬45元"
},
"badge" : 0,
"sound" : "default"
}
}
三失暂、代碼實(shí)現(xiàn)處理
1.NotificationService.m文件內(nèi)的代碼彼宠,就是iOS10的推送擴(kuò)展鳄虱。
實(shí)際使用的是設(shè)置self.bestAttemptContent.sound的方式,而現(xiàn)階段蘋果也只能通過(guò)設(shè)置該屬性去實(shí)現(xiàn)播放語(yǔ)音的功能凭峡。在我的項(xiàng)目中用的是提前錄制好的語(yǔ)音文件拙已,而支付寶,是使用文字轉(zhuǎn)語(yǔ)音文件的方式摧冀,之后再設(shè)置該屬性去實(shí)現(xiàn)的語(yǔ)音播放倍踪,你也可以通過(guò)網(wǎng)絡(luò)下載語(yǔ)音文件之后播放的方式去實(shí)現(xiàn)同樣的效果。
網(wǎng)上有很多比較老的文章按价,筆者在研究此功能的時(shí)候也走了不少?gòu)澛凡咽剩热缭跀U(kuò)展類NotificationService中,實(shí)現(xiàn)系統(tǒng)語(yǔ)音播報(bào)MediaPlayer楼镐,AVFoundation去語(yǔ)音轉(zhuǎn)文字播放癞志,或者直接播放語(yǔ)音文件都是不能實(shí)現(xiàn)該功能的,但是iOS10.0是可以實(shí)現(xiàn)的框产,根據(jù)大量閱讀蘋果開發(fā)文檔凄杯,蘋果起初設(shè)計(jì)該擴(kuò)展類的目的是優(yōu)化推送樣式,不是為了語(yǔ)音處理秉宿,但是發(fā)現(xiàn)大部分開發(fā)者通過(guò)這種方式處理語(yǔ)音播放問(wèn)題戒突,所以后來(lái)把所有播放語(yǔ)音類的代碼,讓其在UNNotificationServiceExtension擴(kuò)展類中的代碼都是無(wú)效的描睦。但是self.bestAttemptContent仍包含的有sound屬性膊存,所以后來(lái)大家都通過(guò)處理過(guò)后生成的語(yǔ)音文件,賦值給self.bestAttemptContent.sound去實(shí)現(xiàn)播放語(yǔ)音的效果忱叭。
#import "NotificationService.h"
@interface NotificationService ()<AVAudioPlayerDelegate,AVSpeechSynthesizerDelegate>
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
#pragma mark -
// // 重寫一些東西
// self.bestAttemptContent.title = @"我是標(biāo)題";
// self.bestAttemptContent.subtitle = @"我是子標(biāo)題";
// self.bestAttemptContent.body = @"來(lái)自徐不同";
NSDictionary *userInfoDic = self.bestAttemptContent.userInfo;
NSLog(@"后臺(tái)推送消息內(nèi)容:%@",userInfoDic);
// 安全預(yù)警提醒類型
if ([userInfoDic[@"pushType"] isEqualToString:@"SAFETY_WARNING"]) {
// 12.1系統(tǒng)版本之前 可以是用AVAudioSession
if ([[[UIDevice currentDevice] systemVersion]floatValue] < 12.1) {
// AVAudioSession是一個(gè)單例類
AVAudioSession *session = [AVAudioSession sharedInstance];
// AVAudioSessionCategorySoloAmbient是系統(tǒng)默認(rèn)的category
[session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
// 激活A(yù)VAudioSession
[session setActive:YES error:nil];
// 文字轉(zhuǎn)語(yǔ)音播放
[self playVoiceWithContent:userInfoDic[@"content"]];
} else {
if ([userInfoDic[@"sinoiovNoticeType"] isEqualToString:@"SPEEDING"]) {
// SPEEDING 超速
self.bestAttemptContent.sound = [UNNotificationSound soundNamed:@"overSpeed.m4a"];
NSLog(@"超速");
}else if ([userInfoDic[@"sinoiovNoticeType"] isEqualToString:@"FATIGUE"]) {
// FATIGUE 疲勞
self.bestAttemptContent.sound = [UNNotificationSound soundNamed:@"fatigueDriving.m4a"];
NSLog(@"疲勞");
}else if ([userInfoDic[@"sinoiovNoticeType"] isEqualToString:@"VEHICLE_OFFLINE"]) {
// VEHICLE_OFFLINE 離線
self.bestAttemptContent.sound = [UNNotificationSound soundNamed:@"offLine.m4a"];
NSLog(@"離線");
}else if ([userInfoDic[@"sinoiovNoticeType"] isEqualToString:@"PARKING_VIOLATION"]) {
// PARKING_VIOLATION 高速公路違規(guī)停車
self.bestAttemptContent.sound = [UNNotificationSound soundNamed:@"illegalParking.m4a"];
NSLog(@"高速公路違規(guī)停車");
}else if ([userInfoDic[@"sinoiovNoticeType"] isEqualToString:@"LOW_SPEED"]) {
// LOW_SPEED 高速公路低速行駛
self.bestAttemptContent.sound = [UNNotificationSound soundNamed:@"lowSpeed.m4a"];
NSLog(@"高速公路低速行駛");
}
self.contentHandler(self.bestAttemptContent);
}
}else {
// 直接彈出推送框
self.contentHandler(self.bestAttemptContent);
}
}
- (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
需要注意的是:
1.以上擴(kuò)展方法中是不能打斷點(diǎn)的隔崎,打斷點(diǎn)會(huì)導(dǎo)致崩潰且找不到原因,需要打印數(shù)據(jù)使用NSLog方法韵丑,打斷點(diǎn)會(huì)崩潰的問(wèn)題不是個(gè)例爵卒。
2.該種方式的推送語(yǔ)音消息只支持6s時(shí)間,因?yàn)橄到y(tǒng)的推送彈框的顯示時(shí)間為6s撵彻,超過(guò)6s的語(yǔ)音會(huì)停止播放钓株,請(qǐng)注意語(yǔ)音時(shí)長(zhǎng),因?yàn)闀r(shí)間原因陌僵,本人沒有做深入研究是否可以延長(zhǎng)推送彈框的方法轴合。
以上問(wèn)題有哪位大神知道原因或者有解決辦法,可以聯(lián)系告知筆者碗短。