mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 05:25:19 +00:00
Make voice note loading completely lazy with deferred initialization
Aggressive performance optimization to prevent UI freezes: Problem: Even with async loading, creating 10+ VoiceNotePlaybackController instances simultaneously (when scrolling past multiple voice notes) spawned 20+ concurrent background tasks, potentially starving main thread. Solution - Ultra-lazy loading: 1. VoiceNotePlaybackController.init() now does ZERO work - No duration loading - No player creation - Instant initialization 2. Duration loaded on-demand via public loadDuration() method - Called from VoiceNoteView.onAppear after 150ms delay - Reduced priority: .utility instead of .userInitiated - Guard prevents duplicate loading 3. Waveform loading also deferred 150ms - Gives UI time to settle after message appears - Prevents task storms when multiple voice notes appear This spreads the work over time instead of all at once.
This commit is contained in:
@@ -16,12 +16,14 @@ final class VoiceNotePlaybackController: NSObject, ObservableObject, AVAudioPlay
|
||||
init(url: URL) {
|
||||
self.url = url
|
||||
super.init()
|
||||
// Load duration asynchronously to avoid blocking main thread
|
||||
loadDurationAsync()
|
||||
// Don't load anything eagerly - wait until user interaction or view is fully displayed
|
||||
}
|
||||
|
||||
private func loadDurationAsync() {
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
||||
func loadDuration() {
|
||||
// Only load if not already loaded
|
||||
guard duration == 0 else { return }
|
||||
|
||||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
do {
|
||||
let player = try AVAudioPlayer(contentsOf: self.url)
|
||||
@@ -44,7 +46,8 @@ final class VoiceNotePlaybackController: NSObject, ObservableObject, AVAudioPlay
|
||||
stop()
|
||||
self.url = url
|
||||
player = nil
|
||||
loadDurationAsync()
|
||||
duration = 0
|
||||
// Duration will be loaded on demand when needed
|
||||
}
|
||||
|
||||
func togglePlayback() {
|
||||
|
||||
@@ -100,10 +100,13 @@ struct VoiceNoteView: View {
|
||||
.stroke(borderColor, lineWidth: 1)
|
||||
)
|
||||
.onAppear {
|
||||
WaveformCache.shared.waveform(for: url, completion: { bins in
|
||||
self.waveform = bins
|
||||
})
|
||||
// No need to call playback.replaceURL - already initialized with correct URL
|
||||
// Defer both duration and waveform loading to let UI settle after message appears
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||
playback.loadDuration()
|
||||
WaveformCache.shared.waveform(for: url, completion: { bins in
|
||||
self.waveform = bins
|
||||
})
|
||||
}
|
||||
}
|
||||
.onChange(of: url) { newValue in
|
||||
WaveformCache.shared.waveform(for: newValue, completion: { bins in
|
||||
|
||||
Reference in New Issue
Block a user