WIP
This commit is contained in:
@@ -18,6 +18,12 @@ enum WatchActions: Codable {
|
||||
case workoutComplete(Data)
|
||||
}
|
||||
|
||||
enum PhoneToWatchActions: Codable {
|
||||
case inExercise(WatchPackageModel)
|
||||
case reset
|
||||
case endWorkout
|
||||
}
|
||||
|
||||
class BridgeModule: NSObject, ObservableObject {
|
||||
private let kMessageKey = "message"
|
||||
|
||||
@@ -100,10 +106,7 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
self.workoutStartDate = nil
|
||||
self.workoutEndDate = nil
|
||||
}
|
||||
|
||||
let watchModel = WatchPackageModel(currentExerciseName: "", currentTimeLeft: -100, workoutStartDate: Date())
|
||||
let data = try! JSONEncoder().encode(watchModel)
|
||||
send(data)
|
||||
sendResetToWatch()
|
||||
}
|
||||
|
||||
private func startWorkoutTimer() {
|
||||
@@ -134,11 +137,7 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
@objc func updateCurrentExerciseTimer() {
|
||||
if currentExerciseTimeLeft > 0 {
|
||||
currentExerciseTimeLeft -= 1
|
||||
|
||||
let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date())
|
||||
let data = try! JSONEncoder().encode(watchModel)
|
||||
send(data)
|
||||
|
||||
|
||||
if currentExerciseTimeLeft == 0 {
|
||||
playFinished()
|
||||
} else {
|
||||
@@ -146,6 +145,7 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
playBeep()
|
||||
}
|
||||
}
|
||||
sendCurrentExerciseToWatch()
|
||||
} else {
|
||||
nextExercise()
|
||||
}
|
||||
@@ -209,25 +209,16 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
|
||||
if let duration = exercise.duration,
|
||||
duration > 0 {
|
||||
print(duration)
|
||||
self.startExerciseTimerWith(duration: duration)
|
||||
} else {
|
||||
var intWatchDispaly = -1
|
||||
if let reps = self.currentExercise?.reps,
|
||||
reps > 0 {
|
||||
intWatchDispaly = reps
|
||||
}
|
||||
|
||||
// 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: self.currentExercise?.exercise.name ?? "-", currentTimeLeft: intWatchDispaly, workoutStartDate: self.workoutStartDate ?? Date())
|
||||
let data = try! JSONEncoder().encode(watchModel)
|
||||
self.send(data)
|
||||
}
|
||||
self.sendCurrentExerciseToWatch()
|
||||
}
|
||||
}
|
||||
|
||||
func completeWorkout() {
|
||||
self.currentExerciseTimer?.invalidate()
|
||||
self.currentExerciseTimer = nil
|
||||
|
||||
workoutEndDate = Date()
|
||||
|
||||
//if connected to watch
|
||||
@@ -276,12 +267,41 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
}
|
||||
|
||||
extension BridgeModule: WCSessionDelegate {
|
||||
func sendWorkoutCompleteToWatch() {
|
||||
let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date())
|
||||
func sendResetToWatch() {
|
||||
let watchModel = PhoneToWatchActions.reset
|
||||
let data = try! JSONEncoder().encode(watchModel)
|
||||
send(data)
|
||||
}
|
||||
|
||||
func sendWorkoutCompleteToWatch() {
|
||||
let model = PhoneToWatchActions.endWorkout
|
||||
let data = try! JSONEncoder().encode(model)
|
||||
send(data)
|
||||
}
|
||||
|
||||
func sendCurrentExerciseToWatch() {
|
||||
if 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 {
|
||||
var intWatchDispaly = -1
|
||||
if let reps = self.currentExercise?.reps,
|
||||
reps > 0 {
|
||||
intWatchDispaly = reps
|
||||
}
|
||||
|
||||
// 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: self.currentExercise?.exercise.name ?? "-", currentTimeLeft: intWatchDispaly, 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 {
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import WatchKit
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
MainWatchView()
|
||||
WatchControlView()
|
||||
NowPlayingView()
|
||||
}
|
||||
.padding()
|
||||
.tabViewStyle(PageTabViewStyle())
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
import SwiftUI
|
||||
|
||||
struct MainWatchView: View {
|
||||
@StateObject var vm = WatchMainViewModel()
|
||||
@StateObject var vm = WatchMainViewModel.shared
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
if let model = vm.watchPackageModel {
|
||||
Text(model.currentExerciseName)
|
||||
if vm.isInWorkout {
|
||||
Text(vm.watchPackageModel.currentExerciseName)
|
||||
.font(Font.system(size: 55))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(.white)
|
||||
Divider()
|
||||
Text("\(model.currentTimeLeft )")
|
||||
Text("\(vm.watchPackageModel.currentTimeLeft )")
|
||||
.font(Font.system(size: 55))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
@@ -48,14 +48,19 @@ struct MainWatchView: View {
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
Button(action: {
|
||||
vm.nextExercise()
|
||||
}, label: {
|
||||
Image(systemName: "arrow.forward")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.buttonStyle(BorderedButtonStyle(tint: .green))
|
||||
|
||||
if vm.isInWorkout {
|
||||
Button(action: {
|
||||
vm.nextExercise()
|
||||
}, label: {
|
||||
Image(systemName: "arrow.forward")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.buttonStyle(BorderedButtonStyle(tint: .green))
|
||||
} else {
|
||||
Text("No Werkout")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct WatchControlView: View {
|
||||
@StateObject var vm = WatchMainViewModel()
|
||||
@StateObject var vm = WatchMainViewModel.shared
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
@@ -54,8 +54,8 @@ struct WatchControlView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchControlView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WatchControlView(vm: WatchMainViewModel())
|
||||
}
|
||||
}
|
||||
//struct WatchControlView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// WatchControlView(vm: WatchMainViewModel())
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -11,10 +11,18 @@ import SwiftUI
|
||||
import HealthKit
|
||||
|
||||
class WatchMainViewModel: NSObject, ObservableObject {
|
||||
static let shared = WatchMainViewModel()
|
||||
var session: WCSession
|
||||
@Published var watchPackageModel: WatchPackageModel?
|
||||
|
||||
@Published var isInWorkout = false
|
||||
@Published var watchPackageModel = WatchMainViewModel.defualtPackageModle
|
||||
|
||||
@Published var heartValue: Int?
|
||||
|
||||
static var defualtPackageModle: WatchPackageModel {
|
||||
WatchPackageModel(currentExerciseName: "", currentTimeLeft: -1, workoutStartDate: Date())
|
||||
}
|
||||
|
||||
let healthStore = HKHealthStore()
|
||||
var hkWorkoutSession: HKWorkoutSession?
|
||||
var hkBuilder: HKLiveWorkoutBuilder?
|
||||
@@ -82,27 +90,106 @@ class WatchMainViewModel: NSObject, ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
extension WatchMainViewModel {
|
||||
func initWorkout() -> Bool {
|
||||
let configuration = HKWorkoutConfiguration()
|
||||
configuration.activityType = .functionalStrengthTraining
|
||||
configuration.locationType = .indoor
|
||||
|
||||
do {
|
||||
hkWorkoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
|
||||
hkBuilder = hkWorkoutSession?.associatedWorkoutBuilder()
|
||||
} catch {
|
||||
fatalError("Unable to create the workout session!")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
} else {
|
||||
print("didn not init workout")
|
||||
}
|
||||
}
|
||||
|
||||
func stopWorkout(sendDetails: Bool) {
|
||||
guard let hkWorkoutSession = hkWorkoutSession, let hkBuilder = hkBuilder else {
|
||||
return
|
||||
}
|
||||
|
||||
hkWorkoutSession.end()
|
||||
hkBuilder.endCollection(withEnd: Date()) { (success, error) in
|
||||
hkBuilder.finishWorkout { (workout, error) in
|
||||
DispatchQueue.main.async() {
|
||||
self.hkWorkoutSession = nil
|
||||
self.hkBuilder = nil
|
||||
let totalEnergy = workout?.totalEnergyBurned?.doubleValue(for: .kilocalorie()) ?? -1
|
||||
let watchFinishWorkoutModel = WatchFinishWorkoutModel(totalBurnedEnergery: totalEnergy, allHeartRates: self.heartRates)
|
||||
let data = try! JSONEncoder().encode(watchFinishWorkoutModel)
|
||||
let watchAction = WatchActions.workoutComplete(data)
|
||||
let watchActionData = try! JSONEncoder().encode(watchAction)
|
||||
|
||||
if sendDetails {
|
||||
self.send(watchActionData)
|
||||
}
|
||||
|
||||
self.heartRates.removeAll()
|
||||
self.isInWorkout = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WatchMainViewModel: WCSessionDelegate {
|
||||
func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
|
||||
if let model = try? JSONDecoder().decode(WatchPackageModel.self, from: messageData) {
|
||||
if let model = try? JSONDecoder().decode(PhoneToWatchActions.self, from: messageData) {
|
||||
DispatchQueue.main.async {
|
||||
if model.currentTimeLeft == -100 {
|
||||
self.watchPackageModel = nil
|
||||
return
|
||||
switch model {
|
||||
case .inExercise(let data):
|
||||
if self.isInWorkout == false {
|
||||
self.startWorkout()
|
||||
}
|
||||
self.watchPackageModel = data
|
||||
case .reset:
|
||||
self.isInWorkout = false
|
||||
self.watchPackageModel = WatchMainViewModel.defualtPackageModle
|
||||
self.stopWorkout(sendDetails: false)
|
||||
case .endWorkout:
|
||||
self.isInWorkout = false
|
||||
self.watchPackageModel = WatchMainViewModel.defualtPackageModle
|
||||
self.stopWorkout(sendDetails: true)
|
||||
}
|
||||
if self.watchPackageModel?.workoutEndDate != nil {
|
||||
self.watchPackageModel = nil
|
||||
self.stopWorkout()
|
||||
} else if self.watchPackageModel == nil {
|
||||
self.startWorkout()
|
||||
}
|
||||
self.watchPackageModel = model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
|
||||
|
||||
print("activation did complete")
|
||||
}
|
||||
|
||||
func send(_ data: Data) {
|
||||
@@ -126,70 +213,6 @@ extension WatchMainViewModel: WCSessionDelegate {
|
||||
}
|
||||
|
||||
extension WatchMainViewModel: HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate {
|
||||
func initWorkout() {
|
||||
let configuration = HKWorkoutConfiguration()
|
||||
configuration.activityType = .functionalStrengthTraining
|
||||
configuration.locationType = .indoor
|
||||
|
||||
do {
|
||||
hkWorkoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
|
||||
hkBuilder = hkWorkoutSession?.associatedWorkoutBuilder()
|
||||
} catch {
|
||||
fatalError("Unable to create the workout session!")
|
||||
}
|
||||
guard let hkWorkoutSession = hkWorkoutSession, let hkBuilder = hkBuilder else {
|
||||
return
|
||||
}
|
||||
// Setup session and builder.
|
||||
hkWorkoutSession.delegate = self
|
||||
hkBuilder.delegate = self
|
||||
|
||||
/// Set the workout builder's data source.
|
||||
hkBuilder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore,
|
||||
workoutConfiguration: configuration)
|
||||
}
|
||||
|
||||
func startWorkout() {
|
||||
// Initialize our workout
|
||||
initWorkout()
|
||||
|
||||
guard let hkWorkoutSession = hkWorkoutSession, let hkBuilder = hkBuilder else {
|
||||
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)))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopWorkout() {
|
||||
guard let hkWorkoutSession = hkWorkoutSession, let hkBuilder = hkBuilder else {
|
||||
return
|
||||
}
|
||||
|
||||
hkWorkoutSession.end()
|
||||
hkBuilder.endCollection(withEnd: Date()) { (success, error) in
|
||||
hkBuilder.finishWorkout { (workout, error) in
|
||||
DispatchQueue.main.async() {
|
||||
self.hkWorkoutSession = nil
|
||||
self.hkBuilder = nil
|
||||
let totalEnergy = workout?.totalEnergyBurned?.doubleValue(for: .kilocalorie()) ?? -1
|
||||
let watchFinishWorkoutModel = WatchFinishWorkoutModel(totalBurnedEnergery: totalEnergy, allHeartRates: self.heartRates)
|
||||
let data = try! JSONEncoder().encode(watchFinishWorkoutModel)
|
||||
let watchAction = WatchActions.workoutComplete(data)
|
||||
let watchActionData = try! JSONEncoder().encode(watchAction)
|
||||
self.send(watchActionData)
|
||||
|
||||
self.heartRates.removeAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
|
||||
print("[workoutSession] Changed State: \(toState.rawValue)")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user