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) {
// 类型为7
NSString *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