I Messaggi Vocali nelle App Moderne
L'invio di note vocali è diventato il metodo di comunicazione più utilizzato nelle app di messaggistica. Per creare un'esperienza fluida senza blocchi dell'interfaccia (lag o freeze), occorre eseguire la registrazione in un thread prioritario e convertire l'audio istantaneamente in formato M4A con codec AAC compresso.
Codice Pratico: Voice Recorder con Estrazione Livelli Decibel in SwiftUI
import SwiftUI
import AVFoundation
@Observable
final class VoiceNoteRecorder: NSObject, AVAudioRecorderDelegate {
var isRecording: Bool = false
var audioLevels: [CGFloat] = []
var duration: TimeInterval = 0
private var audioRecorder: AVAudioRecorder?
private var timer: Timer?
private(set) var recordedFileURL: URL?
func startRecording() {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .allowBluetooth])
try audioSession.setActive(true)
let tempDir = FileManager.default.temporaryDirectory
let fileURL = tempDir.appendingPathComponent("voice_note_\(UUID().uuidString).m4a")
self.recordedFileURL = fileURL
let settings: [String: Any] = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 24000.0,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue,
AVEncoderBitRateKey: 32000 // 32kbps: qualità ottima per voce con peso minimo
]
audioRecorder = try AVAudioRecorder(url: fileURL, settings: settings)
audioRecorder?.delegate = self
audioRecorder?.isMeteringEnabled = true
audioRecorder?.record()
isRecording = true
audioLevels.removeAll()
// Monitora i livelli ogni 50ms per disegnare la waveform
timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [weak self] _ in
guard let self = self, let recorder = self.audioRecorder, recorder.isRecording else { return }
recorder.updateMeters()
let decibels = recorder.averagePower(forChannel: 0)
// Normalizza da [-60dB, 0dB] a [0.1, 1.0]
let level = max(0.1, CGFloat((decibels + 60) / 60))
self.audioLevels.append(level)
self.duration = recorder.currentTime
}
} catch {
print("Impossibile avviare registrazione audio: \(error.localizedDescription)")
}
}
func stopRecording() -> URL? {
timer?.invalidate()
timer = nil
audioRecorder?.stop()
isRecording = false
return recordedFileURL
}
}
struct WaveformBarView: View {
let levels: [CGFloat]
var body: some View {
HStack(spacing: 3) {
ForEach(Array(levels.suffix(40).enumerated()), id: \.offset) { _, level in
RoundedRectangle(cornerRadius: 2)
.fill(Color.orange)
.frame(width: 3, height: max(4, level * 36))
}
}
.frame(height: 40)
}
}
Vuoi creare la tua App o hai bisogno di consulenza personalizzata?
Sviluppo app iOS native in SwiftUI, piattaforme backend scalabili e offro supporto strategico per monetizzazione e progetti internazionali.