Files
voicediary/VoiceDiary/Services/AudioRecorderService.swift
T
2026-02-15 23:01:21 +01:00

78 lines
1.9 KiB
Swift

import AVFoundation
import Foundation
@Observable
@MainActor
final class AudioRecorderService: NSObject {
var isRecording = false
var recordingDuration: TimeInterval = 0
private var audioRecorder: AVAudioRecorder?
private var timer: Timer?
private var startTime: Date?
func startRecording() throws -> URL {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.record, mode: .default)
try session.setActive(true)
let fileName = "memo-\(Date.now.ISO8601Format()).m4a"
.replacingOccurrences(of: ":", with: "-")
let url = VoiceMemo.audioDirectory.appendingPathComponent(fileName)
let settings: [String: Any] = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100.0,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue,
]
audioRecorder = try AVAudioRecorder(url: url, settings: settings)
audioRecorder?.delegate = self
audioRecorder?.record()
isRecording = true
recordingDuration = 0
startTime = .now
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
Task { @MainActor in
guard let self, let startTime = self.startTime else { return }
self.recordingDuration = Date.now.timeIntervalSince(startTime)
}
}
return url
}
func stopRecording() -> (url: URL, duration: TimeInterval)? {
guard let recorder = audioRecorder else { return nil }
let url = recorder.url
let duration = recorder.currentTime
recorder.stop()
timer?.invalidate()
timer = nil
audioRecorder = nil
isRecording = false
startTime = nil
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
return (url, duration)
}
}
extension AudioRecorderService: AVAudioRecorderDelegate {
nonisolated func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
Task { @MainActor in
if !flag {
isRecording = false
timer?.invalidate()
timer = nil
}
}
}
}