Home > front end >  How to control published property value update in viewmodel in SwiftUI?
How to control published property value update in viewmodel in SwiftUI?

Time:12-06

I have a view model class with multiple @Published properties.

class AddPassaround : ObservableObject {
    @Published var name: String = ""
    @Published var reversed : String = ""
    @Published var password: String = ""
    @Published var age: String = ""
    @Published var address: String = ""
    @Published var oneAnotherProperty: String = ""

    init() {
    }
}

Whenever any one of the @Published property is updated, I call an API. Now there is another scenario that needs to update multiple @Published properties at once programmatically. Something like this

viewModel.name = "test"
viewModel.password = "newPassword"
viewModel.oneAnotherProperty = "notUpdateAll"

Now the problem is the API is called multiple times and view is reloaded multiple times. How can I make the API to call only once in this case only. It should work normally in other cases.

CodePudding user response:

Create a Bool property to keep track of whether the API has already been called. You can set this property to true when the API is called, and check its value before calling the API again. If the property is true, you can simply return from the function without calling the API again.

Here is an example of how you could implement this:

class AddPassaround : ObservableObject {
    @Published var name: String = ""
    @Published var reversed : String = ""
    @Published var password: String = ""
    @Published var age: String = ""
    @Published var address: String = ""
    @Published var oneAnotherProperty: String = ""

    // Add a new property to keep track of whether the API has been called
    var calledAPI: Bool = false

    init() {
    }

    // Update the function that calls the API to check the value of calledAPI
    // before calling the API
    func updateAPI() {
        if calledAPI {
            return
        }

        // Call the API here

        // Set calledAPI to true to indicate that the API has been called
        calledAPI = true
    }
}

CodePudding user response:

The easy answer is to remove @Published and call objectWillChange.send() when you want to the View to update.

The long answer is to substitute @Published with your own implementation.

class AddPassaroundVM : ObservableObject {
    var name: String = ""{
        //Substitute for @Published
        didSet{
            if autoUpdate{
                updateNow()
            }
        }
    }
    //Keeps track of when to update
    private (set) var autoUpdate: Bool = true
    ///Toggles the ability to update with every change
    func toggleAutoUpdate(){
        autoUpdate = !autoUpdate
        updateNow()
    }
    ///Signals to update
    func updateNow(){
        objectWillChange.send()
    }

}
struct AddPassaroundView: View {
    @StateObject var vm: AddPassaroundVM = .init()
    var body: some View {
        VStack{
            TextField("name", text: $vm.name)
            Text(Array(arrayLiteral: vm.name).reversed().joined())
            Button("auto update \(vm.autoUpdate ? "on" : "off")", action: vm.toggleAutoUpdate)
            Button("update now", action: vm.updateNow)

        }.textFieldStyle(.roundedBorder)
    }
}
  • Related