try to fix watch stuff
This commit is contained in:
@@ -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