Home > other >  SwiftUI - changes in nested View Model classes not detected using onChange method
SwiftUI - changes in nested View Model classes not detected using onChange method

Time:02-16

I have a nested View Model class WatchDayProgramViewModel as an ObservableObject. Within WatchDayProgramViewModel, there is a WorkoutModel that is a child class. I want to detect any updates in the currentHeartRate to trigger data transfer to iPhone.

Hence, I tried from ContentView using WatchDayProgramViewModel as an EnvironmentObject and detecting changes in WorkoutModel via onChange() method. But it seems that SwiftUI views does not detect any property changes in WorkoutModel.

I understand that this issue could be due to ObservableObject not detecting changes in child/nested level of classes, and SO answer (SwiftUI change on multilevel children Published object change) suggests using struct instead of class. But changing WorkoutModel to struct result in various @Published properties and functions to show error.

Is there any possible way to detect changes in child View Model from the ContentView itself?

ContentView

struct ContentView: View {
    @State var selectedTab = 0
    @StateObject var watchDayProgramVM = WatchDayProgramViewModel()
    
    var body: some View {
    
        NavigationView {
TabView(selection: $selectedTab) {
                    
                    WatchControlView().id(0)
                    NowPlayingView().id(1)
                }
                .environmentObject(watchDayProgramVM)
                .onChange(of: self.watchDayProgramVM.workoutModel.currentHeartRate) { newValue in
                                        print("WatchConnectivity heart rate from contentView \(newValue)")
   
                }
       }
}

WatchDayProgramViewModel

class WatchDayProgramViewModel: ObservableObject {
    
    @Published var workoutModel = WorkoutModel()
    
    init() {
    }
    
}

WorkoutModel

import Foundation
import HealthKit

class WorkoutModel: NSObject, ObservableObject {
    
    let healthStore = HKHealthStore()
    var session: HKWorkoutSession?
    var builder: HKLiveWorkoutBuilder?
    
    @Published var currentHeartRate: Double = 0
    @Published var workout: HKWorkout?

//Other functions to start/run workout hidden

func updateForStatistics(_ statistics: HKStatistics?) {
        
        guard let statistics = statistics else {
            return
        }
        
        DispatchQueue.main.async {
            switch statistics.quantityType {
            case HKQuantityType.quantityType(forIdentifier: .heartRate):
                
                let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
                
                self.currentHeartRate = statistics.mostRecentQuantity()?.doubleValue(for: heartRateUnit) ?? 0
                
            default:
                return
                
            }
            
        }//end of dispatchqueue
        
    }// end of function

}

extension WorkoutModel: HKLiveWorkoutBuilderDelegate {
    
    func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
        
        for type in collectedTypes {
            guard let quantityType = type as? HKQuantityType else {
                return
            }

            let statistics = workoutBuilder.statistics(for: quantityType)
            updateForStatistics(statistics)
            
        }
    }
}

CodePudding user response:

Figure it out. Just had to create another AnyCancellable variable to call objectWillChange publisher.

WatchDayProgramViewModel

class WatchDayProgramViewModel: ObservableObject {
    
    @Published var workoutModel = WorkoutModel()
    var cancellable: AnyCancellable?

    init() {
        cancellable = workoutModel.objectWillChange
            .sink { _ in
                self.objectWillChange.send()
            }
    }
    
}

CodePudding user response:

Try to change

@StateObject var watchDayProgramVM = WatchDayProgramViewModel()

with

@ObservedObject var watchDayProgramVM = WatchDayProgramViewModel()
  • Related