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()