SFSpeechRecognizer 是Apple在iOS生态系统中用于语音识别的主要API。该框架通过Speech.framework提供对设备ASR引擎的访问,支持实时流式转录和单次音频文件识别。SFSpeechRecognizer可用于iOS 10+、macOS 10.15+、watchOS 6+和tvOS 17+。从iOS 17开始,Apple添加了完整的设备端模式,无需互联网连接即可识别语音。根据Apple Speech Documentation, 2025,SFSpeechRecognizer在超过200,000个App Store应用中使用,每天处理数百万次请求,英语的WER为7%。
要点
SFSpeechRecognizer — 来自Speech.framework的一个类,代表特定语言的语音识别器。它使用Locale(ru_RU、en_US、zh_CN)进行初始化。SFSpeechRecognizer管理识别请求(SFSpeechRecognitionRequest)并返回带有转录和元数据的结果(SFSpeechRecognitionResult)。支持两种类型的请求:SFSpeechAudioBufferRecognitionRequest(实时音频流)和SFSpeechURLRecognitionRequest(音频文件)。两者都返回SFTranscription — 备选识别变体的数组。
SFSpeechRecognitionResult 包含:bestTranscription(最佳变体,SFTranscription)、transcriptions(所有备选)、isFinal(最终/临时)。SFTranscription包含:formattedString(文本)、segments(带有时间戳、置信度、substringRange、alternativeSubstrings的SFTranscriptionSegment数组)。每个片段的置信度(0..1) — 允许过滤不可靠的片段。segments — Speech.framework的关键特性:为每个单词提供带置信度的注释。
SFSpeechRecognizer的架构:AVFoundation(音频捕获)→ Audio Buffer → SFSpeechAudioBufferRecognitionRequest → SFSpeechRecognizer(ASR引擎)→ SFSpeechRecognitionResult → SFSpeechRecognitionTask(控制:cancel、finish、pause)。Apple的ASR引擎使用混合架构:Conformer(编码器)+ RNNT(解码器)用于设备端,Transducer用于云端。模型针对Apple Neural Engine(ANE)进行了优化。在A17 Pro上,设备端ASR的RTF为0.3–0.6(比实时更快)。
| 组件 | 用途 | iOS版本 |
|---|---|---|
| SFSpeechRecognizer | 主要识别类 | iOS 10+ |
| SFSpeechAudioBufferRecognitionRequest | 实时音频流 | iOS 10+ |
| SFSpeechURLRecognitionRequest | 音频文件(.wav, .m4a) | iOS 10+ |
| SFSpeechRecognitionTask | 请求管理(cancel, finish) | iOS 10+ |
| 设备端识别 | 离线ASR | iOS 17+ |
| 组合识别 | 设备端+云端混合 | iOS 17+ |
音频要求:SFSpeechRecognizer接受LPCM(16 kHz、16 bit、单声道)或Opus(iOS 17+)。AVFoundation可以以任何格式捕获缓冲区,请求自动转换。最小长度:0.5秒(静音检测)。最大:每个请求1分钟(云端)/2分钟(设备端)。对于长转录,将音频分割成30-60秒的片段,重叠2-5秒。
用户权限:SFSpeechRecognizer需要两个明确的权限。NSMicrophoneUsageDescription(Privacy — Microphone Usage Description)— 用于访问麦克风。NSSpeechRecognitionUsageDescription(Privacy — Speech Recognition Usage Description)— 用于访问语音识别。两者都是向用户解释原因的字符串。SFSpeechRecognizer.requestAuthorization — 调用权限对话框。状态:notDetermined、denied、restricted、authorized。如果没有authorized,识别器调用将返回错误216(Speech framework error)。
可用性检查:SFSpeechRecognizer.isAvailable — 检查ASR是否可用于当前语言。在iOS 16-上,isAvailable = false(无互联网)。在iOS 17+上,isAvailable = true(设备端语言即使在离线时也可用)。SFSpeechRecognizer.supportedLocales — 获取设备支持的语言列表的静态方法。建议在每次启动前检查isAvailable(用户可能在设置中关闭Siri/听写)。可用性取决于地区:中文 — 仅在中国,阿拉伯语 — 在阿联酋。
// SFSpeechRecognizer设置
import Speech
SFSpeechRecognizer.requestAuthorization { status in
guard status == .authorized else { return }
}
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "ru_RU"))!
guard recognizer.isAvailable else {
print("ASR不可用。请在设置中启用Siri和听写")
return
}
let request = SFSpeechAudioBufferRecognitionRequest()
request.requiresOnDeviceRecognition = true
错误处理:SFSpeechRecognizer通过可以返回错误的completion handler调用。主要错误:203 — no internet(云端,iOS 16-);216 — not authorized(无权限);301 — audio error(麦克风/格式问题);401 — service unavailable(服务过载);601 — language unavailable(设备不支持该语言)。对于设备端iOS 17+,主要错误:301(音频),601(语言)。始终处理completion handler — 错误可能在录制期间的任何时候发生。
实时ASR管道:AVAudioEngine(从麦克风捕获)→ installTap(音频缓冲区)→ request.append(buffer) → SFSpeechRecognizer → recognitionTask → delegate/resultHandler。AVAudioEngine确保低延迟(从麦克风到缓冲区5-10毫秒)。SFSpeechRecognitionDelegate:didHypothesizeTranscription(每200-500毫秒 — 部分文本)、didFinishRecognition(最终结果)。Task.isFinishing — 可以在识别完成时取消。对于连续识别(无限听写)— 在isFinal之后创建新task。
SFSpeechRecognitionTaskDelegate:可选方法。speechRecognitionDidDetectSpeech(开始说话)— 用于UI指示。didHypothesizeTranscription(临时文本)— 用于在说话时显示。didFinishRecognition(最终结果)— 用于固定文本。didFinishSuccessfully(成功/错误)— 用于完成。didProcessAudio(音频缓冲区已处理)— 用于RMS表。建议实现didHypothesizeTranscription — 用户立即看到文本,无需等待。最终转录 — 用于保存。
// 实时语音识别
let audioEngine = AVAudioEngine()
let request = SFSpeechAudioBufferRecognitionRequest()
request.shouldReportPartialResults = true
recognitionTask = recognizer.recognitionTask(with: request) { result, error in
if let result = result {
textView.text = result.bestTranscription.formattedString
}
if error != nil || result?.isFinal == true {
audioEngine.stop()
request.endAudio()
}
}
let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024,
format: recordingFormat) { buffer, _ in
request.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
连续识别:对于"始终监听"模式(语音输入、助手),需要在isFinal后重新启动任务。创建循环:finishTask → request = SFSpeechAudioBufferRecognitionRequest() → recognitionTask = recognizer.recognitionTask(with: request)。为避免暂停,将一个任务的结束与下一个任务的开始重叠(在前一个任务结束前1-2秒开始新请求)。AVFoundation AVAudioSession — .playAndRecord配置用于同时播放和录制。在iOS 17+上,设备端连续ASR每小时消耗2-5%的电池(A15+)。
SFSpeechURLRecognitionRequest — 通过URL识别音频文件的请求。支持的格式:.wav(16 kHz、16 bit、单声道)、.m4a(AAC)、.mp4、.mov。最大长度:云端约1分钟,设备端约2分钟。对于更长的文件 — 分割成片段(AVAssetExportSession + trim)。SFSpeechURLRecognitionRequest不支持流式(无部分结果)— 仅最终结果。对于带文件的部分结果 — 转换为Audio Buffer Request。
获取音频文件的转录:SFSpeechRecognizer.recognitionTask(with: request) resultHandler。提取bestTranscription.formattedString。对于单词级时间戳 — 来自bestTranscription.segments的segments(segment.duration、segment.timestamp、segment.substring)。每个片段的置信度 — ASR对每个单词的置信度(用于过滤)。备选变体 — 低置信度单词的transcriptionFormatter备选项。对于多说话者的文件 — SFSpeechRecognitionRequest可能返回多个请求(每个说话者一个,iOS 17+)。
// 音频文件转录
let audioURL = Bundle.main.url(forResource: "recording", withExtension: "m4a")!
let request = SFSpeechURLRecognitionRequest(url: audioURL)
request.requiresOnDeviceRecognition = true
recognizer.recognitionTask(with: request) { result, error in
guard let result = result, error == nil else {
print("错误: \(error?.localizedDescription ?? "unknown")")
return
}
let transcription = result.bestTranscription
let text = transcription.formattedString
let words = transcription.segments.map { segment in
Word(text: segment.substring,
start: segment.timestamp,
duration: segment.duration,
confidence: segment.confidence)
}
}
分割长音频文件:对于超过1分钟的文件,切割成30-60秒的片段,重叠2-3秒(拼接)。AVAssetExportSession + CMTimeRange — 导出片段。替代方案:UISelectionView(用户选择片段)。转录拼接:文本连接,通过重叠拼接(前一片段的最后2-3个单词与下一片段的前2-3个单词比较 — 删除重复)。Vosk和Whisper原生支持长音频,而SFSpeechRecognizer不支持。对于长转录,我推荐Whisper(Core ML)— 无长度限制。
设备端模式(iOS 17+):request.requiresOnDeviceRecognition = true。ASR在Apple Neural Engine(ANE)上本地执行。优点:无需互联网、隐私(音频不离开设备)、零成本(免费)、低延迟(RTF 0.3–0.6)。缺点:精度较低(WER 12–14% vs 7–12%)、每个请求2分钟限制、语言集有限(并非所有60种语言都可在设备端使用)。设备端模型在设备上占用约50-100 MB,在第一次请求时加载。
云端(iOS 16-):request.requiresOnDeviceRecognition = false(或参数缺失)。Apple服务器上的ASR — 更精确(WER 7% EN、12% RU)、更多语言、每个请求最多1分钟。需要互联网(WiFi或蜂窝网络)。Apple不向开发者收取云端ASR费用(免费)。限制:每个应用每分钟1个请求(未指定),但对于生产规模,Apple可能会限制。云端ASR — best-effort质量,无SLA。对于企业应用,请使用Google Cloud ASR或Whisper(Core ML)。
| 参数 | 设备端(iOS 17+) | 云端(iOS 10+) |
|---|---|---|
| 需要互联网 | 否 | 是 |
| WER(EN) | 10–12% | 7% |
| WER(RU) | 12–14% | 12% |
| 延迟(RTF) | 0.3–0.6 | 0.3–0.8(+网络) |
| 最大长度 | 2分钟 | 1分钟 |
| 隐私 | 完全 | 音频发送到服务器 |
| 免费 | 是 | 是 |
组合识别(iOS 17+):有互联网时request.requiresOnDeviceRecognition = false(云端),离线时 = true(回退)。Apple自动选择模式。对于精度关键的应用:检查NetworkMonitor(NWPathMonitor)— 有互联网用云端,没有则用设备端。对于隐私关键的应用:始终使用设备端。对于转录:云端(更精确)。对于语音输入:设备端(更快)。推荐的组合:80%云端,20%设备端回退。
自定义键盘中的语音输入 — SFSpeechRecognizer嵌入键盘扩展中(iOS 10+)。用户按下麦克风按钮 — ASR将语音转录为文本并插入到文本字段中。要求:部分结果(实时查看文本)、设备端(键盘必须无需互联网工作)。SFSpeechRecognizer + AVSpeechSynthesizer — 语音输入 + 语音输出。对于iOS 17+上的自定义键盘,使用设备端。键盘扩展的限制:AVAudioEngine可用,但有时间限制(后台执行受限)。
会议和讲座的转录 — 用于录制和转录音频的应用(Otter.ai、Rev、Apple Voice Memos)。SFSpeechURLRecognitionRequest处理.m4a文件。对于长录音(30-60分钟)— Whisper Core ML(无长度限制)。SFSpeechRecognizer带时间戳的片段 — 用于文本与音频的同步(播放时高亮文本)。每个片段的置信度 — 用于显示不可靠的片段(黄色高亮)。对于说话人分离(谁说了话)— Apple不提供API,在服务器上使用pyannote-audio + Whisper。
iOS应用中的语音命令 — SFSpeechRecognizer + VGS框架(Voice Grammar Services)用于执行命令:“打开设置”、“发送消息”、“打开手电筒”。基于语法的识别:SFSpeechRecognitionRequest contextualStrings = 命令数组。这将命令识别精度提高到95%(相比自由格式的85%)。SFSpeechRecognitionTask.cancel — 用于在命令执行后中断。VGS + SFSpeechRecognizer + Shortcuts — 无需编写界面的自定义语音命令。对于无障碍:Voice Control(内置)+ SFSpeechRecognizer(自定义命令)。
// 带上下文字符串的语音命令
let request = SFSpeechAudioBufferRecognitionRequest()
request.requiresOnDeviceRecognition = true
request.shouldReportPartialResults = true
request.contextualStrings = ["open contacts", "send message",
"show notifications", "打开手电筒"]
recognitionTask = recognizer.recognitionTask(with: request) { result, _ in
guard let text = result?.bestTranscription.formattedString.lowercased() else { return }
voiceCommands.first { text.contains($0) }.map { command in
DispatchQueue.main.async { self.executeCommand(command) }
recognitionTask?.cancel()
}
}
医疗和法律应用中的听写 — SFSpeechRecognizer用于通过语音创建结构化文档。领域适应:包含医学术语/法律术语的contextualStrings(500+短语)。SFSpeechRecognitionRequest.customLmProbability — contextualStrings的影响(0.1–1.0)。对于医疗听写,使用设备端(患者数据隐私)。在医疗数据上微调的Whisper — 替代方案,WER为5%,而不是基础SFSpeechRecognizer的12%。符合HIPAA:设备端模式(数据不离开设备)。对于就诊转录 — SFSpeechURLRecognitionRequest + 带说话者时间戳的segments。
常见问题
SFSpeechRecognizer 从iOS 10、macOS 10.15、watchOS 6和tvOS 17开始可用。设备端模式 — 从iOS 17开始(requiresOnDeviceRecognition)。在iOS 10–16上,SFSpeechRecognizer仅通过Apple云服务器工作(需要互联网)。所有版本都需要用户的明确许可(NSMicrophoneUsageDescription + NSSpeechRecognitionUsageDescription)。SFSpeechRecognizer.supportedLocales — 动态列表,取决于地区和设备型号。
可以,通过在isFinal后创建新的recognitionTask。在完成上一个任务后重新启动以实现连续识别。在iOS 17上,设备端连续ASR每小时消耗2-5%的电池(A15+)。在iOS 16-上,连续ASR需要互联网(数据消耗约1 MB/分钟)。对于真正连续的识别(始终监听),使用Vosk(离线)或Whisper Core ML。SFSpeechRecognizer在iOS 16-上不支持始终开启模式。
使用result.bestTranscription.segments — 每个SFTranscriptionSegment包含单词的置信度(Float 0..1)。segments[i].substring — 单词的文本。segments[i].confidence — ASR对该单词的置信度。置信度小于0.5的单词 — 不可靠,在界面中高亮它们。segments[i].timestamp — 开始时间(TimeInterval)。segments[i].duration — 持续时间。segments[i].alternativeSubstrings — 单词的备选识别变体。对于长文件,遍历segments可提供每个单词的置信度,无需手动解析。
203 — SFSpeechError.ErrorCode.opportunistic(云端ASR无互联网)。在iOS 16-上或request.requiresOnDeviceRecognition = false但无互联网时发生。解决方案:设置requiresOnDeviceRecognition = true(iOS 17+)用于离线工作。在iOS 16-上使用NWPathMonitor — 无互联网时显示消息“语音识别需要互联网”。替代方案:无网络时使用Vosk(离线,iOS 12+)作为回退。
SFSpeechRecognizer — Apple内置API,免费,易于集成,但有长度限制(1-2分钟)、精度WER 7-14%,且仅适用于iOS生态系统。Whisper Core ML — 开源(MIT),支持99种语言,WER 6-10%,无长度限制,但需要手动集成,Core ML模型(39M-769M参数),RTF 0.5-6(比SFSpeechRecognizer慢)。SFSpeechRecognizer — 用于标准语音输入。Whisper — 用于高精度长音频转录。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。