rai212
rai212

Reputation: 781

Dismissing view using ObservableObject class provokes Publishing changes from within view updates is not allowed

When dismissing a fullScreenCover using a variable inside an ObservableObject (lines commented with 1.-) it shows the "Publishing changes from within view updates is not allowed, this will cause undefined behavior." message in the console, but using a @State variable (lines commented with 2.-) does not show the warning. I do not understand why.

Here is the code:

import SwiftUI

final class DismissWarningVM: ObservableObject {
    @Published var showAnotherView = false
}

struct DismissWarningView: View {
    @StateObject private var dismissWarningVM = DismissWarningVM()
    
    @State private var showAnotherView = false
    
    var body: some View {
        VStack {
            HStack {
                Spacer()
                Button {
                    // 1.- This line provokes the warning
                    dismissWarningVM.showAnotherView = true
                    // 2.- This line DO NOT provokes the warning
                    //showAnotherView = true
                } label: {
                    Text("Show")
                }
            }
            .padding(.trailing, 20)
            Spacer()
            Text("Main view")
            Spacer()
            
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(.white)
        // 1.- This line provokes the warning
        .fullScreenCover(isPresented: $dismissWarningVM.showAnotherView) {
        // 2.- This line DO NOT provokes the warning
        //.fullScreenCover(isPresented: $showAnotherView) {
            AnotherView()
        }
    }
}

struct AnotherView: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack(spacing: 30) {
            Text("Another view")
            Button {
                dismiss()
            } label: {
                Text("Dismiss")
                    .foregroundColor(.red)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .ignoresSafeArea()
    }
}

struct DismissWarningView_Previews: PreviewProvider {
    static var previews: some View {
        DismissWarningView()
    }
}

Upvotes: 2

Views: 108

Answers (2)

rai212
rai212

Reputation: 781

Fixed. It was a problem of the XCode version. I was trying with 14.0.1 version but after updating to 14.1 version the warning is no longer shown

Upvotes: 0

malhal
malhal

Reputation: 30746

@StateObject isn't designed for view model objects. In SwiftUI the View struct is the view model already you don't another one. Remember SwiftUI is diffing these structs and creating/updating/removing UIView objects automatically for us. If you use view model objects then you'll have viewModel object -> View struct -> UIView object which is a big mess and will lead to bugs. @StateObject is designed for when you need a reference type in an @State which isn't very often nowadays given we have .task and .task(id:) for asynchronous features.

You can achieve what you need like this:

struct WarningConfig {
    var isPresented = false

    // mutating func someLogic() {} 
}

struct SomeView: View {

    @State private var config = WarningConfig()

    ...

    .fullScreenCover(isPresented: $config.isPresented) {
        WarningView(config: $config)

Upvotes: -3

Related Questions