You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS应用静音且被杀状态下接收推送播自定义声音报OSStatus 561015905错误

问题描述

需要实现的功能:iOS应用在设备开启静音模式、*App被系统终止(后台被杀)*的状态下,接收远程推送时可播放自定义通知声音。
当前实现运行报错,提示:操作无法完成(OSStatus error 561015905.)

现有实现代码

import UserNotifications
import AVFoundation
import MediaPlayer

class NotificationService: UNNotificationServiceExtension {
    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?
    var audioPlayer = AVAudioPlayer()

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        playSound()
        if let bestAttemptContent = bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // 系统即将终止扩展前触发,需在此回调中返回最终修改后的通知内容,否则将使用原始推送 payload
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

extension NotificationService {
    //MARK: - 音频播放实现
    private func playSound() {
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            try AVAudioSession.sharedInstance().setActive(true)
            if let url = URL(string: "file:///private/var/mobile/Containers/Shared/AppGroup/9A4FCED-7432FE-FEW-A559-234FBW/Library/Sounds/Update.caf") {
                audioPlayer = try AVAudioPlayer(contentsOf: url,  fileTypeHint: AVFileType.caf.rawValue)
                audioPlayer.volume = 1.0
                DispatchQueue.main.asyncAfter(deadline: .now() + 5) { 
                    // 预留5秒延迟播放音频,避免音频因过短被截断无法被听到
                    self.audioPlayer.play()
                }
            }
        } catch {
            print("[SoundPlayer] 加载音频失败: \(error.localizedDescription)")
        }
        
        if (audioPlayer.isPlaying) {
            audioPlayer.stop()
        }
        audioPlayer.play()
    }
}
问题根因与可落地方案
  • 错误码OSStatus 561015905对应内核层面的kAudioUnitErr_CannotDoInCurrentContext,触发原因是通知服务扩展的沙盒权限不足,不被允许配置playback类别的AVAudioSession——这类不受静音键限制的播放权限,仅开放给拥有前台音频播放资格的主App,扩展进程直接调用相关接口会被系统拦截报错。
  • 现有执行逻辑存在硬伤。调用contentHandler提交通知内容后,扩展进程会立刻进入待回收状态,代码里设置的5秒延迟播放任务,基本会在执行前就被系统强制终止,根本无法走到音频播放的逻辑分支。
  • 硬编码的共享容器路径不具备通用性。沙盒路径里的App Group标识是设备维度的随机字符串,换设备、系统版本迭代后路径会直接失效,通过纯字符串拼接file://URL加载资源,会频繁出现文件找不到的加载异常。
  • 核心认知偏差:通知服务扩展本身不支持绕过静音键主动播放自定义长音频。要实现静音模式下的通知声音,完全不需要在扩展里手动初始化音频播放器——只需要在推送payload里指定符合格式要求的自定义声音文件,系统通知进程会负责触发音频播放,这套机制不受App状态、静音键状态、进程权限的限制,哪怕App已经被彻底终止,只要推送到达设备就会正常播放。

修正步骤

  • 将自定义CAF格式音频文件同时打包到主App和通知扩展的Target资源目录中,不要通过硬编码的沙盒路径加载文件。
  • 远程推送的payload中添加sound字段,值为自定义音频的文件名,例如sound: "Update.caf",由系统负责音频的加载和播放,移除扩展里所有手动初始化AVAudioPlayer、配置AVAudioSession的代码。
  • 移除代码中冗余的重复调用audioPlayer.play()的逻辑,避免音频会话状态冲突触发系统错误。
  • 注意自定义音频时长不得超过30秒,否则会被系统截断,无法完整播放。

内容的提问来源于stack exchange,提问作者Yerke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.25 13:15:06