Day Vision

Reading Everyday,Extending Vision

iOS update to 12.1 after voice broadcast processing (Swift with demo)

2023-02-28 04:09:34


去新公司接手的项目需要做语音播报,类似“支付宝到账100元”,上一个开发者只是做了前台播报,相对来说比较简单,接到推送后,在 func application(_ application: UIApplication, didReceive


To go to the new company to take over the project need to do voice broadcast, similar to "Alipay to the account 100 yuan", the last developer just did the front desk broadcast, relatively simple, after receiving the push, in the func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) uses AVSpeechSynthesizer for speech synthesis broadcast
  • New requirements after taking over the project: background programs need to do voice reporting after a kill, but because the kill has to be done by the user in order to do so, it is definitely not possible to do this in appdelegate, baidu has taken a look at ios 10's unnotification service extension to fix the problem

Integrated unnotificationservice extension

Step 2: Click Next

发现我们工程中多了 NotificationService.swift 文件,根据苹果官方文档,NotificationService.swift是为了丰富推送,给程序员修改推送内容的一次机会,相当于推送过来 我们会在NotificationService.swift中提前拿到通知,然后才会到推送弹框展示在用户眼前

iOS12.1之前语音播报

Since we can get the notification in advance, we make a push judgment in the NotificationService .swift to see if it is a voice broadcast or an ordinary push

We see that there is a method in the NotificationService .swift:

if let content = request.content.userInfo as? Dictionary<String,Any>,
                  let aps = content["aps"] as? Dictionary<String,Any>,
                  let alert = aps["alert"] as? Dictionary<String,Any>,
                  let str = alert["subtitle"] as? String{
}

Here add the str attribute we got is the content we want to broadcast, for example, str = "a card receives 100 yuan", and then we will broadcast by speech synthesis, the code is as follows:

let speaker = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: str)
utterance.rate = 0.5
et voice = AVSpeechSynthesisVoice(language: "zh-CN")
utterance.volume = 1
utterance.voice = voice
speaker.speak(utterance)

Writing here, tried, OK, perfect solution, front desk, backstage, program kill voice broadcast perfect, but after the line, sad, customer report voice broadcast no sound, I immediately tested, no problem, AH, then I tried my colleague's cell phone. It was silent. I looked it up on the internet and found that after 12.1, notificationservice. In Swift, Apple doesn't allow voice-synced broadcasts, and the official document reads something like this: “I'm notificationservice.”. Swift is to enrich the notice, but you used to do a voice broadcast, I am not happy, do not use. Finished, since in the program background, killing the case of voice broadcast, not to write here, no place to go. I tried a lot of things, but I couldn't figure it out, so I went back to the notificationservice. Swift, since you won't let me do voice synthesis, I'll have to play the voice file using local notifications.

iOS12.1以后语音播报

1. First of all we need to record voice files, you can use Baidu, Iflytek, etc. , here I use Baidu Voice, using Python script to generate files, do not want to run Python files directly to my code to copy, the Pyhton file is as follows:
# -*- coding: utf-8 -*-
import os
from aip import AipSpeech



APP_ID = '15977485'
API_KEY = '9pQMw3sDbsjlc88PupjK63g8'
SECRET_KEY = '6pYLyuxadNvBTpRoRuAssOrbIxBwoK9G'

client = AipSpeech(APP_ID,API_KEY,SECRET_KEY)

def functionNum(num):
    if(num == 0):
        return "0"
    elif(num == 1):
        return "1"
    elif (num == 2):
        return "2"
    elif (num == 3):
        return "3"
    elif (num == 4):
        return "4"
    elif (num == 5):
        return "5"
    elif (num == 6):
        return "6"
    elif (num == 7):
        return "7"
    elif (num == 8):
        return "8"
    elif (num == 9):
        return "9"
    elif (num == 10):
        return "十"
    elif (num == 11):
        return "百"
    elif (num == 12):
        return "千"
    elif (num == 13):
        return "万"
    elif (num == 14):
        return "点"
    elif (num == 15):
        return "元"
    elif (num == 16):
        return "一卡通到账"
    elif (num == 17):
        return "一卡通收款一笔"

for i in range(0, 18):
    
    print(os.getcwd())
    filePath = '/Users/wang/Desktop/Sound/'
    
    text =  functionNum(i)
    result = client.synthesis(text,'zh',1,None)
    sound = text
    
    if not isinstance(result,dict):
        with open(sound+'.mp3','wb') as f:
            f.write(result)
    print(sound)
When you get the voice file and add it to your project, be sure to check your own project and the unnotificationservice extension project

Start at notificationservice. exe. Write Code in Swift

  • 首先还是判断过来的通知是否为语音播报通知(和上面一样)
  • Determine the current version number, and then do different processing
if #available(iOS 12.1, *){//判断是否是12.1以上的系统
                    
                    bestAttemptContent.sound = nil
                    play_registerNotification(str: str)
                    
                }else{ //12.1以下的系统  直接执行语音合成播报
                    
                    bestAttemptContent.sound = UNNotificationSound.default
                    playVoiceWithContent(str: str)
                    contentHandler(bestAttemptContent)
                }
  • If this is a pre-ios 12.1 release, broadcast directly (same as above)

  • 如果是iOS12.1以后的版本

To withdraw the amount of registered local notice

 // MARK: - 使用本地通知12.1以上系统
    func play_registerNotification(str:String){
        if !str.hasPrefix("一卡通到账"){ //通知内容不是金额播报类型
            bestAttemptContent?.sound = UNNotificationSound.default
            contentHandler!(bestAttemptContent!)
            return
        }
        
        if (str as NSString).length <= 10{ //长度问题
            bestAttemptContent?.sound = UNNotificationSound.default
            contentHandler!(bestAttemptContent!)
            return
        }
        
        let strCount = (str as NSString).substring(with: NSMakeRange(5, (str as NSString).length-5-1))
        
        print(strCount)
        
        if let countStr =  Float(strCount) {
            if countStr > 10000.0{
                //大于10000播放收款一笔
                bestAttemptContent?.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "一卡通收款一笔.mp3"))
                contentHandler!(bestAttemptContent!)
                return
            }else{ //小于10000的播放金额
                
                if let array = stringToArray("\(countStr)") {
                    print(array)
                     pushLocalNotification(array, 0)
                }
            }
            
        }else{
            bestAttemptContent?.sound = UNNotificationSound.default
            contentHandler!(bestAttemptContent!)
        }
    }

利用递归一个一个播放

//Mark:-Func Play (Str: String){ if! Str. Hasprefix {//notification content is not the amount broadcast type bestAttemptContent? . sound = UNNotificationSound . Default contentHandler ! (bestAttemptContent!) Return } if (STR a s NSString)  . Length & LT; = 10{//the length problem bestAttemptContent ?. Sound = unnotification sound. Default Contenthandler! (bestAttemptContent!) Return } Let strCount = (STR a s NSString) . Substring (with: NSMakeRange (5, (STR a s NSString) . Length-5-1)) print (strCount) I F let countStr = Float (strCount){ if COUNTSTR & GT; 10000.0{//> 10000 play a bestAttemptContent? . sound = UNNotificationSound (named: UNNotificationSoundName (rawValue: “One card pays one sum . MP3”) Contenthandler ! (bestAttemptContent!) Return } else {//play amount less than 10000 if let array = stringToArray (”(COUNTSTR)”){ print (array) pushLocalNotification (array, 0)}}} else { bestAttemptContent ?. Sound = unnotification sound. Default Contenthandler! (bestAttemptContent!)} 

我的拆分金额的方法,我们服务器语音播报的消息格式:一卡通到账100.32元

 // MARK: - 使用本地通知12.1以上系统
    func play_registerNotification(str:String){
        if !str.hasPrefix("一卡通到账"){ //通知内容不是金额播报类型
            bestAttemptContent?.sound = UNNotificationSound.default
            contentHandler!(bestAttemptContent!)
            return
        }
        
        if (str as NSString).length <= 10{ //长度问题
            bestAttemptContent?.sound = UNNotificationSound.default
            contentHandler!(bestAttemptContent!)
            return
        }
        
        let strCount = (str as NSString).substring(with: NSMakeRange(5, (str as NSString).length-5-1))
        
        print(strCount)
        
        if let countStr =  Float(strCount) {
            if countStr > 10000.0{
                //大于10000播放收款一笔
                bestAttemptContent?.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "一卡通收款一笔.mp3"))
                contentHandler!(bestAttemptContent!)
                return
            }else{ //小于10000的播放金额
                
                if let array = stringToArray("\(countStr)") {
                    print(array)
                     pushLocalNotification(array, 0)
                }
            }
            
        }else{
            bestAttemptContent?.sound = UNNotificationSound.default
            contentHandler!(bestAttemptContent!)
        }
    }

At present, this scheme can solve the problem of voice broadcast, but there are drawbacks, the local push push a voice file will vibrate once, so you can guide users to turn off the vibration. Or multi-recorded voice files, such as 123.06 yuan, can be divided into “One hundred and twenty-three points” and “06 yuan,” with a prefix of more than 1,000 voice files, in the split amount when the method changes according to the actual situation, so that three or four vibration can solve the problem of voice broadcast, but the package will be larger.

Note: The pushed field must add mutable-content:1 field, otherwise the program push will not go UNNotificationServiceExtension.

Note again: Alipay's solution is VoIP (the most perfect voice broadcast solution), but if your project does not support the call function, do not use it, or you can try it to see if it can pass the review