try to fix watch stuff
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
1C0494822C23C56E003D18BB /* WatchMainViewModel+WCSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WatchMainViewModel+WCSessionDelegate.swift"; sourceTree = "<group>"; };
|
||||
1C0494862C23E7BD003D18BB /* BridgeModule+Watch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BridgeModule+Watch.swift"; sourceTree = "<group>"; };
|
||||
1C0494892C25CB4F003D18BB /* BridgeModule+WorkoutActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BridgeModule+WorkoutActions.swift"; sourceTree = "<group>"; };
|
||||
1C04948B2C25CB80003D18BB /* AudioEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioEngine.swift; sourceTree = "<group>"; };
|
||||
1C0494922C25CEF0003D18BB /* BridgeModule+Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BridgeModule+Timer.swift"; sourceTree = "<group>"; };
|
||||
1C1A3C712C3373E10010CDD5 /* WatchDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchDelegate.swift; sourceTree = "<group>"; };
|
||||
1C1A3C732C3376150010CDD5 /* WatchWorkout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchWorkout.swift; sourceTree = "<group>"; };
|
||||
1C31C8822A53AE3E00350540 /* short_beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = short_beep.m4a; sourceTree = "<group>"; };
|
||||
1C31C8832A53AE3E00350540 /* long_beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = long_beep.m4a; sourceTree = "<group>"; };
|
||||
1C31C8862A55B2CC00350540 /* PlayerUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerUIView.swift; sourceTree = "<group>"; };
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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("🍑")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
41
iphone/Werkout_watch Watch App/WatchDelegate.swift
Normal file
41
iphone/Werkout_watch Watch App/WatchDelegate.swift
Normal file
@@ -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)))")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)")
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user