From d39467f7d3cba5365eaeb5b82b690cf4e446a995 Mon Sep 17 00:00:00 2001 From: jack <212554440+jackjackbits@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:09:09 +0100 Subject: [PATCH] Defer alert-binding dismissal writes out of the view update (#1537) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SwiftUI invokes an alert Binding's setter inside the current view update when the alert dismisses because its get re-evaluated (a scenePhase change while the Bluetooth-off or voice-error alert is up). Both root alert bindings wrote their @Published backing state synchronously from that setter — the 'Publishing changes from within view updates is not allowed' undefined-behavior warning, reproduced on device by launching with Bluetooth off and backgrounding. Defer the write one main-actor hop. Co-authored-by: jack Co-authored-by: Claude Fable 5 --- bitchat/Views/ContentView.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/bitchat/Views/ContentView.swift b/bitchat/Views/ContentView.swift index be2bdeec..4efb0d34 100644 --- a/bitchat/Views/ContentView.swift +++ b/bitchat/Views/ContentView.swift @@ -182,7 +182,13 @@ struct ContentView: View { !hasRootModalPresentation else { return } - appChromeModel.showBluetoothAlert = false + // SwiftUI can invoke this setter inside a view update (the + // alert dismisses when a scenePhase change re-evaluates the + // `get`); publishing synchronously there is undefined + // behavior, so defer the write one hop. + Task { @MainActor in + appChromeModel.showBluetoothAlert = false + } } ) } @@ -205,7 +211,11 @@ struct ContentView: View { !hasRootModalPresentationBesidesVoiceAlert else { return } - voiceRecordingVM.showAlert = false + // Same deferral as the Bluetooth alert above: the setter can + // run inside a view update when the sheet state changes. + Task { @MainActor in + voiceRecordingVM.showAlert = false + } } ) }