最近申请了支付宝的二维码收钱码,其中支付宝有这么一个功能,就是,别人扫描你的二维码给你转帐以后,收到钱会有一条语音推送,”支付宝到帐 1000万“之类的推送消息,无论你的支付宝app有没有被杀死。git

只要你的远程推送开着,而且支付宝的"二维码收钱到帐语音提醒",都打开着,就能够收到。github

打开方式:支付宝点击右上角设置-通用-新消息通知,打开到帐提醒便可。
json

image.pngimage.png

而且别人给你转多少钱就会播报到帐多少钱。数组

探索实现一下。bash

当前有两种方案实现了上面描述的场景。网络

必备条件
上面的描述场景只有在iOS10以上版本才能够,由于必需要基于Notification Servivice Extension
session

image.pngimage.png

若是对Notification Servivice Extension不是很熟悉的,建议先了解一下
iOS10 推送extension之 Service Extension你玩过了吗?app

实现方式
一、ServiceExtension中收到推送以后,用AVSpeechSynthesisVoice相关类,直接把推送过来须要播报相关的文字转化成语音播报
二、ServiceExtension中收到推送以后,将要播报的数字,找到对应的单个音频,排序,用拼接音频的方式<经过推送过来的文字去查找相关的音频,而后拼接成一个音频>,而后使用AudioServicesCreateSystemSoundID播放工具

在介绍相关方式以前,先介绍一个测试工具
SmartPushpost

使用方式也很简单,应该一看就懂

image.pngimage.png


正式进入主题
方式一

@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];

    self.contentHandler(self.bestAttemptContent);


    [self playVoiceWithAVSpeechSynthesisVoiceWithContent:self.bestAttemptContent.body];

}

- (void)playVoiceWithAVSpeechSynthesisVoiceWithContent:(NSString *)content
{
    if (content.length == 0) {
        return;
    }
    // 建立嗓音,指定嗓音不存在则返回nil
    AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];

    // 建立语音合成器
    AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];

    // 实例化发声的对象
    AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:content];
    utterance.voice = voice;
    utterance.rate = 0.5; // 语速

    // 朗读的内容
    [synthesizer speakUtterance:utterance];
}

推送的内容是:

{
  "aps":{
    "alert":{
      "title":"iOS 10 title",
      "subtitle":"iOS 10 subtitle",
      "body":"世上只有妈妈好,有妈的孩子像块宝。投进妈妈的怀抱,幸福哪里找。没妈的孩子像根草"
    },
    "my-attachment":"http://img01.taopic.com/160317/240440-16031FU23937.jpg",
    "mutable-content":1,
    "category":"myNotificationCategory1",
    "badge":3

  }
}

坑点
说明:当前实现的是将push内容中的body播放出来
一、若是你收到推送了可是添加了系统的铃声,也就是你在push的json中添加了"sound":"default"那么就可能会影响推送声音的播放
二、收到推送了,可是没有播报语音,检查一下这里

image.pngimage.png
三、播放的声音时间长度,通过测试最可能是5秒钟,这里应该是苹果作了限制,拿上面的推送内容举例子 "body":"世上只有妈妈好,有妈的孩子像块宝。投进妈妈的怀抱,幸福哪里找。没妈的孩子像根草",最多也就是播放到 世上只有妈妈好,有妈的孩子像块宝。投进妈妈的怀抱


方式二
通过对比,支付宝播放的声音明显比系统方法文字转语音播放的好听,一听就是小姑娘录得。要么就是本身集成了一套文字转语音的东西。
首先尝试使用科大讯飞来实现,结果失败了。
而后尝试的是语音合成的方式来播放
好比提早先录好 如下可能播报的内容


支付宝到帐、 0、 一、 二、 三、 四、 五、 六、 七、 八、 九、 10、 百、 千、 万、 十万、 百万、 千万、 亿、 元 等等


这样的几种录音,而后用相关的名字命名好<相关的规则本身命名就好>。
好比push过来的是内容是 10010,那么转化成的录音文件名称的数组就是
@[@"支付宝到帐",@"1",@"万",@"0",@"1",@"十",@"元"]
而后找到这几个文件,而后按照顺序拼接成一个语音文件进行播放

代码演示:

@implementation NotificationService

static int lianxunPlay = 1;

- (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];


    self.contentHandler(self.bestAttemptContent);

    // 方式4,语音合成,使用AudioServicesPlayAlertSoundWithCompletion播放,成功,可是时间最多5秒
    [self hechengVoice];

}
- (void)hechengVoice
{
    /************************合成音频并播放*****************************/
    NSMutableArray *audioAssetArray = [[NSMutableArray alloc] init];
    NSMutableArray *durationArray = [[NSMutableArray alloc] init];
    [durationArray addObject:@(0)];

    AVMutableComposition *composition = [AVMutableComposition composition];

    NSArray *fileNameArray = @[@"daozhang",@"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]];
        [audioAssetArray addObject:audioAsset];

        // 音频轨道
        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];

            static SystemSoundID soundID = 0;

            AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);

            AudioServicesPlayAlertSoundWithCompletion(soundID, ^{
                NSLog(@"播放完成");
            });



        } else {
            // 其余状况, 具体请看这里`AVAssetExportSessionStatus`.
        }
    }];

    /************************合成音频并播放*****************************/
}

说明:
上面并无实现 数字转对应音频文件名称数组的过程,直接实现的是合成音频的方法。

坑点:
一、播放的时长仍然受到限制,大概5秒钟。不过若是说播放一个钱数,足够了。
二、合成以后的音频文件用AVAudioPlayer播放是没有声音的


分析:
以上的功能只是针对iOS10以上的系统版本能够,那么iOS10如下的怎么办?能够这么办,不用播报到帐多少钱,能够经过定制远程推送的语音,来播报”支付宝,您有一笔到帐,请及时查看“之类的,支付宝好像也是这个套路。

每次push以前,先去后台查看当前须要推送的设备的系统版本是啥<这个不难实现>,而后定制推送不一样的内容。iOS10以上的就推送钱数,而且不推送sound。
iOS10如下的就推送 "sound"="定制的声音文件名称"。

综上所述,支付宝实现的方式应该是方式二,本地合成音频文件播放的。

还有一个注意点就是,由于打开了下面的这个开关

image.pngimage.png
再上线的时候可能须要做出一下说明,否则有很大的可能被苹果打回来,最近苹果对这种权限的开启之类的比较严格,若是你没有相似的功能,你还开启了这样的权限,可能被干回来。若是有相关的好的像苹果说明的方法,还请给你们普及一下。

最后献上相关的Demo地址,若是你有更好的建议欢迎留言,若有不正,欢迎来喷。

能够直接用个人Demo进行调试,调试的时候注意修改下bundleId,而后用本身的开发者帐号配置一下相关的push证书就能够了

image.pngimage.png

image.pngimage.png