From f1781744cabfa024781f0b7e073190f00307c914 Mon Sep 17 00:00:00 2001 From: Trey t Date: Mon, 1 Jul 2024 21:22:12 -0500 Subject: [PATCH] try to fix watch stuff --- iphone/Werkout-ios-Info.plist | 2 + iphone/Werkout_ios.xcodeproj/project.pbxproj | 12 +- iphone/Werkout_ios/BridgeModule+Timer.swift | 2 +- .../BridgeModule+WorkoutActions.swift | 4 +- .../Views/ExternalWorkoutDetailView.swift | 29 +++++ .../WorkoutDetail/WorkoutDetailView.swift | 3 +- .../subview/ExtCountdownView.swift | 10 -- .../MainWatchView.swift | 59 +++++---- .../WatchControlView.swift | 3 +- .../WatchDelegate.swift | 41 +++++++ ...WatchMainViewModel+WCSessionDelegate.swift | 6 +- .../WatchMainViewModel.swift | 56 ++------- ...orkoutActions.swift => WatchWorkout.swift} | 115 ++++++++---------- .../Werkout_watchApp.swift | 2 + 14 files changed, 187 insertions(+), 157 deletions(-) create mode 100644 iphone/Werkout_watch Watch App/WatchDelegate.swift rename iphone/Werkout_watch Watch App/{WatchMainViewModel+WorkoutActions.swift => WatchWorkout.swift} (51%) diff --git a/iphone/Werkout-ios-Info.plist b/iphone/Werkout-ios-Info.plist index f6cad2b..b2d450c 100644 --- a/iphone/Werkout-ios-Info.plist +++ b/iphone/Werkout-ios-Info.plist @@ -2,6 +2,8 @@ + ITSAppUsesNonExemptEncryption + NSAppTransportSecurity NSAllowsArbitraryLoads diff --git a/iphone/Werkout_ios.xcodeproj/project.pbxproj b/iphone/Werkout_ios.xcodeproj/project.pbxproj index 6266120..3a0e6bf 100644 --- a/iphone/Werkout_ios.xcodeproj/project.pbxproj +++ b/iphone/Werkout_ios.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 1C0494812C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494802C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift */; }; 1C0494832C23C56E003D18BB /* WatchMainViewModel+WCSessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494822C23C56E003D18BB /* WatchMainViewModel+WCSessionDelegate.swift */; }; 1C0494872C23E7BD003D18BB /* BridgeModule+Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494862C23E7BD003D18BB /* BridgeModule+Watch.swift */; }; 1C0494882C23E7C5003D18BB /* BridgeModule+Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494862C23E7BD003D18BB /* BridgeModule+Watch.swift */; }; @@ -17,6 +16,8 @@ 1C04948E2C25CD3D003D18BB /* BridgeModule+WorkoutActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494892C25CB4F003D18BB /* BridgeModule+WorkoutActions.swift */; }; 1C0494932C25CEF0003D18BB /* BridgeModule+Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494922C25CEF0003D18BB /* BridgeModule+Timer.swift */; }; 1C0494942C25CEF4003D18BB /* BridgeModule+Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494922C25CEF0003D18BB /* BridgeModule+Timer.swift */; }; + 1C1A3C722C3373E10010CDD5 /* WatchDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C1A3C712C3373E10010CDD5 /* WatchDelegate.swift */; }; + 1C1A3C742C3376150010CDD5 /* WatchWorkout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C1A3C732C3376150010CDD5 /* WatchWorkout.swift */; }; 1C31C8842A53AE3E00350540 /* short_beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 1C31C8822A53AE3E00350540 /* short_beep.m4a */; }; 1C31C8852A53AE3E00350540 /* long_beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 1C31C8832A53AE3E00350540 /* long_beep.m4a */; }; 1C31C8872A55B2CC00350540 /* PlayerUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C31C8862A55B2CC00350540 /* PlayerUIView.swift */; }; @@ -154,12 +155,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 1C0494802C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WatchMainViewModel+WorkoutActions.swift"; sourceTree = ""; }; 1C0494822C23C56E003D18BB /* WatchMainViewModel+WCSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WatchMainViewModel+WCSessionDelegate.swift"; sourceTree = ""; }; 1C0494862C23E7BD003D18BB /* BridgeModule+Watch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BridgeModule+Watch.swift"; sourceTree = ""; }; 1C0494892C25CB4F003D18BB /* BridgeModule+WorkoutActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BridgeModule+WorkoutActions.swift"; sourceTree = ""; }; 1C04948B2C25CB80003D18BB /* AudioEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioEngine.swift; sourceTree = ""; }; 1C0494922C25CEF0003D18BB /* BridgeModule+Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BridgeModule+Timer.swift"; sourceTree = ""; }; + 1C1A3C712C3373E10010CDD5 /* WatchDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchDelegate.swift; sourceTree = ""; }; + 1C1A3C732C3376150010CDD5 /* WatchWorkout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchWorkout.swift; sourceTree = ""; }; 1C31C8822A53AE3E00350540 /* short_beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = short_beep.m4a; sourceTree = ""; }; 1C31C8832A53AE3E00350540 /* long_beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = long_beep.m4a; sourceTree = ""; }; 1C31C8862A55B2CC00350540 /* PlayerUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerUIView.swift; sourceTree = ""; }; @@ -511,11 +513,12 @@ children = ( 1CF65AB72A4534DC0042FFBD /* Werkout_watch Watch App.entitlements */, 1CF65AB82A45387B0042FFBD /* Werkout-watch-Watch-App-Info.plist */, + 1C1A3C732C3376150010CDD5 /* WatchWorkout.swift */, + 1C1A3C712C3373E10010CDD5 /* WatchDelegate.swift */, 1CF65A972A452D270042FFBD /* ContentView.swift */, 1C5190D32A59AEDE00885849 /* MainWatchView.swift */, 1C5190D12A59ACA400885849 /* WatchControlView.swift */, 1CF65AB52A4532940042FFBD /* WatchMainViewModel.swift */, - 1C0494802C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift */, 1C0494822C23C56E003D18BB /* WatchMainViewModel+WCSessionDelegate.swift */, 1CF65A952A452D270042FFBD /* Werkout_watchApp.swift */, 1CF65A992A452D290042FFBD /* Assets.xcassets */, @@ -735,6 +738,7 @@ buildActionMask = 2147483647; files = ( 1C4AFF222A885EBD0027710B /* PreviewData.swift in Sources */, + 1C1A3C722C3373E10010CDD5 /* WatchDelegate.swift in Sources */, 1CF65A982A452D270042FFBD /* ContentView.swift in Sources */, 1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */, 1C4AFF192A65CD6F0027710B /* Superset.swift in Sources */, @@ -759,7 +763,7 @@ 1C04948E2C25CD3D003D18BB /* BridgeModule+WorkoutActions.swift in Sources */, 1C5190D42A59AEDE00885849 /* MainWatchView.swift in Sources */, 1CF65AB42A4530200042FFBD /* WatchPackageModel.swift in Sources */, - 1C0494812C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift in Sources */, + 1C1A3C742C3376150010CDD5 /* WatchWorkout.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iphone/Werkout_ios/BridgeModule+Timer.swift b/iphone/Werkout_ios/BridgeModule+Timer.swift index 2c910e6..9cad039 100644 --- a/iphone/Werkout_ios/BridgeModule+Timer.swift +++ b/iphone/Werkout_ios/BridgeModule+Timer.swift @@ -13,6 +13,7 @@ extension BridgeModule { currentWorkoutRunTimer = nil currentWorkoutRunTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in self.currentWorkoutRunTimeInSeconds += 1 + self.sendCurrentExerciseToWatch() }) currentWorkoutRunTimer?.fire() } @@ -50,7 +51,6 @@ extension BridgeModule { } } } - sendCurrentExerciseToWatch() } else { nextExercise() } diff --git a/iphone/Werkout_ios/BridgeModule+WorkoutActions.swift b/iphone/Werkout_ios/BridgeModule+WorkoutActions.swift index 5a6cc37..71b44ad 100644 --- a/iphone/Werkout_ios/BridgeModule+WorkoutActions.swift +++ b/iphone/Werkout_ios/BridgeModule+WorkoutActions.swift @@ -50,7 +50,6 @@ extension BridgeModule { if let duration = exercise.duration, duration > 0 { self.startExerciseTimerWith(duration: duration) } - self.sendCurrentExerciseToWatch() } } @@ -104,6 +103,9 @@ extension BridgeModule { func resetCurrentWorkout() { DispatchQueue.main.async { + if self.isInWorkout { + self.sendWorkoutCompleteToWatch() + } self.currentWorkoutRunTimeInSeconds = 0 self.currentWorkoutRunTimer?.invalidate() self.currentWorkoutRunTimer = nil diff --git a/iphone/Werkout_ios/Views/ExternalWorkoutDetailView.swift b/iphone/Werkout_ios/Views/ExternalWorkoutDetailView.swift index 4be82a8..b312c09 100644 --- a/iphone/Werkout_ios/Views/ExternalWorkoutDetailView.swift +++ b/iphone/Werkout_ios/Views/ExternalWorkoutDetailView.swift @@ -42,6 +42,7 @@ struct ExternalWorkoutDetailView: View { ExtCountdownView() .padding(.leading, 50) .padding(.bottom, 20) + if extShowNextVideo && extThotStyle != .off { PlayerView(player: $smallAVPlayer) .frame(width: metrics.size.width * 0.2, @@ -60,6 +61,34 @@ struct ExternalWorkoutDetailView: View { .edgesIgnoringSafeArea(.all) .scaledToFill() } + + VStack { + HStack { + if bridgeModule.currentWorkoutRunTimeInSeconds > -1 { + Text(" \(Double(bridgeModule.currentWorkoutRunTimeInSeconds).asString(style: .positional)) ") + .font(Font.system(size: 120)) + .minimumScaleFactor(0.01) + .lineLimit(1) + .padding() + .bold() + .foregroundColor(.white) + .background( + Capsule() + .strokeBorder(Color.black, lineWidth: 0.8) + .background(Color(uiColor: UIColor(red: 148/255, + green: 0, + blue: 211/255, + alpha: 0.5))) + .clipped() + ) + .clipShape(Capsule()) + } + Spacer() + } + .padding(.leading, 50) + Spacer() + } + } .onChange(of: bridgeModule.isInWorkout, perform: { _ in playVideos() diff --git a/iphone/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift b/iphone/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift index f7ea538..40b3967 100644 --- a/iphone/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift +++ b/iphone/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift @@ -176,9 +176,8 @@ struct WorkoutDetailView: View { bridgeModule.completedWorkout = { if let workoutData = createWorkoutData() { workoutComplete = .completedWorkout(workoutData) - } else { - bridgeModule.resetCurrentWorkout() } + bridgeModule.resetCurrentWorkout() } } .onReceive(NotificationCenter.default.publisher( diff --git a/iphone/Werkout_ios/subview/ExtCountdownView.swift b/iphone/Werkout_ios/subview/ExtCountdownView.swift index 82a33a3..a7c7b6a 100644 --- a/iphone/Werkout_ios/subview/ExtCountdownView.swift +++ b/iphone/Werkout_ios/subview/ExtCountdownView.swift @@ -21,16 +21,6 @@ struct ExtCountdownView: View { .minimumScaleFactor(0.01) .lineLimit(1) .frame(maxWidth: .infinity, alignment: .leading) - - if bridgeModule.currentWorkoutRunTimeInSeconds > -1 { - Text("\(Double(bridgeModule.currentWorkoutRunTimeInSeconds).asString(style: .positional))") - .font(Font.system(size: 100)) - .scaledToFit() - .minimumScaleFactor(0.01) - .lineLimit(1) - .frame(maxWidth: .infinity, alignment: .trailing) - .padding(.trailing, 100) - } } .frame(height: metrics.size.height * 0.5) diff --git a/iphone/Werkout_watch Watch App/MainWatchView.swift b/iphone/Werkout_watch Watch App/MainWatchView.swift index 6415fe2..e20b175 100644 --- a/iphone/Werkout_watch Watch App/MainWatchView.swift +++ b/iphone/Werkout_watch Watch App/MainWatchView.swift @@ -8,18 +8,20 @@ import SwiftUI struct MainWatchView: View { - @StateObject var vm = WatchMainViewModel.shared + @ObservedObject var vm = WatchMainViewModel.shared + @ObservedObject var watchWorkout = WatchWorkout.shared var body: some View { VStack { - HStack { - if vm.isInWorkout { + if watchWorkout.isInWorkout { + HStack { + Text(vm.watchPackageModel.currentExerciseName) .font(.body) .foregroundColor(.white) .lineLimit(10) .fixedSize(horizontal: false, vertical: true) - .frame(maxWidth: .infinity, alignment: .leading) + .frame(maxWidth: .infinity, alignment: .leading) Divider() Text("\(vm.watchPackageModel.currentTimeLeft )") .font(.title) @@ -27,28 +29,27 @@ struct MainWatchView: View { .lineLimit(10) .fixedSize(horizontal: false, vertical: true) } - } - - HStack { - if let heartValue = vm.heartValue { - VStack { - Image(systemName: "heart.fill") - .font(Font.system(size: 22)) - .scaledToFit() - .minimumScaleFactor(0.01) - .lineLimit(1) - .foregroundColor(.red) - Text("\(heartValue)") - .font(Font.system(size: 55)) - .scaledToFit() - .minimumScaleFactor(0.01) - .lineLimit(1) - .foregroundColor(.red) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - if vm.isInWorkout { + + HStack { + if let heartValue = watchWorkout.heartValue { + VStack { + Image(systemName: "heart.fill") + .font(Font.system(size: 22)) + .scaledToFit() + .minimumScaleFactor(0.01) + .lineLimit(1) + .foregroundColor(.red) + Text("\(heartValue)") + .font(Font.system(size: 55)) + .scaledToFit() + .minimumScaleFactor(0.01) + .lineLimit(1) + .foregroundColor(.red) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + Button(action: { vm.nextExercise() }, label: { @@ -57,12 +58,10 @@ struct MainWatchView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) }) .buttonStyle(BorderedButtonStyle(tint: .green)) - } else { - VStack { - Text("No Werkout") - Text("🍑") - } } + } else { + Text("No Werkout") + Text("🍑") } } } diff --git a/iphone/Werkout_watch Watch App/WatchControlView.swift b/iphone/Werkout_watch Watch App/WatchControlView.swift index 4953e5b..d547e38 100644 --- a/iphone/Werkout_watch Watch App/WatchControlView.swift +++ b/iphone/Werkout_watch Watch App/WatchControlView.swift @@ -9,6 +9,7 @@ import SwiftUI struct WatchControlView: View { @StateObject var vm = WatchMainViewModel.shared + @StateObject var watchWorkout = WatchWorkout.shared var body: some View { HStack { @@ -44,7 +45,7 @@ struct WatchControlView: View { Button(action: { vm.pauseWorkout() }, label: { - if vm.isPaused { + if watchWorkout.isPaused { Image(systemName: "play") .font(.title) .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/iphone/Werkout_watch Watch App/WatchDelegate.swift b/iphone/Werkout_watch Watch App/WatchDelegate.swift new file mode 100644 index 0000000..57d79dd --- /dev/null +++ b/iphone/Werkout_watch Watch App/WatchDelegate.swift @@ -0,0 +1,41 @@ +// +// WatchDelegate.swift +// Werkout_watch Watch App +// +// Created by Trey Tartt on 7/1/24. +// + +import WatchKit +import HealthKit + +class WatchDelegate: NSObject, WKApplicationDelegate { + func applicationDidFinishLaunching() { + autorizeHealthKit() + } + + func applicationDidBecomeActive() { + + } + + func applicationWillResignActive() { + + } + + func handle(_ workoutConfiguration: HKWorkoutConfiguration) { +// WatchWorkout.shared.startWorkout() + } + + func autorizeHealthKit() { + let healthKitTypes: Set = [ + HKObjectType.quantityType(forIdentifier: .heartRate)!, + HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!, + HKObjectType.quantityType(forIdentifier: .oxygenSaturation)!, + HKQuantityType.workoutType() + ] + HKHealthStore().requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { (succ, error) in + if !succ { + fatalError("Error requesting authorization from health store: \(String(describing: error)))") + } + } + } +} diff --git a/iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift b/iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift index cc5a8c4..2b53e2f 100644 --- a/iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift +++ b/iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift @@ -18,8 +18,10 @@ extension WatchMainViewModel: WCSessionDelegate { func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { print("activation did complete") } - - func send(_ data: Data) { +} + +class DataSender { + static func send(_ data: Data) { guard WCSession.default.activationState == .activated else { return } diff --git a/iphone/Werkout_watch Watch App/WatchMainViewModel.swift b/iphone/Werkout_watch Watch App/WatchMainViewModel.swift index 3c29c5c..2db7aad 100644 --- a/iphone/Werkout_watch Watch App/WatchMainViewModel.swift +++ b/iphone/Werkout_watch Watch App/WatchMainViewModel.swift @@ -12,81 +12,59 @@ import HealthKit class WatchMainViewModel: NSObject, ObservableObject { static let shared = WatchMainViewModel() + var session: WCSession - @Published var isInWorkout = false @Published var watchPackageModel = WatchMainViewModel.defualtPackageModle - @Published var heartValue: Int? - static var defualtPackageModle: WatchPackageModel { WatchPackageModel(currentExerciseName: "", currentExerciseID: -1, currentTimeLeft: -1, workoutStartDate: Date()) } - - let healthStore = HKHealthStore() - var hkWorkoutSession: HKWorkoutSession? - var hkBuilder: HKLiveWorkoutBuilder? - var heartRates = [Int]() - @Published var isPaused = false - + override init() { session = WCSession.default super.init() session.delegate = self session.activate() - autorizeHealthKit() - } - - func autorizeHealthKit() { - let healthKitTypes: Set = [ - HKObjectType.quantityType(forIdentifier: .heartRate)!, - HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!, - HKObjectType.quantityType(forIdentifier: .oxygenSaturation)!, - HKQuantityType.workoutType() - ] - healthStore.requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { (succ, error) in - if !succ { - fatalError("Error requesting authorization from health store: \(String(describing: error)))") - } - } + } // actions from view func nextExercise() { let nextExerciseAction = WatchActions.nextExercise let data = try! JSONEncoder().encode(nextExerciseAction) - send(data) + DataSender.send(data) WKInterfaceDevice.current().play(.start) } func restartExercise() { let nextExerciseAction = WatchActions.restartExercise let data = try! JSONEncoder().encode(nextExerciseAction) - send(data) + DataSender.send(data) WKInterfaceDevice.current().play(.start) } func previousExercise() { let nextExerciseAction = WatchActions.previousExercise let data = try! JSONEncoder().encode(nextExerciseAction) - send(data) + DataSender.send(data) WKInterfaceDevice.current().play(.start) } func completeWorkout() { let nextExerciseAction = WatchActions.stopWorkout let data = try! JSONEncoder().encode(nextExerciseAction) - send(data) + DataSender.send(data) WKInterfaceDevice.current().play(.start) } func pauseWorkout() { let nextExerciseAction = WatchActions.pauseWorkout let data = try! JSONEncoder().encode(nextExerciseAction) - send(data) - isPaused = !isPaused + DataSender.send(data) WKInterfaceDevice.current().play(.start) + WatchWorkout.shared.togglePaused() } func dataToAction(messageData: Data) { @@ -94,27 +72,19 @@ class WatchMainViewModel: NSObject, ObservableObject { DispatchQueue.main.async { switch model { case .inExercise(let newWatchPackageModel): - if !self.isInWorkout { - self.startWorkout() - } + WatchWorkout.shared.startWorkout() if self.watchPackageModel.currentExerciseID != newWatchPackageModel.currentExerciseID { - self.isPaused = false WKInterfaceDevice.current().play(.start) } self.watchPackageModel = newWatchPackageModel case .reset: - self.isInWorkout = false self.watchPackageModel = WatchMainViewModel.defualtPackageModle - self.stopWorkout(sendDetails: false) + WatchWorkout.shared.stopWorkout(sendDetails: false) case .endWorkout: - self.isInWorkout = false self.watchPackageModel = WatchMainViewModel.defualtPackageModle - self.stopWorkout(sendDetails: true) + WatchWorkout.shared.stopWorkout(sendDetails: true) case .startWorkout: - if self.isInWorkout { - self.stopWorkout(sendDetails: false) - } - self.startWorkout() + WatchWorkout.shared.startWorkout() } } } diff --git a/iphone/Werkout_watch Watch App/WatchMainViewModel+WorkoutActions.swift b/iphone/Werkout_watch Watch App/WatchWorkout.swift similarity index 51% rename from iphone/Werkout_watch Watch App/WatchMainViewModel+WorkoutActions.swift rename to iphone/Werkout_watch Watch App/WatchWorkout.swift index 350cec9..9d89cd5 100644 --- a/iphone/Werkout_watch Watch App/WatchMainViewModel+WorkoutActions.swift +++ b/iphone/Werkout_watch Watch App/WatchWorkout.swift @@ -1,96 +1,85 @@ // -// WatchMainViewModel+WorkoutActions.swift +// WatchWorkout.swift // Werkout_watch Watch App // -// Created by Trey Tartt on 6/19/24. +// Created by Trey Tartt on 7/1/24. // import Foundation -import WatchConnectivity -import SwiftUI import HealthKit -extension WatchMainViewModel { - func initWorkout() -> Bool { - let configuration = HKWorkoutConfiguration() - configuration.activityType = .functionalStrengthTraining - configuration.locationType = .indoor - +class WatchWorkout: NSObject, ObservableObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate { + static let shared = WatchWorkout() + + @Published var heartValue: Int? + + let healthStore = HKHealthStore() + var hkWorkoutSession: HKWorkoutSession + var hkBuilder: HKLiveWorkoutBuilder + var heartRates = [Int]() + @Published var isInWorkout = false + @Published var isPaused = false + + private override init() { do { + let configuration = HKWorkoutConfiguration() + configuration.activityType = .functionalStrengthTraining + configuration.locationType = .indoor + hkWorkoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration) - hkBuilder = hkWorkoutSession?.associatedWorkoutBuilder() + hkBuilder = hkWorkoutSession.associatedWorkoutBuilder() + super.init() + hkWorkoutSession.delegate = self + hkBuilder.delegate = self + + /// Set the workout builder's data source. + hkBuilder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, + workoutConfiguration: configuration) } catch { - fatalError("Unable to create the workout session!") + fatalError() } - guard let hkWorkoutSession = hkWorkoutSession, let hkBuilder = hkBuilder else { - return false - } - // Setup session and builder. - hkWorkoutSession.delegate = self - hkBuilder.delegate = self - - /// Set the workout builder's data source. - hkBuilder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, - workoutConfiguration: configuration) - return true } func startWorkout() { - // Initialize our workout - if initWorkout() { - - guard let hkWorkoutSession = hkWorkoutSession, let hkBuilder = hkBuilder else { - return + if isInWorkout { return } + // Start the workout session and begin data collection + hkWorkoutSession.startActivity(with: Date()) + hkBuilder.beginCollection(withStart: Date()) { (succ, error) in + if !succ { + fatalError("Error beginning collection from builder: \(String(describing: error)))") } - - // Start the workout session and begin data collection - hkWorkoutSession.startActivity(with: Date()) - hkBuilder.beginCollection(withStart: Date()) { (succ, error) in - if !succ { - fatalError("Error beginning collection from builder: \(String(describing: error)))") - } - } - isInWorkout = true - isPaused = false - WKInterfaceDevice.current().play(.start) - } else { - print("did not init workout") } + isInWorkout = true + //WKInterfaceDevice.current().play(.start) } func stopWorkout(sendDetails: Bool) { - guard let hkWorkoutSession = hkWorkoutSession, let hkBuilder = hkBuilder else { - return - } - hkWorkoutSession.end() + self.heartRates.removeAll() + self.isInWorkout = false + hkBuilder.endCollection(withEnd: Date()) { (success, error) in - hkBuilder.finishWorkout { (workout, error) in + if !success || error != nil { return } + + self.hkBuilder.finishWorkout { (workout, error) in + guard let workout = workout else { return } + if !sendDetails { return } + DispatchQueue.main.async() { - self.hkWorkoutSession = nil - self.hkBuilder = nil - self.heartRates.removeAll() - self.isInWorkout = false - self.isPaused = false - - guard let id = workout?.uuid else { - return - } - let watchFinishWorkoutModel = WatchFinishWorkoutModel(healthKitUUID: id) + let watchFinishWorkoutModel = WatchFinishWorkoutModel(healthKitUUID: workout.uuid) let data = try! JSONEncoder().encode(watchFinishWorkoutModel) let watchAction = WatchActions.workoutComplete(data) let watchActionData = try! JSONEncoder().encode(watchAction) - - if sendDetails { - self.send(watchActionData) - } + DataSender.send(watchActionData) } } } } -} - -extension WatchMainViewModel: HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate { + + func togglePaused() { + self.isPaused.toggle() + } + func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) { print("[workoutSession] Changed State: \(toState.rawValue)") } diff --git a/iphone/Werkout_watch Watch App/Werkout_watchApp.swift b/iphone/Werkout_watch Watch App/Werkout_watchApp.swift index ff19861..59fc049 100644 --- a/iphone/Werkout_watch Watch App/Werkout_watchApp.swift +++ b/iphone/Werkout_watch Watch App/Werkout_watchApp.swift @@ -9,6 +9,8 @@ import SwiftUI @main struct Werkout_watch_Watch_AppApp: App { + @WKApplicationDelegateAdaptor(WatchDelegate.self) var delegate: WatchDelegate + var body: some Scene { WindowGroup { ContentView()