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 } }