本文参考文章 iOS 模仿支付宝支付到账推送,播报钱数看上面写的一些不是很详细遇到了许多问题,这里特意自己总结了一下。将我遇到的问题以及解决方案给罗列出来供大家参考。

iOS10之后的ServiceExtends,如果不是很清楚可以自行百度或者浏览一下iOS10 推送extension之 Service Extension

首先创建一个工程:

打开推送通知注册接受

Background Modes内部的第一个我看有的demo是有够选的,我这这里没有勾选,通知是同样能够收到并且在后台播放的。那么为了不必要的麻烦这里就不勾选了。

然后我们创建通知扩展,通知的扩展能够为我们的app即使在被杀死的情况下也能唤醒30s左右的时间来供我们的app进行语音播报。

这句话的大概意思就是告诉你去激活它,然后在toolbar上面可以选择使用它,我们点击Activate按钮就可以了。

然后你就会发现你的项目工程里面多了一个文件夹,就是你刚才你创建的推送扩展。

然后我们在AppDelegate里面进行通知的注册。

代码如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {// Override point for customization after application launch.[self registerRemoteNotification];return YES;
}
// 注册推送
- (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 注册APNSif ([application respondsToSelector:@selector(registerForRemoteNotifications)]) {[application registerForRemoteNotifications];}else{UIRemoteNotificationType notificationTypes = UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert;[[UIApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];}
#endif
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];[UIPasteboard generalPasteboard].string =token;NSLog(@"device token is %@",token);}

然后就等之后的推送消息在AppDelegate里面的方法里面接收通知:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
{NSLog(@"userInfo ===== %@",userInfo);
}- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler
{NSLog(@"userInfo === %@",userInfo);
}

另外介绍两个本不属于这个专项的相关问题:Safa Area报错记得勾选去掉

我们在拖入声音文件的时候会有以下提示,这个时候我们的通常做法可以是全部都勾选,(这样做其实是为了方便对iOS10一下声音文件获取),其实对我们iOS10的扩展,我们可以只勾选扩展,因为文件只是在扩展里面才使用。这里作简要的说明一下。

然后就是加入工程的声音文件如下图:

然后我们在NotificationService.m文件内写入我们的声音文件合成代码。并播放声音文件的代码。

代码如下:(亲测通过,没有问题)

#import "NotificationService.h"
#import <AVFoundation/AVFoundation.h>#define kFileManager [NSFileManager defaultManager]typedef void(^PlayVoiceBlock)(void);@interface NotificationService ()<AVAudioPlayerDelegate>@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
//声音文件的播放器
@property (nonatomic, strong)AVAudioPlayer *myPlayer;
//声音文件的路径
@property (nonatomic, strong) NSString *filePath;// 语音合成完毕之后,使用 AVAudioPlayer 播放
@property (nonatomic, copy)PlayVoiceBlock aVAudioPlayerFinshBlock;@end@implementation NotificationService- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {self.contentHandler = contentHandler;self.bestAttemptContent = [request.content mutableCopy];// Modify the notification content here...self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];__weak __typeof(self)weakSelf = self;/*******************************推荐用法*******************************************/// 方法3,语音合成,使用AVAudioPlayer播放,成功AVAudioSession *session = [AVAudioSession sharedInstance];[session setActive:YES error:nil];[session setCategory:AVAudioSessionCategoryPlayback error:nil];[self hechengVoiceAVAudioPlayerWithFinshBlock:^{weakSelf.contentHandler(weakSelf.bestAttemptContent);}];
//    self.contentHandler(self.bestAttemptContent);
}#pragma mark- 合成音频使用 AVAudioPlayer 播放
- (void)hechengVoiceAVAudioPlayerWithFinshBlock:(PlayVoiceBlock )block
{if (block) {self.aVAudioPlayerFinshBlock = block;}/************************合成音频并播放*****************************/AVMutableComposition *composition = [AVMutableComposition composition];NSArray *fileNameArray = @[@"daozhang",@"1",@"2",@"3",@"4",@"5",@"6",@"1",@"2",@"3",@"4",@"5",@"6",@"1",@"2",@"3",@"4",@"5",@"6"];CMTime allTime = kCMTimeZero;for (NSInteger i = 0; i < fileNameArray.count; i++) {NSString *auidoPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@",fileNameArray[i]] ofType:@"m4a"];AVURLAsset *audioAsset = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:auidoPath]];// 音频轨道AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:0];// 音频素材轨道AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];// 音频合并 - 插入音轨文件[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration) ofTrack:audioAssetTrack atTime:allTime error:nil];// 更新当前的位置allTime = CMTimeAdd(allTime, audioAsset.duration);}// 合并后的文件导出 - `presetName`要和之后的`session.outputFileType`相对应。AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];NSString *outPutFilePath = [[self.filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"xindong.m4a"];if ([[NSFileManager defaultManager] fileExistsAtPath:outPutFilePath]) {[[NSFileManager defaultManager] removeItemAtPath:outPutFilePath error:nil];}// 查看当前session支持的fileType类型NSLog(@"---%@",[session supportedFileTypes]);session.outputURL = [NSURL fileURLWithPath:outPutFilePath];session.outputFileType = AVFileTypeAppleM4A; //与上述的`present`相对应session.shouldOptimizeForNetworkUse = YES;   //优化网络[session exportAsynchronouslyWithCompletionHandler:^{if (session.status == AVAssetExportSessionStatusCompleted) {NSLog(@"合并成功----%@", outPutFilePath);NSURL *url = [NSURL fileURLWithPath:outPutFilePath];self.myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];self.myPlayer.delegate = self;[self.myPlayer play];} else {// 其他情况, 具体请看这里`AVAssetExportSessionStatus`.// 播放失败self.aVAudioPlayerFinshBlock();}}];/************************合成音频并播放*****************************/
}
#pragma mark- AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{if (self.aVAudioPlayerFinshBlock) {self.aVAudioPlayerFinshBlock();}
}- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer*)player error:(NSError *)error{//解码错误执行的动作
}
- (void)audioPlayerBeginInteruption:(AVAudioPlayer*)player{//处理中断的代码
}
- (void)audioPlayerEndInteruption:(AVAudioPlayer*)player{//处理中断结束的代码
}- (NSString *)filePath {if (!_filePath) {_filePath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];NSString *folderName = [_filePath stringByAppendingPathComponent:@"MergeAudio"];BOOL isCreateSuccess = [kFileManager createDirectoryAtPath:folderName withIntermediateDirectories:YES attributes:nil error:nil];if (isCreateSuccess) _filePath = [folderName stringByAppendingPathComponent:@"xindong.m4a"];}return _filePath;
}
- (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);
}

当然播放声音也可以使用我们苹果系统的开放库AVSpeechSynthesisVoice

使用AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];创建嗓音

// 创建语音合成器

synthesizer = [[AVSpeechSynthesizer alloc] init];

synthesizer.delegate = self;

// 实例化发声的对象

AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:content];

utterance.voice = voice;

utterance.rate = 0.5; // 语速

// 朗读的内容

[synthesizer speakUtterance:utterance];

来实现朗读声音的播放

接下来,我们开始运行我们的代码开始体验我们的结果:

1.我们要选中我们的扩展scheme

然后选中我们的运行的app

接下来就是需要通过推送进行消息的检测的时刻了。

这里推荐一款测试软件SmartPush推送测试工具:(奉上链接地址:SmartPush)

运行然后选择我们的对应的推送证书,设备token

然后我们如果测试在后台推送的数据消息,那么需要多加上字段:"mutable-content":1,

例如:{
  "aps":{
    "alert":{
      "title":"iOS 10 title",
      "subtitle":"iOS 10 subtitle",
      "body":"iOS 10 body"
    },
     "mutable-content":1,
    "category":"saySomethingCategory",
    "sound":"default",
    "badge":3
  }

}

然后我们点击发送,在前台,后台,杀死状态下就可以测试听语音播报了。

以上就是关于语音播放收款信息的一些搜集和自己的实践整理出来的,

还有后期的打包上架需要选哪个scheme的问题,个人觉得是选择扩展的那个scheme进行打包,还在测试,没有上架呢。待以后有时间及时为大家更新补上。

奉上本文的demo链接地址:艾鑫文学社

2019.4.23号补充:(最近看到还有许多朋友也在不断的点赞,希望再次把我发现的问题告知大家,让关注我的朋友少走弯路。)

测试发现从iOS12.0.1版本之后出现在app后台的模式下不再进行语音播报,经过断点以及log排查,发现是语音合成没有问题,是在此之后的AVAudioPlayer不支持播报导致的问题。暂时尝试过使用iOS的voip推送,是可以让系统重新支持AVAudioPlayer播放处理。但是voip的使用审核可能比较严格,相关使用的同学可以去细致看一下voip的审核问题。(之后的iOS系统什么时候能够后台打开这个AVAudioPlayer播放还需期待。)

下面还有一个微信的收款语音提醒的一个总结微信 iOS 收款到帐语音提醒开发总结,供大家有空参考!

iOS远程推送自定义语音合成播放声音(类似支付宝收款提醒)相关推荐

  1. iOS推送语音播报(类似支付宝收款提醒)

    需求分析 实现类似支付宝微信收款后的语音播报如:支付宝到账xx元.要求是APP在前台运行.锁屏.杀死进程后都会有语音播报.那想到的解决方案就是利用推送了. 功能实现思路分析 上面说了,要使用推送,也就 ...

  2. iOS远程推送原理及实现过程

    该文章是我16年在公司博客上写的,除了证书注册的过程大致没有改变,像接收通知的方法都有所改变,所以将iOS 10 之后的接收通知及注册通知的方法在文章中补全,希望对正在处理远程推送的伙伴们有所帮助 一 ...

  3. iOS 推送语音播报(类似支付宝微信的收款提醒)

    项目需求: 近期项目有个需求,实现类似支付宝微信收款后的语音播报如:支付宝到账xx元.要求是APP在前台运行.锁屏.杀死进程后都会有语音播报. 预想方案: 1.通过UIBackgroundTaskId ...

  4. iOS 远程推送 总结

    首先,让我们看看jpush的申请证书以及配置教程http://docs.jpush.io/client/ios_tutorials/#ios-sdk 相关配置完成后,我们就来实现相应的远程推送功能. ...

  5. [转]iOS 远程推送(APNs)

    我是搬运工.原帖:http://blog.csdn.net/lifengzhong/article/details/7737028 目录(?)[-] 一简介 二使用步骤 step1 step2 ste ...

  6. 客户端技术:一文带你了解iOS消息推送机制

    导语 | 消息推送我们几乎每天都会用到,但你知道 iOS 中的消息推送是如何实现的吗?本文将从推送权限申请,到本地和远程消息推送,再到 App 对推送消息的处理等多个步骤,详细介绍 iOS 中消息推送 ...

  7. iOS应用处于前台、后台、应用被杀掉场景-收到远程推送内容进行收款语音播报;

    iOS应用处于前台.后台.应用被杀掉场景-收到远程推送内容进行收款语音播报: 介绍: 收银应用两大技术点:远程推送.收款成功语音播报收款金额及其他附带语音内容: 顺便点下android语音播报有一个至 ...

  8. IOS开发之----远程推送通知

    原文地址:IOS开发之----远程推送通知作者:倒計時 玩了一年的iPhone了各种App的远程通知接收了不少,每次接收到的时候,就在反思,这丫的怎么实现的! 由于工作方面一直没有接触的机会,所以只好 ...

  9. 玩转ios友盟远程推送,16年5月图文防坑版

    最近有个程序员妹子在做远程推送的时候遇到了困难,求助本帅.尽管本帅也是多彩的绘图工具,从没做过远程推送,但是本着互相帮助,共同进步的原则,本帅还是掩饰了自己的彩笔身份,耗时三天(休息时间)帮她完成了推 ...

最新文章

  1. 【Interfacenavigation】XML中的字体(27)
  2. Host XXX is not allowed to connect to this MySql 远程连接
  3. 2021.04.14HDOJ
  4. cpython cython_python – 优化Cython中的字符串
  5. React、Angular、Vue 框架比较
  6. javaScript设计模式之常用工厂模式
  7. Disk Drill Enterprise for Mac(数据恢复软件)
  8. 使用文件进行输入输出的两种方式(算法竞赛入门经典第2章)
  9. 世界地图可以无限放大_不敢相信!世界地图,你竟然骗了我这么多年...
  10. 《3D游戏与计算机图形学中的数学方法》读书笔记--四元数
  11. Android flag详解
  12. Wave Arts Tube Saturator——实时电子管放大器插件
  13. alert获取输入框内容_Alert弹出框处理
  14. 深藏不漏!.cc域名接连高价成交!最高突破6位数!
  15. js 获取所有被选中复选框的值
  16. 基于NLM的插值算法
  17. linux操作系统. 80188,Materials-Studio5.5在Linux服务器上安装与测算讨论 - 第一原理 - 小木虫 - 学术 科研 互动社区...
  18. 学会和人沟通与请教问题秘籍
  19. 【20考研】如何度过一个有意义的寒假?
  20. 1^3+2^3+3^3+...+n^3通项公式

热门文章

  1. 冯绍峰赵丽颖官宣结婚 ,微博瘫痪,微博技术专家如何应对?
  2. 游戏服务器维护六天了,1月6日服务器例行维护公告(已完成)
  3. python取整方式(向上取整/向下取整/四舍五入)
  4. BFC是什么?开启BFC的标志?解决什么问题?
  5. 【深度学习】 自编码器(AutoEncoder)
  6. 创意与策划——网络营销的魂与骨
  7. 抖音怎么拍视频能上热门推荐?
  8. Vue中computed与watched
  9. 二倍图(物理像素物理像素比,多倍图,背景缩放),移动端开发选择(移动端主流方案【单独制作移动端页面(主流),响应式页面兼容移动端(其次)】,移动端技术解决方案【移动端浏览器,特殊样式】)
  10. android 自定义仿qq点赞动画