iOS NotificationService语音播报
最近碰到个接收到推送要实现语音播报的需求,需要后台推送通知,APP客户端收到通知之后语音播放:“您的账户收到一笔巨款”的功能。
因为工程之前已经集成了极光推送服务。这里直接使用Notification Service Extension通知服务扩展
Notification Service Extension 简单说我们在收到推送且在弹框展示之前,对通知内容进行修改(只针对远程推送)。
一 主工程需要如下配置:
设置Background Modes
二 IOS 10 创建Notification Service Extension
创建Notification Service Extension的target
创建后系统会生成NotificationService.h和NotificationService.m两个文件。
设置下Targets中的NotificationService的 Push Notifications
三 使用AVSpeechUtterance
在NotificationService中代码
#import "NotificationService.h"
#import <AVFoundation/AVFAudio.h>
#import <AVFoundation/AVFoundation.h>@interface NotificationService ()<AVSpeechSynthesizerDelegate>@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;@property (nonatomic, strong) AVSpeechSynthesisVoice *synthesisVoice;
@property (nonatomic, strong) AVSpeechSynthesizer *synthesizer;@end@implementation NotificationService- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {self.contentHandler = contentHandler;self.bestAttemptContent = [request.content mutableCopy];// 这个info 内容就是通知信息携带的数据,后面我们取语音播报的文案,通知栏的title,以及通知内容都是从这个info字段中获取NSString *type = self.bestAttemptContent.userInfo[@"type"];NSString *typeStr = [NSString stringWithFormat:@"%@",type];// 语音播报if (7 == typeStr.integerValue) {// 类型为7NSString *info = self.bestAttemptContent.userInfo[@"content"];// 播报语音[self playVoiceWithContent:info];}// 这行代码需要注释,当我们想解决当同时推送了多条消息,这时我们想多条消息一条一条的挨个播报,我们就需要将此行代码注释// self.contentHandler(self.bestAttemptContent);
}- (void)playVoiceWithContent:(NSString *)content {if (!(content && [content isKindOfClass:[NSString class]] && content.length > 0)) {return;}AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:content];utterance.rate = 0.5;utterance.voice = self.synthesisVoice;[self.synthesizer speakUtterance:utterance];
}// 新增语音播放代理函数,在语音播报完成的代理函数中,我们添加下面的一行代码
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance { // 每一条语音播放完成后,我们调用此代码,用来呼出通知栏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);
}// 调试,代替debug
- (void)playVoice:(NSString *)info {AVSpeechSynthesizer *av = [[AVSpeechSynthesizer alloc] init];AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:info];utterance.rate = 0.5;utterance.voice= voice;[av speakUtterance:utterance];
}- (AVSpeechSynthesisVoice *)synthesisVoice {if (!_synthesisVoice) {_synthesisVoice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];}return _synthesisVoice;
}- (AVSpeechSynthesizer *)synthesizer {if (!_synthesizer) {_synthesizer = [[AVSpeechSynthesizer alloc] init];_synthesizer.delegate = self;}return _synthesizer;
}@end
四 IOS 10之前版本在AppDelegate中的设置
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {DebugLog(@"收到通知:%@", userInfo);[[DFAVSpeechManager sharedInstance] playAVSpeechUserInfo:userInfo completion:nil];completionHandler(UIBackgroundFetchResultNewData);
}
五 IOS 设置app打开的时候在前台的推送栏显示
如果是语音播报,这里我设置需要显示推送栏。
#pragma mark - IOS10 LaterNotification UNUserNotificationCenterDelegate Methods
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{DebugLog(@"willPresentNotification");NSDictionary * userInfo = notification.request.content.userInfo;UNNotificationRequest *request = notification.request; // 收到推送的请求UNNotificationContent *content = request.content; // 收到推送的消息内容NSNumber *badge = content.badge; // 推送消息的角标NSString *body = content.body; // 推送消息体UNNotificationSound *sound = content.sound; // 推送消息的声音NSString *subtitle = content.subtitle; // 推送消息的副标题NSString *title = content.title; // 推送消息的标题if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {DebugLog(@"iOS10 前台收到远程通知:%@",userInfo);} else {// 判断为本地通知DebugLog(@"iOS10 前台收到本地通知:{nbody:%@,ntitle:%@,nsubtitle:%@,nbadge:%@,nsound:%@,nuserInfo:%@n}",body,title,subtitle,badge,sound,userInfo);}BOOL needShowPushColum = [[DFAVSpeechManager sharedInstance] canShowPushColumn:userInfo];if (needShowPushColum) {//前台接到推送展示 则需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以设置completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);} else {//前台接到推送不展示completionHandler(0);}
}
六 在极光后台设置进行推送
在极光后台设置进行推送
当然特别需要注意的地方是如果服务器推送语音播报,需要设置mutable-content为1 或 true