前言
因为目前所在的公司是做聚合支付的,面向的用户是商家,所以收款播报金额这一块用户很是看重,在iOS12.1之前一直用的都是UNNotificationServiceExtension
推送扩展,然后使用本地录好的MP3音频文件根据推送过来的金额合成m4a格式文件放在本地然后使用AVAudioPlayer
进行播放,一直以来都挺好,限制就是iOS10之前不支持,然后对iOS10之前的录了一个固定格式的音频文件进行推送播放,但是最新iOS12.1系统UNNotificationServiceExtension
突然间不行的,系统的合成,百度合成,科大讯飞合成,通通不行,系统的AVAudioPlayer
也是废,经测试百度的离线合成根本不能在UNNotificationServiceExtension
用,因为推送扩展系统分配的内存资源很少,只能做一些微小的操作,调起百度的离线合成直接就内存溢出直接挂掉了,但是在线合成也只支持iOS12.1之前的系统。我目前经过了接近两周的研究和网上查找目前找到了两种方式,但是这两种都有很大可能被拒,所以在这里分享出来大家讨论一下,或者都多提交试试,互相分享一下过审经验。
目前播报的两种方式
1、UNNotificationServiceExtension
还是使用UNNotificationServiceExtension
推送扩展,但是播放器的不能在使用AVAudioPlayer
了,而是要使用AudioServicesPlaySystemSound
,这个是C语音的一个用来播放提示音或者手机振动的一个功能,限制是只能播放30秒,而且需要在UNNotificationServiceExtension
的infoplist里面添加App plays audio or streams audio/video using AirPlay
目前网上查到的没有一个使用这种方式通过审核的,使用AudioServicesPlaySystemSound
进行播放只能播放本地的音频文件,建议使用本地录好的MP3音频文件根据推送过来的金额合成m4a格式文件放在本地然后进行播放,这样比较省事,也可以选择使用科大讯飞的SDK文字转语音获取mp3文件放在本地然后进行播放。音频文件也是可以盗用其他app的,比如支付宝的,收钱吧的,可以在PP助手里面下载到他们的ipa包,直接把后缀改成zip格式的进行解压,然后就可以获取他们的资源包,然后右键显示报内容然后进行mp3格式进行搜索就能查到了,我目前用的是收钱吧录制的mp3文件,感觉挺自然的,我们自己产品录得就是一坨屎。
下面粘贴一下AudioServicesPlaySystemSound
方式的播放代码
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID);
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, &playCallback, (__bridge void * _Nullable)(self));
AudioServicesPlaySystemSound(soundID);
void playCallback(SystemSoundID ssID, void* __nullable clientData) {
AudioServicesDisposeSystemSoundID(ssID);
selfClass.contentHandler(selfClass.bestAttemptContent);
NSLog(@"播放完成...");
}
url
代表音频文件的路径,playCallback
是播放完成的回调方法,是C语言的方法,想要在C语言里面调用OC的方法需要在外面声明一下
static NotificationService *selfClass =nil;
然后在合适的时机selfClass = self;
就可以了。
2、VOIP
最近一直在网上查到播报的问题,看到VOIP突然有了灵感于是就写了一个Demo,经测试可以实现语音播报,而且即使在杀死程序的情况下也是可以播报的,我是看了这篇帖子写的demo,写的很不错,而且里面还提供了PHP的后台代码,改一改token自己就可以本地测试了。
但是目前还是有所限制,因为VOIP是专门为网络电话类型的app提供的一种唤醒app的模式,但是我们的app的做聚合支付的呀跟网络电话没一点关系,而且如果走VOIP模式的话,之前的推送基本就是要推倒了,后台就要对应用户去管理token了,不能再向之前去依赖极光,信鸽啥的第三方了,所以比较费时费力,就怕你费时费力的搞好了然后被拒了就恶心了。
这我在网上找的一个关于怎么审核的可行方案,但是呼入可以录制,但是哪来的呼出呢
1、当app要上传App Store时,请在iTunes connect上传页面右下角备注中填写你用到VoIP推送的原因,附加上音视频呼叫用到VoIP推送功能的demo演示链接,演示demo必须提供呼出和呼入功能。
我已经把写的demo放在GitHub的链接VOIPDEMO,里面也有多个mp3合成一个m4a的方法。
demo里面你们只需要看VoicePlayingManager这个类和AppDelegate这个类就可以了,其他都不需要看,只是之前写testdemo乱写的。
VOIP证书制作要看这篇帖子。
今天测试了一下微信的收款是可以的,即使杀死程序也会播报,所以目前可以肯定微信一定是通过VOIP的方式实现的,因为微信本来就是VOIP类型的程序所以过审分分钟,但是支付宝就没有语音聊天的功能所以不能用VOIP,还有收钱吧是内置了一千多个音频文件,通过推送sound字段然后后台去控制的一种很物理的方式,而且覆盖不到全部金额,比如你去测试付个0.12元就不会播报金额了,但是测试0.01元就可以。
我建了一个iOS12.1语音播报交流群 839097185 没事都可以过来讨论讨论。
又想到一个方式,就是通过后台推送唤醒去播报金额,但是不能退出杀死程序而且需要勾选后台播放模式,可能会被拒,但是可以申诉就是不知道能不能给过。小伙伴们可以尝试去提交一下,通过的过来交流一下过审经验。
这是之前写的推送后台唤醒打印蓝牙小票的帖子可以参考一下 地址
最新消息 2018年12月11日 下午4点26分更新内容:支付宝实现了12.1之后的语音播报使用的是VOIP的方式有图为证
实际去使用支付宝,发现他们并没有实现语音通话的功能,就是不知道他们怎么过审的,肯定是申诉,大家们行动起来都去搞VOIP申诉过审呀,啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊,上面有VOIP的实现方式。
3、本地推送
本地推送就是,把播报金额拆分成,一、二、三,四、五...千、百、万、点、元等一个个音频文件,根据推送过来的金额进行进行筛选然后按照顺序放入数组,然后循环数组进行创建并发送本地推送。
我使用的是递归的方式根据音频的时间循环发送本地推送的。类似于下图。
这个方法的好处很直观,包比上面那种方法小,缺点就是声音很难听,很奇怪,想让它声音变得好听,要花费不少时间了,在录音和播放时间上想想办法。还有就是本地通知每传一次会震动一次,比如:收银到账,震一下,九,震一下,万,震一下,八,震一下,千,震一下,七,震一下,百,震一下,六,震一下,十,震一下,五,震一下,点,震一下,四,震一下,三,震一下,元,震一下。这个暂时没有什么好方法,只能引导用户关掉设置里的震动开关。另外,本地通知的语音播报容易被打断。
用这个方法,还会有个问题,比如888.88元,分位上的八可能会因为角位上的八的通知对音频的引用没结束,分位上的八就要播, 会出现播不了的情况。解决方法思路:数字的mp3文件多复制一份,命名如:*_copy.mp3, 循环判断string数组,如果与前一个string重复,就改成 *_copy,这样,就不会连续读取一个文件了。
注意:音频文件add的时候要两个targets都要勾选。还有本地推送的方式,APP在前台的时候是没有声音的,前台需要走正常的推送解析,走正常的语音播报,反正都在前台了,不就想怎么播怎么播了。
还有,不需要考虑推送序列的问题,扩展里面是要self.contentHandler(self.bestAttemptContent);走完这句代码推送到达真正的App里面时候,才会处理下一个推送。
总结
第一个方法是废弃了,打包会成功但是上传包审核的时候我直接报错。
第二个方法,可行但是改动巨大,而且不一定能申诉过审。
第三个方法,目前是可行性最大的,也是我目前使用的,无过审压力。
第四种方法,录制1w+的音频然后修改推送sound。
self.contentHandler(self.bestAttemptContent);这句话的意思是,扩展里面的东西处理完了,这个推送可以到达真正的APP里面去了的一个结束语。
也可以三四方法结合使用,毕竟第三种方法有很多弊端。