205 lines
6.3 KiB
Swift
205 lines
6.3 KiB
Swift
//
|
|
// 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) {
|
|
DispatchQueue.main.async {
|
|
self.timer?.invalidate()
|
|
self.timer = nil
|
|
self.timeLeft = duration
|
|
self.timer = Timer.scheduledTimer(timeInterval: 1,
|
|
target: self,
|
|
selector: #selector(self.updateCounter),
|
|
userInfo: nil,
|
|
repeats: true)
|
|
self.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) {
|
|
DispatchQueue.main.async {
|
|
self.currentExercise = exercise
|
|
|
|
if let duration = exercise.duration {
|
|
self.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))")
|
|
}
|
|
}
|
|
}
|