使用 Apple Speech 框架将语音转录为文本。适用于使用 AVAudioEngine 实现实时麦克风转录、识别录制的音频文件、处理语音和麦克风授权、选择设备端或服务器端 SFSpeechRecognizer 行为,或在 iOS 26+ 上采用 SpeechAnalyzer、SpeechTranscriber、DictationTranscriber、AssetInventory 和异步结果流。
语音识别
使用 Apple Speech 框架将实时和预录制的音频转录为文本。
涵盖 SpeechAnalyzer / SpeechTranscriber(iOS 26+)和
SFSpeechRecognizer(iOS 10+)的回退指南。
范围边界: 此技能用于语音到文本识别、语音授权、麦克风捕获管道和结果处理。文本分析、转录后的语言识别、情感分析、嵌入和翻译交给 natural-language;音频播放 UI 交给 avkit;转录内容的摘要或生成交给 apple-on-device-ai。
目录
- SpeechAnalyzer 策略(iOS 26+)
- SFSpeechRecognizer 设置
- 授权
- 实时麦克风转录
- 预录制音频文件识别
- 设备端与服务器端识别
- 处理结果
- 常见错误
- 审查清单
- 参考资料
SpeechAnalyzer 策略(iOS 26+)
对于现代 iOS 26+ 语音分析,尤其是长格式录音、实时转录、时间索引转录和完全设备端流程,请使用 SpeechAnalyzer。对于 iOS 10+ 部署目标、服务器端语言覆盖或现有的回调/委托实现,保留 SFSpeechRecognizer。
在实现 iOS 26+ 转录管道、模型资产处理、易变结果或文件/缓冲区示例时,请阅读 SpeechAnalyzer 模式。
SpeechAnalyzer 设置清单
- 选择模块:
SpeechTranscriber用于较新的通用设备端模型。DictationTranscriber当当前设备或语言不支持SpeechTranscriber且可接受听写兼容支持时。SpeechDetector仅在与转录器结合使用时,当语音活动检测值得权衡准确性和功耗时。
- 在创建会话前检查支持:
SpeechTranscriber.isAvailableSpeechTranscriber.supportedLocale(equivalentTo:)SpeechTranscriber.installedLocales/supportedLocales在显示语言选择时。
- 选择文档化的预设:
.transcription用于基本准确的转录。.progressiveTranscription用于实时 UI 更新。.timeIndexedProgressiveTranscription当播放高亮需要audioTimeRange时。
- 使用
AssetInventory.assetInstallationRequest安装所需资产。 - 在产生
AnalyzerInput之前,将实时音频缓冲区转换为SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith:)。 - 在单独的任务中从模块的
AsyncSequence消费结果。 - 显式使用
finalizeAndFinish(through:)、finalizeAndFinishThroughEndOfInput()或cancelAndFinishNow()完成。
不要使用 offlineTranscription 预设;Apple 没有文档化该预设。完成 AsyncStream 输入序列并不会完成分析器会话。
SFSpeechRecognizer 设置
使用语言环境创建识别器
import Speech
// 默认语言环境(用户当前语言)
let recognizer = SFSpeechRecognizer()
// 特定语言环境
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))
// 检查该语言环境是否支持识别
guard let recognizer, recognizer.isAvailable else {
print("语音识别不可用")
return
}
监控可用性变化
final class SpeechManager: NSObject, SFSpeechRecognizerDelegate {
private let recognizer = SFSpeechRecognizer()!
override init() {
super.init()
recognizer.delegate = self
}
func speechRecognizer(
_ speechRecognizer: SFSpeechRecognizer,
availabilityDidChange available: Bool
) {
// 更新 UI — 不可用时禁用录音按钮
}
}
授权
在开始实时转录之前,请求同时请求语音识别和麦克风权限。在 Info.plist 中添加以下键:
NSSpeechRecognitionUsageDescriptionNSMicrophoneUsageDescription
import Speech
import AVFoundation
func requestPermissions() async -> Bool {
let speechStatus = await withCheckedContinuation { continuation in
SFSpeechRecognizer.requestAuthorization { status in
continuation.resume(returning: status)
}
}
guard speechStatus == .authorized else { return false }
let micStatus: Bool
if #available(iOS 17, *) {
micStatus = await AVAudioApplication.requestRecordPermission()
} else {
micStatus = await withCheckedContinuation { continuation in
AVAudioSession.sharedInstance().requestRecordPermission { granted in
continuation.resume(returning: granted)
}
}
}
return micStatus
}
实时麦克风转录
标准模式:AVAudioEngine 捕获麦克风音频 → 缓冲区附加到 SFSpeechAudioBufferRecognitionRequest → 结果流式传输。
import Speech
import AVFoundation
final class LiveTranscriber {
private let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!
private let audioEngine = AVAudioEngine()
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
func startTranscribing() throws {
// 取消任何进行中的任务
recognitionTask?.cancel()
recognitionTask = nil
// 配置音频会话
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
// 创建请求
let request = SFSpeechAudioBufferRecognitionRequest()
request.shouldReportPartialResults = true
self.recognitionRequest = request
// 开始识别任务
recognitionTask = recognizer.recognitionTask(with: request) { result, error in
if let result {
let text = result.bestTranscription.formattedString
print("转录:\(text)")
if result.isFinal {
self.stopTranscribing()
}
}
if let error {
print("识别错误:\(error)")
self.stopTranscribing()
}
}
// 安装音频 tap
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()
}
func stopTranscribing() {
audioEngine.stop()
audioEngine.inputNode.removeTap(onBus: 0)
recognitionRequest?.endAudio()
recognitionRequest = nil
recognitionTask?.cancel()
recognitionTask = nil
}
}
预录制音频文件识别
使用 SFSpeechURLRecognitionRequest 识别磁盘上的音频文件:
func transcribeFile(at url: URL) async throws -> String {
guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
throw SpeechError.unavailable
}
let request = SFSpeechURLRecognitionRequest(url: url)
request.shouldReportPartialResults = false
return try await withCheckedThrowingContinuation { continuation in
var didResume = false
recognizer.recognitionTask(with: request) { result, error in
guard !didResume else { return }
if let error {
didResume = true
continuation.resume(throwing: error)
} else if let result, result.isFinal {
didResume = true
continuation.resume(
returning: result.bestTranscription.formattedString
)
}
}
}
}
设备端与服务器端识别
SFSpeechRecognizer 可以在 iOS 13+ 上对支持的语言环境使用设备端识别。如果 supportsOnDeviceRecognition 为 false,则识别器需要网络连接。requiresOnDeviceRecognition 仅在识别器支持时生效。
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!
// 检查该语言环境是否支持设备端
if recognizer.supportsOnDeviceRecognition {
let request = SFSpeechAudioBufferRecognitionRequest()
request.requiresOnDeviceRecognition = true // 强制设备端
}
SFSpeechRecognizer 请求可能仍然不适合长格式捕获。Apple 文档指出语音识别大约有一分钟的任务限制和其他服务限制。对于 iOS 26+ 上的长录音,优先使用 SpeechAnalyzer;否则在达到限制之前分块或重新启动识别,并在任务之间保留转录状态。
处理结果
部分结果与最终结果
使用 shouldReportPartialResults 时,替换显示的部分转录,直到 SFSpeechRecognitionResult.isFinal 提交它。这与 SpeechTranscriber.Result.isFinal 不同,后者的易变属性范围必须被该范围的最终结果替换。实时示例和 references/speechanalyzer-patterns.md 包含标准循环。
访问替代转录和置信度
recognizer.recognitionTask(with: request) { result, error in
guard let result else { return }
// 最佳转录
let best = result.bestTranscription
// 所有替代(按置信度降序排列)
for transcription in result.transcriptions {
for segment in transcription.segments {
print("\(segment.substring): \(segment.confidence)")
}
}
}
添加标点(iOS 16+)
let request = SFSpeechAudioBufferRecognitionRequest()
request.addsPunctuation = true
上下文字符串
提高领域特定术语的识别:
let request = SFSpeechAudioBufferRecognitionRequest()
request.contextualStrings = ["SwiftUI", "Xcode", "CloudKit"]
常见错误
| 错误 | 修复 |
|---|---|
| 实时音频请求仅语音授权 | 同时请求语音和麦克风权限。 |
| 识别器可用性只检查一次 | 观察委托可用性变化和模型服务丢失。 |
| 最终/错误/滚动路径执行不同的清理 | 使用一个 stopTranscribing() 所有者来处理引擎停止、tap 移除、endAudio 和任务取消。 |
| 对每个语言环境强制使用设备端模式 | 检查 supportsOnDeviceRecognition 并提供回退。 |
一个 SFSpeechRecognizer 任务用于长格式捕获 |
在 iOS 26+ 上优先使用 SpeechAnalyzer,或滚动有界段同时保留已提交的转录。 |
| 完成分析器输入被视为会话完成 | 显式通过最后一个样本完成或取消并完成。 |
| 易变的 SpeechAnalyzer 结果被追加 | 替换易变范围,直到最终结果提交它。 |
| 第二个识别任务在第一个结束前开始 | 取消/完成活动任务并在替换前完成清理。 |
加载 references/speechanalyzer-patterns.md 获取完整的分析器完成和易变结果代码。
审查清单
- [ ]
NSSpeechRecognitionUsageDescription在 Info.plist 中 - [ ]
NSMicrophoneUsageDescription在 Info.plist 中(如果使用实时音频) - [ ] 在开始识别前请求授权
- [ ] 设置
SFSpeechRecognizerDelegate以处理availabilityDidChange - [ ] 识别结束时停止音频引擎并移除 tap
- [ ] 录制完成时调用
recognitionRequest.endAudio() - [ ] 在开始新任务前取消之前的
recognitionTask - [ ] 在要求设备端模式前检查
supportsOnDeviceRecognition - [ ] 部分结果与最终结果(
isFinal)分开处理 - [ ] 考虑
SFSpeechRecognizer的一分钟/服务限制 - [ ] 对于 iOS 26+:在使用
SpeechAnalyzer前安装AssetInventory资产 - [ ] 对于 iOS 26+:检查
SpeechTranscriber.isAvailable和语言支持 - [ ] 对于 iOS 26+:将实时缓冲区转换为分析器兼容格式
- [ ] 对于 iOS 26+:显式完成或取消分析器会话
- [ ] 对于 iOS 26+:易变结果被最终结果替换,而不是重复
参考资料
- Speech 框架
- SpeechAnalyzer
- SpeechTranscriber
- SpeechTranscriber.Preset
- DictationTranscriber
- SpeechDetector
- SFSpeechRecognizer
- SFSpeechAudioBufferRecognitionRequest
- SFSpeechURLRecognitionRequest
- SFSpeechRecognitionResult
- SFSpeechRecognitionRequest
- AssetInventory
- 请求使用语音识别的权限
- 在实时音频中识别语音
- 使用 SpeechAnalyzer 为你的应用带来先进的语音转文本功能






