简介:本文深入解析iOS推送在后台、锁屏及应用被杀死状态下实现合成语音播报的技术方案,结合微信收款语音场景,提供可落地的代码实现与权限配置指南。
微信收款码语音播报的核心需求是:在任意应用状态下(后台、锁屏、应用被杀死)接收推送后立即播放自定义语音。这一功能涉及iOS系统的三个关键技术点:
iOS系统对后台任务有严格限制,常规应用在进入后台后3分钟内会被挂起,锁屏状态下更是禁止大多数后台活动。要实现类似微信的语音播报,必须通过以下技术组合:
在Xcode工程中完成以下配置:
<!-- Info.plist 添加 -->
<key>UIBackgroundModes</key>
<array>
<string>audio</string> <!-- 音频后台模式 -->
<string>remote-notification</string> <!-- 推送后台模式 -->
</array>
关键在于使用content-available
字段触发后台处理:
{
"aps": {
"alert": "新收款35元",
"sound": "default",
"content-available": 1
},
"custom_data": {
"amount": "35",
"type": "payment"
}
}
import UserNotifications
import AVFoundation
class NotificationHandler: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
// 处理用户点击通知
playPaymentVoice(amount: extractAmount(from: response))
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// 处理前台收到通知
completionHandler([.banner, .sound])
}
// 静默推送处理(应用在后台或杀死状态)
func handleSilentNotification(userInfo: [AnyHashable: Any]) {
guard let amount = extractAmount(from: userInfo) else { return }
DispatchQueue.global(qos: .userInitiated).async {
self.playPaymentVoice(amount: amount)
}
}
private func extractAmount(from userInfo: [AnyHashable: Any]) -> String? {
return (userInfo["custom_data"] as? [String: Any])?["amount"] as? String
}
}
extension NotificationHandler {
func playPaymentVoice(amount: String) {
let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: "收款\(amount)元")
// 中文语音配置
utterance.voice = AVSpeechSynthesisVoice(language: "zh-CN")
utterance.rate = 0.45 // 适中语速
utterance.volume = 1.0
// 播放前确保音频会话激活
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("音频会话配置失败: \(error)")
return
}
synthesizer.speak(utterance)
}
}
当应用被手动杀死时,常规推送无法唤醒应用执行代码。此时需要:
class VoIPHandler: NSObject, PKPushRegistryDelegate {
func pushRegistry(_ registry: PKPushRegistry,
didUpdate credentials: PKPushCredentials,
for type: PKPushType) {
// 注册VoIP token
}
func pushRegistry(_ registry: PKPushRegistry,
didReceiveIncomingPushWith payload: PKPushPayload,
for type: PKPushType,
completion: @escaping () -> Void) {
// 处理VoIP推送
if let amount = payload.dictionaryPayload["amount"] as? String {
DispatchQueue.main.async {
UIApplication.shared.applicationIconBadgeNumber += 1
// 触发本地通知作为后备方案
self.showLocalNotification(amount: amount)
}
}
completion()
}
private func showLocalNotification(amount: String) {
let content = UNMutableNotificationContent()
content.title = "新收款"
content.body = "\(amount)元已到账"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
let request = UNNotificationRequest(identifier: "payment",
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
}
2. **备用本地通知方案**:在应用被杀死时,通过推送携带足够信息触发系统级通知,用户点击后唤醒应用执行语音播报
# 三、关键问题解决方案
## 1. 权限配置清单
| 权限类型 | 配置位置 | 必要参数 |
|---------|---------|---------|
| 推送权限 | Info.plist | 添加Usage Description |
| 音频权限 | Info.plist | NSMicrophoneUsageDescription(VoIP方案需要) |
| 后台模式 | Capabilities | 勾选Audio和Background Fetch |
## 2. 常见问题处理
**问题1**:后台不播放语音
- 解决方案:检查`AVAudioSession`配置是否在播放前激活
- 调试技巧:在`playPaymentVoice`方法开头添加日志,确认方法被调用
**问题2**:应用被杀死后无响应
- 解决方案:
1. 优先使用VoIP推送(需申请特殊权限)
2. 次优方案:推送中包含足够信息触发系统通知,用户点击后处理
**问题3**:语音被系统静音
- 解决方案:
```swift
// 在播放前设置音频类别
try AVAudioSession.sharedInstance().setCategory(.playback, options: [])
渐进式实现策略:
测试覆盖场景:
性能优化技巧:
AVSpeechSynthesizer
实例保持为单例userInitiated
队列而非主队列
// AppDelegate中的完整集成
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var notificationHandler = NotificationHandler()
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 配置推送中心
let center = UNUserNotificationCenter.current()
center.delegate = notificationHandler
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
guard granted else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
// VoIP推送配置(可选)
setupVoIP()
return true
}
private func setupVoIP() {
let registry = PKPushRegistry(queue: DispatchQueue.main)
registry.delegate = VoIPHandler()
registry.desiredPushTypes = [.voIP]
}
// 处理静默推送(应用在后台时)
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
notificationHandler.handleSilentNotification(userInfo: userInfo)
completionHandler(.newData)
}
}
实现iOS全状态下的语音播报功能需要综合运用推送技术、后台模式和音频处理。关键点在于:
未来优化方向包括:
通过本文提供的方案,开发者可以构建出稳定可靠的语音播报系统,满足金融、零售等行业的实时通知需求。实际开发中建议先在测试环境充分验证各场景下的表现,再逐步部署到生产环境。