// // TimerModule.swift // Werkout_ios // // Created by Trey Tartt on 6/14/23. // import Foundation import WatchConnectivity enum WatchActions: Codable { case nextExercise case workoutComplete(Data) } class BridgeModule: NSObject, ObservableObject { private let kMessageKey = "message" 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 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 currentWorkoutRunTimeInSeconds = 0 currentWorkoutRunTimer?.invalidate() currentWorkoutRunTimer = nil currentExerciseIdx = 0 let exercise = workout.exercises[currentExerciseIdx] updateCurrent(exercise: exercise) startWorkoutTimer() workoutStartDate = Date() isInWorkout = true if WCSession.isSupported() { WCSession.default.delegate = self WCSession.default.activate() } } 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 let watchModel = WatchPackageModel(currentExerciseName: "", currentTimeLeft: -100, workoutStartDate: Date()) let data = try! JSONEncoder().encode(watchModel) send(data) } private func startWorkoutTimer() { currentWorkoutRunTimer?.invalidate() currentWorkoutRunTimer = nil currentWorkoutRunTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(addOneToWorkoutRunTime), userInfo: nil, repeats: true) currentWorkoutRunTimer?.fire() } private func startTimerWith(duration: Int) { timer?.invalidate() timer = nil timeLeft = duration timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true) timer?.fire() } @objc func updateCounter() { if timeLeft > 0 { timeLeft -= 1 let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date()) let data = try! JSONEncoder().encode(watchModel) send(data) } else { timer?.invalidate() timer = nil nextExercise() } } func nextExercise() { currentExerciseIdx += 1 if let currentWorkout = currentWorkout { if currentExerciseIdx < currentWorkout.exercises.count { let nextExercise = currentWorkout.exercises[currentExerciseIdx] updateCurrent(exercise: nextExercise) } else { } } } @objc func addOneToWorkoutRunTime() { currentWorkoutRunTimeInSeconds += 1 } func updateCurrent(exercise: ExerciseElement) { self.currentExercise = exercise if let duration = exercise.duration { startTimerWith(duration: duration) } } } 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?() } } } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {} #if os(iOS) func sessionDidBecomeInactive(_ session: WCSession) {} func sessionDidDeactivate(_ session: WCSession) { session.activate() } #endif func send(_ data: Data) { guard WCSession.default.activationState == .activated else { return } #if os(iOS) guard WCSession.default.isWatchAppInstalled else { return } #else guard WCSession.default.isCompanionAppInstalled else { return } #endif WCSession.default.sendMessageData(data, replyHandler: nil) { error in print("Cannot send message: \(String(describing: error))") } } }