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

TestFlight中AVFAudio崩溃求助:Swift录音App真机正常TestFlight崩溃

iOS录音App TestFlight分发后崩溃问题排查

我用Swift开发了一款带录音和播放功能的App,在自己的真机上运行完全正常,但通过TestFlight分发到其他测试设备时出现崩溃。相关Swift 5.5代码如下:

func startRecording() {
    if recognitionTask != nil {
        recognitionTask?.cancel()
        recognitionTask = nil
    }
    
    let audioSession = AVAudioSession.sharedInstance()
    do {
        try audioSession.setCategory(AVAudioSession.Category.playAndRecord, mode: .spokenAudio, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
        try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
    } catch {
        print("audioSession properties weren't set because of an error.")
    }
    
    let bus = 0
    let inputNode = audioEngine.inputNode
    
    let inputFormat = inputNode.outputFormat(forBus: bus)
    let outputFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)!
    let converter = AVAudioConverter(from: inputFormat, to: outputFormat)!
    
    inputNode.removeTap(onBus: 0)
    inputNode.installTap(onBus: bus, bufferSize: 1024, format: inputFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
        var newBufferAvailable = true
        let inputCallback: AVAudioConverterInputBlock = { inNumPackets, outStatus in
            if newBufferAvailable {
                outStatus.pointee = .haveData
                newBufferAvailable = false
                return buffer
            } else {
                outStatus.pointee = .noDataNow
                return nil
            }
        }
        
        let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate))!
        
        var error: NSError?
        let _ = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
        self.recognitionRequest?.append(convertedBuffer)
    }
    
    audioEngine.prepare()
    try audioEngine.start()
    DispatchQueue.main.async {
        self.speechRecognize()
    }
} 

崩溃时捕获到的日志信息:

AVFAudio: AVAE_RaiseException(NSString*, ...) + 52

崩溃原因分析及修复方案

1. 隐私权限配置缺失

TestFlight分发的App会严格校验系统隐私权限,你的代码未处理麦克风权限请求,且Info.plist中大概率缺少NSMicrophoneUsageDescription键,这会直接导致App在调用录音功能时崩溃。

  • 修复步骤:
    • 在Info.plist中添加NSMicrophoneUsageDescription键,值填写麦克风使用说明(如"需要访问麦克风进行录音操作")。
    • 在启动录音前主动请求权限:
      import AVFoundation
      
      func requestMicrophonePermission() {
          AVAudioSession.sharedInstance().requestRecordPermission { granted in
              DispatchQueue.main.async {
                  if granted {
                      self.startRecording()
                  } else {
                      // 提示用户前往设置开启麦克风权限
                  }
              }
          }
      }
      
    调用startRecording()前先执行requestMicrophonePermission()。

2. 强制解包触发崩溃

代码中多处使用!强制解包(如音频格式、转换器、转换后的buffer),若设备不支持指定的音频参数,这些操作会直接触发崩溃。

  • 修复:用可选绑定替代强制解包,处理创建失败的情况:
    // 替换原输出格式创建代码
    guard let outputFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true) else {
        print("无法创建目标音频格式")
        return
    }
    guard let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else {
        print("无法创建音频格式转换器")
        return
    }
    // 替换转换buffer创建代码
    guard let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate)) else {
        print("无法创建转换后的音频缓存")
        return
    }
    

3. 音频会话错误未拦截

当前代码仅打印音频会话配置错误,未终止后续流程,若会话激活失败,继续执行audioEngine相关操作会引发崩溃。

  • 修复:在catch块中终止流程并提示:
    do {
        try audioSession.setCategory(.playAndRecord, mode: .spokenAudio, options: .defaultToSpeaker)
        try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
    } catch {
        print("音频会话配置失败: \(error.localizedDescription)")
        // 可添加UI提示用户,直接返回不继续执行
        return
    }
    

4. AudioEngine启动错误未处理

代码中try audioEngine.start()未用do-catch包裹,启动失败会直接崩溃。

  • 修复:添加错误处理并清理资源:
    do {
        try audioEngine.start()
        DispatchQueue.main.async {
            self.speechRecognize()
        }
    } catch {
        print("音频引擎启动失败: \(error.localizedDescription)")
        inputNode.removeTap(onBus: bus) // 清理已安装的tap
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 04:01:19