swiftuibindingobservedobjectmodal-view

My View is not getting Updated after Editing from Modal View SwiftUI


I have View called TransferFlowView() and I have an ObservedObject. struct TransferFlowView: View { @ObservedObject var transferListVM = TransferListViewModel() }

In TransferListViewModel(), I have a function to Update viewModel. class TransferListViewModel: ObservableObject { init() { fetch } } And I call that update function in Modal View. So Whenever I click submit and dismiss Modal View, The TransferFlowView() is not getting Updated.

I tried using binding property, used .onChange on TransferFlowView. It's not refreshing. I want the view to be loaded, like first time, when it loads.


Solution

  • ObservableObject relies on a synthesizes publisher objectWillChange to be triggered when there is a change, otherwise a view that observes this object through @ObservedObject wouldn't know when to refresh.

    There are two ways to trigger that:

    1. Manually by calling objectWillChange.send()
    struct TransferFlowView: View {
        @StateObject var transferListVM = TransferListViewModel()
        
        var body: some View {
            
            VStack {
                Text(transferListVM.update)
                
                Button(action: {
                    transferListVM.fetch()
                }) {
                    Text("Update")
                }
            }
        }
    }
    
    class TransferListViewModel: ObservableObject {
        var update = UUID().uuidString
        
        init() {
            fetch()
        }
        
        func fetch() {
            update = UUID().uuidString
            objectWillChange.send()
        }
    }
    

    Here if you comment out objectWillChange you will see that the text doesn't change on the view.

    1. Using @Published property.

    If you add @Published to the property, it will automatically trigger objectWillChange when you assign a new value. This code will have the same result:

    class TransferListViewModel: ObservableObject {
        @Published var update = UUID().uuidString
        
        init() {
            fetch()
        }
        
        func fetch() {
            update = UUID().uuidString
        }
    }
    

    You also need to pay attention where you initilize a new instance of TransferListViewModel. In your current code you use @ObservedObject and it will be initializing a new instance of the view model every time you refresh the view. For that you have to pass the already initialized instance from outside.

    Otherwise, you can use @StateObject which will call the initializer only once.