语记手表版 v0.1:一键录音、WatchConnectivity 传输、表盘复杂功能、iPhone 接收转写

This commit is contained in:
2026-10-28 15:24:18 +08:00
commit 0858e706c5
9 changed files with 523 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
import AVFoundation
import Foundation
/// 手表端录音:AVAudioRecorder 写 AAC .m4a,存在手表 Documents 里,停后交给 WatchSessionManager 传到 iPhone。
@MainActor
final class WatchRecorder: ObservableObject {
@Published var isRecording = false
@Published var elapsed: TimeInterval = 0
private var recorder: AVAudioRecorder?
private var timer: Timer?
private var startDate: Date?
private(set) var currentFileURL: URL?
func requestPermission() async -> Bool {
await withCheckedContinuation { continuation in
AVAudioSession.sharedInstance().requestRecordPermission { granted in
continuation.resume(returning: granted)
}
}
}
func start() throws {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.record, mode: .default)
try session.setActive(true)
let fileName = "watch-note-\(Int(Date().timeIntervalSince1970)).m4a"
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let url = docs.appendingPathComponent(fileName)
currentFileURL = url
let settings: [String: Any] = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44_100,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
recorder = try AVAudioRecorder(url: url, settings: settings)
recorder?.record()
isRecording = true
startDate = Date()
elapsed = 0
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { [weak self] _ in
Task { @MainActor in
guard let self, let start = self.startDate else { return }
self.elapsed = Date().timeIntervalSince(start)
}
}
}
/// 停止录音,返回录音文件 URL(nil 表示没录成)
func stop() -> URL? {
timer?.invalidate()
timer = nil
recorder?.stop()
recorder = nil
isRecording = false
try? AVAudioSession.sharedInstance().setActive(false)
return currentFileURL
}
}