skip to Main Content

I’m trying to trigger an alert when is an error in the model but it never get updated to show the alert:

Here is my implementation in the view:

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel
    @State var showAlert = false
    init() {
        viewModel = ViewModel()
        showAlert = viewModel.showAlert
    }
    var body: some View {
        NavigationView {
            Text("Hello, world!")
                .padding()
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("This works"),
                  message: Text("Hello"),
                  dismissButton: .default(Text("got it"))
        )}
    }
}

Here is my models:

class ViewModel: ObservableObject {
    @Published var showAlert = false
    var cancellables = Set<AnyCancellable>()
    
    init() {
        DoSomething.shared.showAlert.sink { _ in
            print("got new Value")
        } receiveValue: {[weak self] value in
            print("value")
            self?.showAlert = value
        }.store(in: &cancellables)
    }
}
class DoSomething {
    let showAlert = PassthroughSubject<Bool, Never>()
    static let shared = DoSomething()
    private init() {
        checkToShowAlert()
    }
    func checkToShowAlert() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
            print("change value")
            self?.showAlert.send(true)
        }
    }
}

Any of you knows why the showAlert variable it never gets updated?

I’ll really appreciate your help

2

Answers


  1. In your current code, you’re setting ContentView‘s showAlert to the ViewModel‘s showAlert at that point in time:

    init() {
      viewModel = ViewModel()
      showAlert = viewModel.showAlert //<-- assignment at the time of init
    }
    

    Meaning, it’s false at the time of assignment. Because it’s just a Bool getting assigned to another Bool, there’s no mechanism to keep it updated if ViewModel‘s showAlert changes.

    The simplest solution is to get rid of your @State variable and observe the @Published property directly:

    struct ContentView: View {
        @ObservedObject var viewModel: ViewModel = ViewModel()
    
        var body: some View {
            NavigationView {
                Text("Hello, world!")
                    .padding()
            }
            .alert(isPresented: $viewModel.showAlert) {
                Alert(title: Text("This works"),
                      message: Text("Hello"),
                      dismissButton: .default(Text("got it"))
            )}
        }
    }
    
    Login or Signup to reply.
  2. Best remove the view model object, we don’t need those in SwiftUI because the View struct holds the view data and the @State and @Binding property wrappers make the struct behave like an object.

    Also, I don’t think you need Combine for what you are trying to do because you aren’t combining anything using combineLatest etc, but when we do use it in SwiftUI we don’t use sink or store, instead we assign the end of the pipeline to an @Published.

    Example:

    struct ContentView: View {
        @State var isPresented = false
    
        var body: some View {
            NavigationView {
                VStack{
                    Text("Hello, world!")
                      .padding()
                    Button("Show Alert") {
                        showAlert()
                     }
                }
            }
            .alert(isPresented: $isPresented) {
                Alert(title:Text("This works"),
                      message: Text("Hello"),
                      dismissButton: .default(Text("got it"))
            )}
        }
        
        func showAlert() {
            DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
                isPresented = true
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search