Am going through a SwiftUI course, so the code is not my own.
When I migrated my @Bindings into @Published items in an @ObservableObject I started getting the following error:
> Publishing changes from within view updates is not allowed, this will cause undefined behavior.
The warning occurs in the ScannerView which is integrated with the main view, BarcodeScannerView. It occurs when an error occurs, and scannerView.alertItem is set to a value.
However, it does not occur when I am setting the value of scannerView.scannedCode, and as far as I can tell, they both come from the sample place, and are the same actions.
There are tons of posts like mine, but I have yet to find an answer. Any thoughts or comments would be very appreciated.
BarcodeScannerView
import SwiftUI
struct BarcodeScannerView: View {
@StateObject var viewModel = BarcodeScannerViewModel()
var body: some View {
NavigationStack {
VStack {
ScannerView(scannedCode: $viewModel.scannedCode, typeScanned: $viewModel.typeScanned, alertItem: $viewModel.alertItem)
.frame(maxWidth: .infinity, maxHeight: 300)
Spacer().frame(height: 60)
BarcodeView(statusText: viewModel.typeScanned)
TextView(statusText: viewModel.statusText, statusTextColor: viewModel.statusTextColor)
}
.navigationTitle("Barcode Scanner")
.alert(item: $viewModel.alertItem) { alertItem in
Alert(title: Text(alertItem.title), message: Text(alertItem.message), dismissButton: alertItem.dismissButton)
}
}
}
}
BarcodeScannerViewModel
import SwiftUI
final class BarcodeScannerViewModel: ObservableObject {
@Published var scannedCode = ""
@Published var typeScanned = "Scanned Barcode"
@Published var alertItem: AlertItem?
var statusText: String {
return scannedCode.isEmpty ? "Not Yet scanned" : scannedCode
}
var statusTextColor: Color {
scannedCode.isEmpty ? .red : .green
}
}
ScannerView
import SwiftUI
struct ScannerView: UIViewControllerRepresentable {
typealias UIViewControllerType = ScannerVC
@Binding var scannedCode : String
@Binding var typeScanned : String
@Binding var alertItem: AlertItem?
func makeCoordinator() -> Coordinator {
Coordinator(scannerView: self)
}
func makeUIViewController(context: Context) -> ScannerVC {
ScannerVC(scannerDelegate: context.coordinator)
}
func updateUIViewController(_ uiViewController: ScannerVC, context: Context) {
}
final class Coordinator: NSObject, ScannerVCDelegate {
private let scannerView: ScannerView
init(scannerView: ScannerView) {
self.scannerView = scannerView
}
func didFind(barcode: String, typeScanned: String) {
scannerView.scannedCode = barcode
scannerView.typeScanned = typeScanned
print (barcode)
}
func didSurface(error: CameraError) {
switch error {
case .invalidDeviceinput:
scannerView.alertItem = AlertContext.invalidDeviceInput
case .invalidScannedValue:
scannerView.alertItem = AlertContext.invalidScannedValue
case .invalidPreviewLayer:
scannerView.alertItem = AlertContext.invalidPreviewLayer
case .invalidStringObject:
scannerView.alertItem = AlertContext.invalidStringObject
}
}
}
}