diff --git a/Werkout_ios/BridgeModule.swift b/Werkout_ios/BridgeModule.swift index aee132f..770d82d 100644 --- a/Werkout_ios/BridgeModule.swift +++ b/Werkout_ios/BridgeModule.swift @@ -8,8 +8,9 @@ import Foundation import WatchConnectivity -enum WatchActions: String, Codable { +enum WatchActions: Codable { case nextExercise + case workoutComplete(Data) } class BridgeModule: NSObject, ObservableObject { @@ -26,11 +27,17 @@ class BridgeModule: NSObject, ObservableObject { var currentExerciseIdx: Int = -1 var workoutStartDate: Date? + + // workoutEndDate ties into WatchPackageModel.workoutEndDate which + // tells the watch app to stop the workout var workoutEndDate: Date? @Published var currentWorkoutRunTimeInSeconds: Int = -1 private var currentWorkoutRunTimer: Timer? @Published var isInWorkout = false + var completedWorkoutFromWatch: (() -> Void)? + var totalCaloire: Float? + var heartRates: [Int]? func start(workout: Workout, atExerciseIndex: Int = 0) { self.currentWorkout = workout @@ -64,15 +71,14 @@ class BridgeModule: NSObject, ObservableObject { currentExercise = nil currentWorkout = nil - - if isInWorkout { - let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date()) - let data = try! JSONEncoder().encode(watchModel) - send(data) - } + isInWorkout = false workoutStartDate = nil workoutEndDate = nil + + let watchModel = WatchPackageModel(currentExerciseName: "", currentTimeLeft: -100, workoutStartDate: Date()) + let data = try! JSONEncoder().encode(watchModel) + send(data) } private func startWorkoutTimer() { @@ -140,11 +146,22 @@ class BridgeModule: NSObject, ObservableObject { } extension BridgeModule: WCSessionDelegate { + func sendWorkoutCompleteToWatch() { + let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date()) + let data = try! JSONEncoder().encode(watchModel) + send(data) + } + func session(_ session: WCSession, didReceiveMessageData messageData: Data) { if let model = try? JSONDecoder().decode(WatchActions.self, from: messageData) { switch model { case .nextExercise: nextExercise() + case .workoutComplete(let data): + let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data) + totalCaloire = Float(model.totalBurnedEnergery) + heartRates = model.allHeartRates + completedWorkoutFromWatch?() } } } diff --git a/Werkout_ios/ToDo b/Werkout_ios/ToDo index 4b46a13..1a4a15b 100644 --- a/Werkout_ios/ToDo +++ b/Werkout_ios/ToDo @@ -1,9 +1,12 @@ -completed workout view - add notes and slider for difficulty +-completed workout view +- add notes and slider for difficulty + +-apple watch + account view workout history view -apple watch calorie upload -edit weights on workouts + video view on iphone during workout +edit weights on workouts diff --git a/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift b/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift index d11e964..a2949b6 100644 --- a/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift +++ b/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift @@ -19,37 +19,18 @@ struct CompletedWorkoutView: View { var body: some View { VStack { - Text(workout.name) - .frame(maxWidth: .infinity, alignment: .leading) - .font(.title3) - .padding(.top - ) - if let desc = workout.description { - Text(desc) - .frame(maxWidth: .infinity, alignment: .leading) - .font(.body) - .padding(.top) - - } + topViews() - Divider() + calsBurned() - Text("how hard was this shit") - - HStack { - Text("easy") - Spacer() - Text("Death") - } - - Slider(value: $difficulty, in: 0...5, step: 1) + heartRates() + rateWorkout() + Divider() TextField("Notes", text: $notes) - -// Divider() - + Spacer() Button("Upload", action: { @@ -66,6 +47,57 @@ struct CompletedWorkoutView: View { .padding([.leading, .trailing]) } + func topViews() -> some View { + VStack { + Text(workout.name) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.title3) + .padding(.top + ) + if let desc = workout.description { + Text(desc) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.body) + .padding(.top) + + } + } + } + + func calsBurned() -> some View { + VStack { + Divider() + Text("calroies burned") + Text("\(postData["total_calories"] as! Float)") + } + } + + func rateWorkout() -> some View { + VStack { + Divider() + + Text("how hard was this shit") + + HStack { + Text("easy") + Spacer() + Text("Death") + } + + Slider(value: $difficulty, in: 0...5, step: 1) + } + } + + func heartRates() -> some View { + VStack { + Divider() + if let heartRates = postData["heart_rates"] as? [Int] { + let avg = heartRates.reduce(0, +)/heartRates.count + Text("Avg heart rate: \(avg)") + } + } + } + func upload(postBody: [String: Any]) { var _postBody = postBody _postBody["difficulty"] = difficulty diff --git a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift index a04b8ea..1e46b42 100644 --- a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift +++ b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift @@ -34,10 +34,7 @@ struct WorkoutDetailView: View { ExerciseListView(workout: workout) ActionsView(completedWorkout: { bridgeModule.workoutEndDate = Date() - if let workoutData = createWorkoutData() { - presentedSheet = .completedWorkout(workoutData) - bridgeModule.resetCurrentWorkout() - } + bridgeModule.sendWorkoutCompleteToWatch() }, workout: workout) .frame(height: 44) @@ -55,6 +52,14 @@ struct WorkoutDetailView: View { .interactiveDismissDisabled() } } + .onAppear{ + bridgeModule.completedWorkoutFromWatch = { + if let workoutData = createWorkoutData() { + presentedSheet = .completedWorkout(workoutData) + bridgeModule.resetCurrentWorkout() + } + } + } } func createWorkoutData() -> [String:Any]? { @@ -68,7 +73,9 @@ struct WorkoutDetailView: View { "workout_start_time": startTime, "workout_end_time": endTime, "workout": workoutid, - "total_time": bridgeModule.currentWorkoutRunTimeInSeconds + "total_time": bridgeModule.currentWorkoutRunTimeInSeconds, + "total_calories": bridgeModule.totalCaloire ?? -1, + "heart_rates": bridgeModule.heartRates ?? [Int]() ] as [String : Any] return postBody diff --git a/Werkout_ios/WatchPackageModel.swift b/Werkout_ios/WatchPackageModel.swift index 2b55273..539428d 100644 --- a/Werkout_ios/WatchPackageModel.swift +++ b/Werkout_ios/WatchPackageModel.swift @@ -13,3 +13,8 @@ struct WatchPackageModel: Codable { var workoutStartDate: Date var workoutEndDate: Date? } + +struct WatchFinishWorkoutModel: Codable { + var totalBurnedEnergery: Double + var allHeartRates: [Int] +} diff --git a/Werkout_watch Watch App/WatchMainViewModel.swift b/Werkout_watch Watch App/WatchMainViewModel.swift index bb85951..07d82ae 100644 --- a/Werkout_watch Watch App/WatchMainViewModel.swift +++ b/Werkout_watch Watch App/WatchMainViewModel.swift @@ -16,12 +16,9 @@ class WatchMainViewModel: NSObject, ObservableObject { @Published var heartValue: Int? let healthStore = HKHealthStore() - var hkWorkoutSession: HKWorkoutSession? { - didSet { - print("here") - } - } + var hkWorkoutSession: HKWorkoutSession? var hkBuilder: HKLiveWorkoutBuilder? + var heartRates = [Int]() override init() { session = WCSession.default @@ -71,6 +68,10 @@ extension WatchMainViewModel: WCSessionDelegate { func session(_ session: WCSession, didReceiveMessageData messageData: Data) { if let model = try? JSONDecoder().decode(WatchPackageModel.self, from: messageData) { DispatchQueue.main.async { + if model.currentTimeLeft == -100 { + self.watchPackageModel = nil + return + } if self.watchPackageModel?.workoutEndDate != nil { self.watchPackageModel = nil self.stopWorkout() @@ -157,6 +158,14 @@ extension WatchMainViewModel: HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDele DispatchQueue.main.async() { self.hkWorkoutSession = nil self.hkBuilder = nil + let totalEnergy = workout?.totalEnergyBurned?.doubleValue(for: .kilocalorie()) ?? -1 + let watchFinishWorkoutModel = WatchFinishWorkoutModel(totalBurnedEnergery: totalEnergy, allHeartRates: self.heartRates) + let data = try! JSONEncoder().encode(watchFinishWorkoutModel) + let watchAction = WatchActions.workoutComplete(data) + let watchActionData = try! JSONEncoder().encode(watchAction) + self.send(watchActionData) + + self.heartRates.removeAll() } } } @@ -182,6 +191,7 @@ extension WatchMainViewModel: HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDele let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute()) let value = statistics!.mostRecentQuantity()?.doubleValue(for: heartRateUnit) self.heartValue = Int(Double(round(1 * value!) / 1)) + self.heartRates.append(Int(Double(round(1 * value!) / 1))) print("[workoutBuilder] Heart Rate: \(String(describing: self.heartValue))") } default: