如何为recognitionTask适配async-await并获取任务实例
解决SFSpeechRecognitionTask适配async-await后无法主动取消的问题
你的核心问题是原适配方法将SFSpeechRecognitionTask封装在内部,外部无法访问,只能依赖系统任务取消机制,没法主动触发语音识别的取消操作。要解决这个问题,需要把底层任务实例的控制权暴露给外部,同时保留async-await的异步结果获取能力。
方案1:自定义封装结构体,支持中间结果与主动取消
这种方式既可以拿到取消任务的能力,还能处理语音识别的中间实时结果(SFSpeechRecognitionTask的回调会多次触发,返回逐字识别的实时结果):
import Speech struct SpeechRecognitionSession { private let underlyingTask: SFSpeechRecognitionTask let recognitionResults: AsyncThrowingStream<SFSpeechRecognitionResult?, Error> init(request: SFSpeechRecognitionRequest, using recognizer: SFSpeechRecognizer) { // 创建异步流传递识别结果 let (stream, continuation) = AsyncThrowingStream.makeStream(of: SFSpeechRecognitionResult?.self) self.recognitionResults = stream // 初始化底层识别任务 self.underlyingTask = recognizer.recognitionTask(with: request) { result, error in if let error = error { continuation.finish(throwing: error) return } if let result = result { continuation.yield(result) // 拿到最终结果后结束流 if result.isFinal { continuation.finish() } } else { continuation.finish() } } } /// 主动取消语音识别任务 func cancelRecognition() { underlyingTask.cancel() } } // 给SFSpeechRecognizer添加便捷扩展 extension SFSpeechRecognizer { func recognitionSession(with request: SFSpeechRecognitionRequest) -> SpeechRecognitionSession { SpeechRecognitionSession(request: request, using: self) } }
使用示例
// 初始化识别器与请求 guard let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "zh-CN")) else { fatalError("当前设备不支持语音识别") } let audioRequest = SFSpeechAudioBufferRecognitionRequest() // 配置音频请求(如添加麦克风输入数据)... // 创建会话对象 let session = recognizer.recognitionSession(with: audioRequest) // 启动异步任务监听识别结果 Task { do { for try await result in session.recognitionResults { guard let result = result else { continue } print("实时识别:\(result.bestTranscription.formattedString)") if result.isFinal { print("最终识别结果:\(result.bestTranscription.formattedString)") break } } } catch { print("识别出错:\(error.localizedDescription)") } } // 按需主动取消识别(比如用户点击取消按钮) // session.cancelRecognition()
方案2:仅获取最终结果的简化封装
如果你的场景只需要最终识别结果,不需要处理中间实时结果,可以用更简洁的封装:
import Speech struct FinalRecognitionTask { private let underlyingTask: SFSpeechRecognitionTask private let finalResult: () async throws -> SFSpeechRecognitionResult? init(request: SFSpeechRecognitionRequest, using recognizer: SFSpeechRecognizer) { var continuation: CheckedContinuation<SFSpeechRecognitionResult?, Error>? self.underlyingTask = recognizer.recognitionTask(with: request) { result, error in guard let cont = continuation else { return } if let error = error { cont.resume(throwing: error) } else if let result = result, result.isFinal { // 仅在拿到最终结果时返回 cont.resume(returning: result) } continuation = nil } self.finalResult = { try await withCheckedThrowingContinuation { cont in continuation = cont } } } func cancel() { underlyingTask.cancel() } func getFinalResult() async throws -> SFSpeechRecognitionResult? { try await finalResult() } }
使用示例
let task = FinalRecognitionTask(request: audioRequest, using: recognizer) // 主动取消识别 // task.cancel() do { let finalResult = try await task.getFinalResult() if let result = finalResult { print("最终识别内容:\(result.bestTranscription.formattedString)") } } catch { print("识别失败:\(error)") }
原代码的问题说明
你之前的适配方法用withCheckedThrowingContinuation只返回一次结果,同时把SFSpeechRecognitionTask变量限制在方法内部,导致外部无法主动调用cancel()。另外,原代码没有处理SFSpeechRecognitionTask多次回调的特性,会丢失中间识别结果。
内容的提问来源于stack exchange,提问作者Eray Diler
相关产品推荐
相关产品推荐

