split files up, fix complete workout screen not showing if remove workout details screen
This commit is contained in:
@@ -23,6 +23,7 @@ enum PhoneToWatchActions: Codable {
|
||||
case inExercise(WatchPackageModel)
|
||||
case reset
|
||||
case endWorkout
|
||||
case startWorkout
|
||||
}
|
||||
|
||||
class BridgeModule: NSObject, ObservableObject {
|
||||
@@ -34,11 +35,11 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
@Published var isInWorkout = false
|
||||
var completedWorkout: (() -> Void)?
|
||||
@Published var currentWorkoutRunTimeInSeconds: Int = -1
|
||||
private var currentWorkoutRunTimer: Timer?
|
||||
var currentWorkoutRunTimer: Timer?
|
||||
|
||||
public private(set) var workoutStartDate: Date?
|
||||
public var workoutStartDate: Date?
|
||||
|
||||
private var currentExerciseTimer: Timer?
|
||||
public var currentExerciseTimer: Timer?
|
||||
|
||||
@Published public private(set) var currentExerciseInfo = CurrentWorkoutInfo()
|
||||
@Published var previewWorkout: Workout?
|
||||
@@ -48,346 +49,9 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
private var isWatchConnected = false
|
||||
// workoutEndDate fills out WatchPackageModel.workoutEndDate which
|
||||
// tells the watch app to stop the workout
|
||||
public private(set) var workoutEndDate: Date?
|
||||
@Published public private(set) var healthKitUUID: UUID?
|
||||
public var workoutEndDate: Date?
|
||||
@Published var healthKitUUID: UUID?
|
||||
@Published var isPaused = false
|
||||
|
||||
var audioPlayer: AVAudioPlayer?
|
||||
var avPlayer: AVPlayer?
|
||||
private let session: WCSession = WCSession.default
|
||||
|
||||
func start(workout: Workout) {
|
||||
currentExerciseInfo.complete = {
|
||||
self.completeWorkout()
|
||||
}
|
||||
|
||||
currentExerciseInfo.start(workout: workout)
|
||||
currentWorkoutRunTimeInSeconds = 0
|
||||
currentWorkoutRunTimer?.invalidate()
|
||||
currentWorkoutRunTimer = nil
|
||||
isPaused = false
|
||||
|
||||
if let superetExercise = currentExerciseInfo.currentExercise {
|
||||
updateCurrent(exercise: superetExercise)
|
||||
startWorkoutTimer()
|
||||
workoutStartDate = Date()
|
||||
isInWorkout = true
|
||||
|
||||
if WCSession.isSupported() {
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func goToExerciseAt(section: Int, row: Int) {
|
||||
if let superetExercise = currentExerciseInfo.goToWorkoutAt(supersetIndex: section,
|
||||
exerciseIndex: row) {
|
||||
updateCurrent(exercise: superetExercise)
|
||||
}
|
||||
}
|
||||
|
||||
var nextExerciseObject: SupersetExercise? {
|
||||
currentExerciseInfo.goToNextExercise
|
||||
}
|
||||
|
||||
func resetCurrentWorkout() {
|
||||
DispatchQueue.main.async {
|
||||
self.currentWorkoutRunTimeInSeconds = 0
|
||||
self.currentWorkoutRunTimer?.invalidate()
|
||||
self.currentWorkoutRunTimer = nil
|
||||
|
||||
self.currentExerciseTimer?.invalidate()
|
||||
self.currentExerciseTimer = nil
|
||||
|
||||
self.currentWorkoutRunTimeInSeconds = -1
|
||||
self.currentExerciseInfo.reset()
|
||||
|
||||
self.isInWorkout = false
|
||||
self.workoutStartDate = nil
|
||||
self.workoutEndDate = nil
|
||||
}
|
||||
}
|
||||
|
||||
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 startExerciseTimerWith(duration: Int) {
|
||||
DispatchQueue.main.async {
|
||||
self.currentExerciseTimer?.invalidate()
|
||||
self.currentExerciseTimer = nil
|
||||
self.currentExerciseTimeLeft = duration
|
||||
self.currentExerciseTimer = Timer.scheduledTimer(timeInterval: 1,
|
||||
target: self,
|
||||
selector: #selector(self.updateCurrentExerciseTimer),
|
||||
userInfo: nil,
|
||||
repeats: true)
|
||||
self.currentExerciseTimer?.fire()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateCurrentExerciseTimer() {
|
||||
if currentExerciseTimeLeft > 1 {
|
||||
currentExerciseTimeLeft -= 1
|
||||
|
||||
if let currentExercise = currentExerciseInfo.allSupersetExecercise, let audioQueues = currentExercise.audioQueues {
|
||||
if let audioQueue = audioQueues.first(where: {
|
||||
$0.playAt == currentExerciseTimeLeft
|
||||
}) {
|
||||
switch audioQueue.audioType {
|
||||
|
||||
case .shortBeep:
|
||||
playBeep()
|
||||
case .finishBeep:
|
||||
playFinished()
|
||||
case .remoteURL(let url):
|
||||
playRemoteAudio(fromURL: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
sendCurrentExerciseToWatch()
|
||||
} else {
|
||||
nextExercise()
|
||||
}
|
||||
}
|
||||
|
||||
func pauseWorkout() {
|
||||
if let _ = currentExerciseTimer {
|
||||
currentExerciseTimer?.invalidate()
|
||||
currentExerciseTimer = nil
|
||||
isPaused = true
|
||||
} else {
|
||||
isPaused = false
|
||||
startExerciseTimerWith(duration: currentExerciseTimeLeft)
|
||||
}
|
||||
}
|
||||
|
||||
func nextExercise() {
|
||||
if let nextSupersetExercise = currentExerciseInfo.goToNextExercise {
|
||||
updateCurrent(exercise: nextSupersetExercise)
|
||||
} else {
|
||||
completeWorkout()
|
||||
}
|
||||
}
|
||||
|
||||
func previousExercise() {
|
||||
if let nextSupersetExercise = currentExerciseInfo.previousExercise {
|
||||
updateCurrent(exercise: nextSupersetExercise)
|
||||
} else {
|
||||
completeWorkout()
|
||||
}
|
||||
}
|
||||
|
||||
func restartExercise() {
|
||||
if let currentExercise = currentExerciseInfo.currentExercise {
|
||||
updateCurrent(exercise: currentExercise)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func addOneToWorkoutRunTime() {
|
||||
currentWorkoutRunTimeInSeconds += 1
|
||||
}
|
||||
|
||||
func updateCurrent(exercise: SupersetExercise) {
|
||||
DispatchQueue.main.async {
|
||||
self.currentExerciseTimer?.invalidate()
|
||||
self.currentExerciseTimer = nil
|
||||
|
||||
if let duration = exercise.duration, duration > 0 {
|
||||
self.startExerciseTimerWith(duration: duration)
|
||||
}
|
||||
self.sendCurrentExerciseToWatch()
|
||||
}
|
||||
}
|
||||
|
||||
func completeWorkout() {
|
||||
self.currentExerciseTimer?.invalidate()
|
||||
self.currentExerciseTimer = nil
|
||||
self.isInWorkout = false
|
||||
|
||||
workoutEndDate = Date()
|
||||
if let completedWorkout = completedWorkout {
|
||||
completedWorkout()
|
||||
self.completedWorkout = nil
|
||||
}
|
||||
}
|
||||
|
||||
func playRemoteAudio(fromURL url: URL) {
|
||||
#if os(iOS)
|
||||
let playerItem = AVPlayerItem(url: url)
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playback,
|
||||
mode: .default,
|
||||
options: [.mixWithOthers])
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
|
||||
avPlayer = AVPlayer(playerItem: playerItem)
|
||||
avPlayer?.play()
|
||||
} catch {
|
||||
print("ERROR")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func playBeep() {
|
||||
#if os(iOS)
|
||||
if let path = Bundle.main.path(forResource: "short_beep", ofType: "m4a") {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playback,
|
||||
mode: .default,
|
||||
options: [.mixWithOthers])
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
|
||||
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
|
||||
audioPlayer?.play()
|
||||
} catch {
|
||||
print("ERROR")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func playFinished() {
|
||||
#if os(iOS)
|
||||
if let path = Bundle.main.path(forResource: "long_beep", ofType: "m4a") {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playback,
|
||||
mode: .default,
|
||||
options: [.mixWithOthers])
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
|
||||
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
|
||||
audioPlayer?.play()
|
||||
} catch {
|
||||
print("ERROR")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension BridgeModule: WCSessionDelegate {
|
||||
func sendResetToWatch() {
|
||||
let watchModel = PhoneToWatchActions.reset
|
||||
let data = try! JSONEncoder().encode(watchModel)
|
||||
|
||||
// user transferUserInfo b/c its guranteed to reach
|
||||
// and end the workout
|
||||
self.session.transferUserInfo(["package": data])
|
||||
}
|
||||
|
||||
func sendWorkoutCompleteToWatch() {
|
||||
let model = PhoneToWatchActions.endWorkout
|
||||
let data = try! JSONEncoder().encode(model)
|
||||
|
||||
// user transferUserInfo b/c its guranteed to reach
|
||||
// and end the workout
|
||||
self.session.transferUserInfo(["package": data])
|
||||
}
|
||||
|
||||
func sendCurrentExerciseToWatch() {
|
||||
if let currentExercise = currentExerciseInfo.currentExercise,
|
||||
let duration = currentExercise.duration ,
|
||||
duration > 0 {
|
||||
let watchModel = WatchPackageModel(currentExerciseName: currentExercise.exercise.name, currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date())
|
||||
let model = PhoneToWatchActions.inExercise(watchModel)
|
||||
let data = try! JSONEncoder().encode(model)
|
||||
send(data)
|
||||
} else {
|
||||
if let currentExercise = currentExerciseInfo.currentExercise,
|
||||
let reps = currentExercise.reps,
|
||||
reps > 0 {
|
||||
|
||||
// if not a timer we need to set the watch display with number of reps
|
||||
// if timer it will set when timer updates
|
||||
let watchModel = WatchPackageModel(currentExerciseName: currentExercise.exercise.name, currentTimeLeft: reps, workoutStartDate: self.workoutStartDate ?? Date())
|
||||
let model = PhoneToWatchActions.inExercise(watchModel)
|
||||
let data = try! JSONEncoder().encode(model)
|
||||
self.send(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
|
||||
if let model = try? JSONDecoder().decode(WatchActions.self, from: messageData) {
|
||||
switch model {
|
||||
case .nextExercise:
|
||||
nextExercise()
|
||||
playFinished()
|
||||
case .workoutComplete(let data):
|
||||
DispatchQueue.main.async {
|
||||
let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data)
|
||||
self.healthKitUUID = model.healthKitUUID
|
||||
}
|
||||
case .restartExercise:
|
||||
restartExercise()
|
||||
case .previousExercise:
|
||||
previousExercise()
|
||||
case .stopWorkout:
|
||||
completeWorkout()
|
||||
case .pauseWorkout:
|
||||
pauseWorkout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func session(_ session: WCSession,
|
||||
activationDidCompleteWith activationState: WCSessionActivationState,
|
||||
error: Error?) {
|
||||
switch activationState {
|
||||
case .notActivated:
|
||||
print("notActivated")
|
||||
case .inactive:
|
||||
print("inactive")
|
||||
case .activated:
|
||||
print("activated")
|
||||
#if os(iOS)
|
||||
let workoutConfiguration = HKWorkoutConfiguration()
|
||||
workoutConfiguration.activityType = .functionalStrengthTraining
|
||||
workoutConfiguration.locationType = .indoor
|
||||
if WCSession.isSupported(), session.activationState == .activated, session.isWatchAppInstalled {
|
||||
HKHealthStore().startWatchApp(with: workoutConfiguration, completion: { (success, error) in
|
||||
print(error.debugDescription)
|
||||
})
|
||||
}
|
||||
#endif
|
||||
@unknown default:
|
||||
print("default")
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
func sessionDidBecomeInactive(_ session: WCSession) {
|
||||
|
||||
}
|
||||
|
||||
func sessionDidDeactivate(_ session: WCSession) {
|
||||
session.activate()
|
||||
}
|
||||
#endif
|
||||
func send(_ data: Data) {
|
||||
guard session.activationState == .activated else {
|
||||
return
|
||||
}
|
||||
#if os(iOS)
|
||||
guard session.isWatchAppInstalled else {
|
||||
return
|
||||
}
|
||||
#else
|
||||
guard session.isCompanionAppInstalled else {
|
||||
return
|
||||
}
|
||||
#endif
|
||||
session.sendMessageData(data, replyHandler: nil) { error in
|
||||
print("Cannot send message: \(String(describing: error))")
|
||||
}
|
||||
}
|
||||
let session: WCSession = WCSession.default
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user