iOS12+语音离线播放(微信,支付宝锁屏状态下语音到账通知)

1,226 阅读5分钟

前言

iOS12以后苹果禁止了推送服务拓展中直接播放语音及合成的功能,造成以前的语音播放实现都失效。在此记录通过多种实验后的离线播报方法。(均需要通过Notification Service Extension完成)

  • 解析推送内容携带的附件,包含后台语音文件下载链接,下载后存储特殊位置播放。
  • app内置所有分解语音包,通过后台推送数据,自行合成然后通过本地通知实现播放。
  • Voip方案(在此只是记录,本次没有自测故暂时省略)。

Notification Service Extension

新的方案是主要是利用了苹果在iOS10中推出的Notification Service Extension(以下简称NSE),当apns的payload上带上"mutable-content"的值为1时,就会进入NSE的代码中。与Voip方案最大的不同之处是,NSE不能唤醒主应用,也不能访问主应用的文件空间,只能在Extension进程中处理相应的逻辑。在NSE中,开发者可以更改通知的内容,利用离线合成或者从后台下载的方式,生成需要播报的内容,通过自定义通知铃声的方式,达到语音播报提醒的目的。NSE方案也是苹果在WWDC2019的Session707上推荐的解决方式。

UNNotificationSound

在NSE中,可以通过给UNNotificationContent中的Sound属性赋值来达到在通知弹出时播放一段自定义音频的目的。

// The sound file to be played for the notification. The sound must be in the Library/Sounds folder of the app's data container or the Library/Sounds folder of an app group data container. If the file is not found in a container, the system will look in the app's bundle.

文档中明确描述了音频文件的存储路径,以及读取的优先级: 主应用中的Library/Sounds文件夹中 AppGroups共享目录中的Library/Sounds文件夹中 main bundle中 自定义铃声支持的声音格式包括,aiff、wav以及caf等格式,铃声的长度必须小于30s,否则系统会播放默认的铃声。

而且由于是通知铃声,声音是默认跟静音开关的,不需跟以前一样再使用判断静音开关的黑魔法(黑魔法在不同机型上偶尔会出现误判的情况)。

AppGroups

由于我们是在NSE中自定义铃声,所以1和3这两个文件路径我们是无法访问的。只能将合成好或者下载到语音音频文件存储到AppGroups下的Library/Sounds文件夹中,需要在Capablities中打开这个AppGroups的能力,即可通过NSFileManager的containerURLForSecurityApplicationGroupIdentifier:方法访问AppGroups的根目录。

语音合成 微信的收款到账语音依赖了我们自研的强大的离线语音合成库。apns的payload中携带了需要合成的文本内容,通过离线语音合成库生成wav音频文件后,将文件写到AppGroups的Library/Sounds文件夹下,最后更改UNNotificationSound属性即可使通知播报一段自定义的收款到账语音。

如果一些小型的企业本身不具备有离线合成的能力(看了下市面上的几个比较厉害的离线合成服务都是需要收费的),则可以采用在线合成再通过http下载的方式,讯飞和微信都有提供免费的服务。这个方案的缺点是依赖后台和当前的网络环境,有可能会导致消息播报不及时的问题。如果出现30s内都无法现在成功,需要在serviceExtensionTimeWillExpire方法中进行处理,最好的兜底方案是播放一段默认的语音。

一、在线下载播放

1、实现Notification Service Extension扩展(此处比较简单,可自行查阅)。

项目推送配置如下 1、App Groups 是存储下载语音文件沙盒

2、External accessory communication 也需要勾选,不然没法调试扩展

2、NotificationService 添加对应代码

didReceiveNotificationRequest: 添加对应代码,在这里修改推送对应数据,以及下载对应文件,如果是语音文件,存储到AppGroups共享目录中的Library/Sounds文件夹中。然后根据对应语音文件名字修改对应 self.bestAttemptContent.sound = [UNNotificationSound soundNamed:@"xxx.wav"];系统会根据名字自动进行播报

二、在app中添加离线语音包,通过对推送数据进行本地解析组合,然后通过本地通知进行播放

1、同方案一,实现Notification Service Extension扩展

2、在app项目工程中先加入离线语音包

3、NotificationService中 添加对应代码

1、根据自己后台发送的推送数据解析对应语音字段 didReceiveNotificationRequest: 添加并修改对应推送字段代码 isSound;//防止播报叠加 2、解析成和自己app包中对应的语音片段名称,压入数组,用于后面递归播放 3、根据语音数组递归发送本地语音通知 自动获取音频时长方法

三、Voip方案,这个方案本次没有实现。

补充:

如果有本地固定语音,可以将语音包直接放入app工程中。然后直接通过推送通知sound字段,添加对应语音名字直接进行播放。这个特性也可以完成简单的自定义通知声音。

小结:

离线语音合成简单总结记录,后续细节有时间再来完善。