iOS12.1之后语音播报问题解决方案总结

    目前公司的项目是一个类似于送快递的项目,所以免不了有类似于饿了么、美团、支付宝收钱吧这种,"您有新的订单","您的收益到账啦"这种类似的推送,这种推送还设计语音播报出来的功能。产品要求APP在前台、后台、程序杀死后都要收到推送消息和语音播报,之前也早早的做了这个功能,就是用的大家常用的方法NotificationService。但是项目升级了iOS12.1后,语音不播报了。

    最开始,我语音播报用的是iOS7后裔新增了一个简单的API----AVSpeechSynthesizer。利用的它的简单的把文字转语音,设置语音国家、语速等属性,将推送获得的aps播报出来,这里不再过多赘述,网上有很多资料,大家可以去查它的简单用法。在iOS12升级前,都是可以正常播报的,但是升级后,遇到了跟大家一样的问题,发现不播报了。刚开始认为是AVSpeechSynthesizer的问题,然后就想换掉它,用百度语音或者其他第三方SDK替换语音转文字功能。但是据网上同道中人反映,集成的第三方SDK进行语音播报的也同样不灵了。但是人家支付宝手钱包解决了呀,人家能正常播报。后来呢,道听途说,听说支付宝收钱吧是内置了很多个语音包文件,所以才能播报的。于是我又踏上了通过内置语音文件,修改通知SoundID的方法进行尝试。

    相关代码:

NSString *type = [NSString stringWithFormat:@"%@",userInfo[@"type"]];
        if ([type isEqualToString:@"1"]) {
            
            SystemSoundID soundID;
            NSString *path = [[NSBundle mainBundle] pathForResource:@"xindingdan.wav" ofType:nil];
            AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);
            AudioServicesPlaySystemSound(soundID);
            /*AudioServicesPlaySystemSoundWithCompletion*/
            AudioServicesPlayAlertSoundWithCompletion(soundID, ^{
            });
            
            
        }
        if ([type isEqualToString:@"5"]) {
            
            SystemSoundID soundID;
            NSString *path = [[NSBundle mainBundle] pathForResource:@"shouyi.wav" ofType:nil];
            AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);
            AudioServicesPlaySystemSound(soundID);
            /*AudioServicesPlaySystemSoundWithCompletion*/
            AudioServicesPlayAlertSoundWithCompletion(soundID, ^{
            });
            
        }

幸运的是iOS12.0到iOS12.1之前,的确是能播报了。但是升级到iOS12.1乃至以后的系统,依然不行。

在NotificationService里打断点调试,发现NotificationService里的相关播报代码也走,但是就是播报不出来,也就是是此种方法播报,如果不需要后台和程序被杀死的情况下收到通知并且进行语音播报,也是可以的。如果需要在后台和杀死程序依然播报,此方法依然行不通。

那么,现在公布我最终解决iOS12.1播报的方案:

整体思路就是:把远程通知在扩展里拆分成多个本地通知,每个本地通知是单个的音频,顺序发出。

思路就是这么简单,思路有了,代码就很快能写出来了。

下面是生成本地通知的相关代码:

//使用 UNNotification 本地通知
-(void)registerNotification:(NSInteger )alerTime:(NSDictionary *)userinfo{
    
    // 使用 UNUserNotificationCenter 来管理通知
    UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
    
    //需创建一个包含待通知内容的 UNMutableNotificationContent 对象,注意不是 UNNotificationContent ,此对象为不可变对象。
    UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
//    content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
//    content.body = [NSString localizedUserNotificationStringForKey:mes
//                                                         arguments:nil];
    
    NSString *type = [NSString stringWithFormat:@"%@",userinfo[@"type"]];
    if ([type isEqualToString:@"1"]) {

        UNNotificationSound *sound = [UNNotificationSound soundNamed:@"xindingdan.wav"];
        content.sound = sound;
    }
    else if ([type isEqualToString:@"5"]) {

        UNNotificationSound *sound = [UNNotificationSound soundNamed:@"shouyi.wav"];
        content.sound = sound;


    }else{
        content.sound = [UNNotificationSound defaultSound];
    }
    
    
    
    content.userInfo = userinfo;
    
    // 在 alertTime 后推送本地推送
    UNTimeIntervalNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger
                                                  triggerWithTimeInterval:alerTime repeats:NO];
    
    UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"FiveSecond"
                                                                          content:content trigger:trigger];
    
    //添加推送成功后的处理!
    [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    }];

}

此方法依然是建立在之前的NotificationService里调用:

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
   
    self.contentHandler = contentHandler;

    self.bestAttemptContent = [request.content mutableCopy];

    
    //获取App Group的共享目录
    NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.jls.app.group"];
    NSURL *fileURL = [groupURL URLByAppendingPathComponent:@"jlsappGroup.txt"];
    //读取文件
    NSString *bobaostr = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:nil];
    
    if (![bobaostr isEqualToString:@"bobaono"]) {
        NSString *type = [NSString stringWithFormat:@"%@",request.content.userInfo[@"type"]];
        if ([type isEqualToString:@"1"] || [type isEqualToString:@"5"]) {
            
            [self registerNotification:1:request.content.userInfo];
            
        }
    }


    
}

对于上面一段appgroup的代码解释,是因为在主程序里,一般都会有一个开启、关闭语音播报的按钮或switch,所以我们在主程序里改变了语音是否播报的状态,但是在NotificationService里,由于bundleID不同,致使我存储到NSUserDefaults的想法不成立。所以我采用了app group的方法存储、获取信息,用来同步扩展和主程序语音播报状态的信息。也可以采用keychain sharing方式,只不过我采用的前者。

至于之前怎么做在后台、杀死程序等获取通知的方法,以后我会补上相关方法。

对于解决iOS12.1以后语音播报问题的方案,如果大家有其他好的解决办法,希望多多交流,谢谢!

猜你喜欢

转载自blog.csdn.net/ZhongLv_HoneyMoon/article/details/85233562