diff --git a/Werkout_ios/APIModels/CompletedWorkout.swift b/Werkout_ios/APIModels/CompletedWorkout.swift index 13a1223..d4f309d 100644 --- a/Werkout_ios/APIModels/CompletedWorkout.swift +++ b/Werkout_ios/APIModels/CompletedWorkout.swift @@ -27,3 +27,24 @@ struct CompletedWorkout: Codable { case totalCalories = "total_calories" } } + +struct CompletedWorkoutPOSTReturn: Codable { + let id: Int + let workout: Int + let createdAt, updatedAt: String + let difficulty, totalTime: Int? + let workoutStartTime: String + let notes: String? + let totalCalories: Int? + + enum CodingKeys: String, CodingKey { + case id, workout + case createdAt = "created_at" + case updatedAt = "updated_at" + case difficulty + case totalTime = "total_time" + case workoutStartTime = "workout_start_time" + case notes + case totalCalories = "total_calories" + } +} diff --git a/Werkout_ios/BridgeModule.swift b/Werkout_ios/BridgeModule.swift index 1db7a86..d8dea64 100644 --- a/Werkout_ios/BridgeModule.swift +++ b/Werkout_ios/BridgeModule.swift @@ -18,28 +18,28 @@ class BridgeModule: NSObject, ObservableObject { static let shared = BridgeModule() @Published var isShowingOnExternalDisplay = false - - private var timer: Timer? - @Published var timeLeft: Int = 0 - - @Published var currentExercise: ExerciseElement? - var currentWorkout: Workout? - 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 isInWorkout = false + var completedWorkout: (() -> Void)? @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) { + var currentWorkout: Workout? + public private(set) var workoutStartDate: Date? + + private var currentExerciseTimer: Timer? + public private(set) var currentExerciseIdx: Int = -1 + @Published var currentExerciseTimeLeft: Int = 0 + @Published var currentExercise: ExerciseElement? + + private var isWatchConnected = false + // workoutEndDate fills out WatchPackageModel.workoutEndDate which + // tells the watch app to stop the workout + public private(set) var workoutEndDate: Date? + public private(set) var totalCaloire: Float? + public private(set) var heartRates: [Int]? + + func start(workout: Workout) { self.currentWorkout = workout currentWorkoutRunTimeInSeconds = 0 currentWorkoutRunTimer?.invalidate() @@ -58,23 +58,35 @@ class BridgeModule: NSObject, ObservableObject { } } + func goToExerciseAt(index: Int) { + guard let currentWorkout = currentWorkout else { + return + } + + currentExerciseIdx = index + let exercise = currentWorkout.exercises[index] + updateCurrent(exercise: exercise) + } + func resetCurrentWorkout() { - currentWorkoutRunTimeInSeconds = 0 - currentWorkoutRunTimer?.invalidate() - currentWorkoutRunTimer = nil - - currentWorkoutRunTimer?.invalidate() - currentWorkoutRunTimer = nil - - currentWorkoutRunTimeInSeconds = -1 - currentExerciseIdx = -1 - - currentExercise = nil - currentWorkout = nil - - isInWorkout = false - workoutStartDate = nil - workoutEndDate = nil + DispatchQueue.main.async { + self.currentWorkoutRunTimeInSeconds = 0 + self.currentWorkoutRunTimer?.invalidate() + self.currentWorkoutRunTimer = nil + + self.currentExerciseTimer?.invalidate() + self.currentExerciseTimer = nil + + self.currentWorkoutRunTimeInSeconds = -1 + self.currentExerciseIdx = -1 + + self.currentExercise = nil + self.currentWorkout = nil + + self.isInWorkout = false + self.workoutStartDate = nil + self.workoutEndDate = nil + } let watchModel = WatchPackageModel(currentExerciseName: "", currentTimeLeft: -100, workoutStartDate: Date()) let data = try! JSONEncoder().encode(watchModel) @@ -94,30 +106,26 @@ class BridgeModule: NSObject, ObservableObject { private func startTimerWith(duration: Int) { DispatchQueue.main.async { - self.timer?.invalidate() - self.timer = nil - self.timeLeft = duration - self.timer = Timer.scheduledTimer(timeInterval: 1, + self.currentExerciseTimer?.invalidate() + self.currentExerciseTimer = nil + self.currentExerciseTimeLeft = duration + self.currentExerciseTimer = Timer.scheduledTimer(timeInterval: 1, target: self, - selector: #selector(self.updateCounter), + selector: #selector(self.updateCurrentExerciseTimer), userInfo: nil, repeats: true) - self.timer?.fire() + self.currentExerciseTimer?.fire() } } - @objc func updateCounter() { - if timeLeft > 0 { - timeLeft -= 1 + @objc func updateCurrentExerciseTimer() { + if currentExerciseTimeLeft > 0 { + currentExerciseTimeLeft -= 1 - let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date()) + let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date()) let data = try! JSONEncoder().encode(watchModel) send(data) - } else { - timer?.invalidate() - timer = nil - nextExercise() } } @@ -129,7 +137,7 @@ class BridgeModule: NSObject, ObservableObject { let nextExercise = currentWorkout.exercises[currentExerciseIdx] updateCurrent(exercise: nextExercise) } else { - + completeWorkout() } } } @@ -147,11 +155,22 @@ class BridgeModule: NSObject, ObservableObject { } } } + + func completeWorkout() { + workoutEndDate = Date() + + //if connected to watch + if WCSession.default.isReachable { + self.sendWorkoutCompleteToWatch() + } else { + completedWorkout?() + } + } } extension BridgeModule: WCSessionDelegate { func sendWorkoutCompleteToWatch() { - let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date()) + let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date()) let data = try! JSONEncoder().encode(watchModel) send(data) } @@ -165,7 +184,7 @@ extension BridgeModule: WCSessionDelegate { let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data) totalCaloire = Float(model.totalBurnedEnergery) heartRates = model.allHeartRates - completedWorkoutFromWatch?() + completedWorkout?() } } } @@ -173,16 +192,25 @@ extension BridgeModule: WCSessionDelegate { func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, - error: Error?) {} - + error: Error?) { + switch activationState { + case .notActivated: + print("notActivated") + case .inactive: + print("inactive") + case .activated: + print("activated") + } + } #if os(iOS) - func sessionDidBecomeInactive(_ session: WCSession) {} + func sessionDidBecomeInactive(_ session: WCSession) { + + } + func sessionDidDeactivate(_ session: WCSession) { session.activate() } #endif - - func send(_ data: Data) { guard WCSession.default.activationState == .activated else { return @@ -196,8 +224,7 @@ extension BridgeModule: WCSessionDelegate { return } #endif - WCSession.default.sendMessageData(data, replyHandler: nil) - { error in + WCSession.default.sendMessageData(data, replyHandler: nil) { error in print("Cannot send message: \(String(describing: error))") } } diff --git a/Werkout_ios/Network/Fetchables.swift b/Werkout_ios/Network/Fetchables.swift index 816c3a0..a94a092 100644 --- a/Werkout_ios/Network/Fetchables.swift +++ b/Werkout_ios/Network/Fetchables.swift @@ -62,7 +62,7 @@ class CompleteWorkoutFetchable: Postable { var postableData: [String : Any]? var successStatus = 201 - typealias Response = CompletedWorkout + typealias Response = CompletedWorkoutPOSTReturn var endPoint: String = "/workout/complete/" init(postData: [String: Any]) { diff --git a/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift b/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift index 8949acd..4256c3c 100644 --- a/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift +++ b/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift @@ -121,7 +121,8 @@ struct CompletedWorkoutView: View { func heartRates() -> some View { VStack { - if let heartRates = postData["heart_rates"] as? [Int] { + if let heartRates = postData["heart_rates"] as? [Int], + heartRates.count > 0 { let avg = heartRates.reduce(0, +)/heartRates.count HStack { Image(systemName: "heart") diff --git a/Werkout_ios/Views/ExternalWorkoutDetailView.swift b/Werkout_ios/Views/ExternalWorkoutDetailView.swift index 6629e61..16493d6 100644 --- a/Werkout_ios/Views/ExternalWorkoutDetailView.swift +++ b/Werkout_ios/Views/ExternalWorkoutDetailView.swift @@ -113,9 +113,9 @@ struct ExtCountdownView: View { } HStack { if let duration = currenExercise.duration { - ProgressView(value: Float(bridgeModule.timeLeft), total: Float(duration)) + ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration)) .scaleEffect(x: 1, y: 6, anchor: .center) - Text("\(bridgeModule.timeLeft)") + Text("\(bridgeModule.currentExerciseTimeLeft)") .font(Font.system(size: 75)) .padding([.leading, .trailing]) } else if let reps = currenExercise.reps { diff --git a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift index 881e489..d601641 100644 --- a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift +++ b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift @@ -35,8 +35,7 @@ struct WorkoutDetailView: View { CountdownView() ExerciseListView(workout: workout) ActionsView(completedWorkout: { - bridgeModule.workoutEndDate = Date() - bridgeModule.sendWorkoutCompleteToWatch() + bridgeModule.completeWorkout() }, planWorkout: { workout in workoutToPlan = workout }, workout: workout, showAddToCalendar: showAddToCalendar) @@ -62,7 +61,7 @@ struct WorkoutDetailView: View { } } .onAppear{ - bridgeModule.completedWorkoutFromWatch = { + bridgeModule.completedWorkout = { if let workoutData = createWorkoutData() { presentedSheet = .completedWorkout(workoutData) bridgeModule.resetCurrentWorkout() @@ -223,7 +222,7 @@ struct ExerciseListView: View { Text(obj.exercise.name) .onTapGesture { - bridgeModule.start(workout: workout, atExerciseIndex: i) + bridgeModule.goToExerciseAt(index: i) } Spacer() @@ -257,8 +256,8 @@ struct CountdownView: View { VStack { if let duration = bridgeModule.currentExercise?.duration { HStack { - ProgressView(value: Float(bridgeModule.timeLeft), total: Float(duration)) - Text("\(bridgeModule.timeLeft)") + ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration)) + Text("\(bridgeModule.currentExerciseTimeLeft)") }.padding(16) } }