0

I have inProgress boolean variable in my models, which keeps track of active API requests like this:

class BranchListViewModel: ObservableObject {

@Published var inProgress: Bool = false

func setActive(success: (() -> Void)? = nil, failure: ((RestError?) -> Void)? = nil) {
    inProgress = true
    putRequest(url: URL(string: K.Api.Url + "/branches/active_status")!, parameters: [:], type:
        success?()
        self.inProgress = false
    } failure: { error in
        failure?(error)
        self.inProgress = false
    }
}

In my view, I'm monitoring this variable with onChange(of:) method. If the variable is false (loading is done) reload data. This approach works fine.

struct BranchListView: View {

@ObservedObject var branchViewModel = BranchListViewModel()

var body: some View {
    VStack {
        Image("logo")
    }
    .onChange(of: branchViewModel.inProgress) { value in
        if value == false {
            branchViewModel.reloadData()
        }
    }
}

However the problem comes, when I want to update EnvironmentObject variable from onChange(of:) method:

struct BranchListView: View {

@EnvironmentObject var routerViewModel: RouterViewModel
@ObservedObject var branchViewModel = BranchListViewModel()

var body: some View {
    VStack {
        Image("logo")
    }
    .onChange(of: branchViewModel.inProgress) { value in
        if value == false {
            branchViewModel.reloadData()
        }
        routerViewModel.showLoadingIndicator = value    <<<------ problem here
    }
}

I want to update routerViewModel variable, which will be then used to show / hide loading indicator. However, when I do so, the .onChange(of:) method is called immediately again with opposite value.

So when I load branchViewModel.setActive() inProgress is set to true and .onChange(of:) is called with value = true (which is correct behaviour). However after assigning routerViewModel.showLoadingIndicater = value, the .onChange(of:) method is called again right away with value = false. Then, when API calls finishes and set inProgress = false, the .onChange(of:) method is not called, because the value was (for unknown reason) already set to false.

I tried put didSet observer to inProgress variable and it has correct behaviour - was set to true when API call started and false when finished.

So the whole process looks like this:

branchViewModel.setActive()
inProgress = true
inProgress didSet -> true
.onChange(of:) -> true
.onChange(of:) -> false        <<<--- problem here
API call started
API call processing
API call finished
inProgress = false
inProgress didSet -> false
filip.karas
  • 596
  • 2
  • 11
  • 27
  • 1
    I suggest you use `@Published var inProgress: Bool = false` **and** `@StateObject var branchViewModel = BranchListViewModel()` – workingdog support Ukraine Mar 28 '23 at 09:16
  • 1
    You are incorrectly initialising an `@ObservedObject` inside a View instead of injecting it. See [What is the difference between ObservedObject and StateObject](https://stackoverflow.com/questions/62544115/what-is-the-difference-between-observedobject-and-stateobject-in-swiftui/62555732#62555732). – Dávid Pásztor Mar 28 '23 at 09:47
  • inProgress is \@Published, I've updated the question (I made a mistake while copying here). Changing branchViewModel to \@StateObject actually helped! Now the .onChange() is called properly. Thank! One more thing though, when I set the routerViewModel.showLoadingIndicator value, the screen flashes. The value is doing nothing for now, it's just declared. When I comment out that line, the flashing is gone. Any thoughts? – filip.karas Mar 28 '23 at 09:53
  • Okay, figure it out. Another ObserverObject instead of StateObject. Thank you guys! Please post your suggestions as answers so I can rate them. – filip.karas Mar 28 '23 at 09:56
  • Try removing the view model object and using the View struct as designed – malhal Mar 28 '23 at 19:02

0 Answers0