This commit is contained in:
Trey t
2023-06-23 13:34:29 -05:00
parent a43acc9c27
commit 48f23f8e77
15 changed files with 644 additions and 6 deletions

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "watchos",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,44 @@
//
// ContentView.swift
// Werkout_watch Watch App
//
// Created by Trey Tartt on 6/22/23.
//
import SwiftUI
struct ContentView: View {
let exercise = PreviewWorkout.parseEquipment()[2]
@StateObject var vm = WatchMainViewModel()
var body: some View {
VStack {
if let model = vm.watchPackageModel {
Text(model.currentExerciseName)
Text("\(model.currentTimeLeft )")
}
if let heartValue = vm.heartValue {
HStack {
Image(systemName: "heart.fill")
Text("\(heartValue)")
}
}
Button(action: {
vm.nextExercise()
}, label: {
Image(systemName: "arrow.forward")
.font(.title)
.frame(maxWidth: .infinity, maxHeight: .infinity)
})
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,195 @@
//
// WatchMainViewMoel.swift
// Werkout_watch Watch App
//
// Created by Trey Tartt on 6/22/23.
//
import Foundation
import WatchConnectivity
import SwiftUI
import HealthKit
class WatchMainViewModel: NSObject, ObservableObject {
var session: WCSession
@Published var watchPackageModel: WatchPackageModel?
@Published var heartValue: Int?
let healthStore = HKHealthStore()
var hkWorkoutSession: HKWorkoutSession? {
didSet {
print("here")
}
}
var hkBuilder: HKLiveWorkoutBuilder?
override init() {
session = WCSession.default
super.init()
session.delegate = self
session.activate()
autorizeHealthKit()
}
func autorizeHealthKit() {
let healthKitTypes: Set = [
HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!,
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKQuantityType.workoutType()
]
healthStore.requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { (succ, error) in
if !succ {
fatalError("Error requesting authorization from health store: \(String(describing: error)))")
}
}
}
private func startHeartRateQuery(quantityTypeIdentifier: HKQuantityTypeIdentifier) {
let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = {
query, samples, deletedObjects, queryAnchor, error in
guard let samples = samples as? [HKQuantitySample] else {
return
}
}
let query = HKAnchoredObjectQuery(type: HKObjectType.quantityType(forIdentifier: quantityTypeIdentifier)!, predicate: devicePredicate, anchor: nil, limit: HKObjectQueryNoLimit, resultsHandler: updateHandler)
query.updateHandler = updateHandler
healthStore.execute(query)
}
func nextExercise() {
let nextExerciseAction = WatchActions.nextExercise
let data = try! JSONEncoder().encode(nextExerciseAction)
send(data)
}
}
extension WatchMainViewModel: WCSessionDelegate {
func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
if let model = try? JSONDecoder().decode(WatchPackageModel.self, from: messageData) {
DispatchQueue.main.async {
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?) {
}
func send(_ data: Data) {
guard WCSession.default.activationState == .activated else {
return
}
#if os(iOS)
guard WCSession.default.isWatchAppInstalled else {
return
}
#else
guard WCSession.default.isCompanionAppInstalled else {
return
}
#endif
WCSession.default.sendMessageData(data, replyHandler: nil)
{ error in
print("Cannot send message: \(String(describing: error))")
}
}
}
extension WatchMainViewModel: HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate {
func initWorkout() {
let configuration = HKWorkoutConfiguration()
configuration.activityType = .functionalStrengthTraining
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
}
}
}
}
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
print("[workoutSession] Changed State: \(toState.rawValue)")
}
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
print("[workoutSession] Encountered an error: \(error)")
}
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
for type in collectedTypes {
guard let quantityType = type as? HKQuantityType else {
return
}
switch quantityType {
case HKQuantityType.quantityType(forIdentifier: .heartRate):
let statistics = workoutBuilder.statistics(for: quantityType)
let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
let value = statistics!.mostRecentQuantity()?.doubleValue(for: heartRateUnit)
heartValue = Int(Double(round(1 * value!) / 1))
print("[workoutBuilder] Heart Rate: \(String(describing: heartValue))")
default:
return
}
}
}
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
guard let workoutEventType = workoutBuilder.workoutEvents.last?.type else { return }
print("[workoutBuilderDidCollectEvent] Workout Builder changed event: \(workoutEventType.rawValue)")
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-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>com.apple.developer.healthkit</key>
<true/>
<key>com.apple.developer.healthkit.access</key>
<array/>
</dict>
</plist>

View File

@@ -0,0 +1,17 @@
//
// Werkout_watchApp.swift
// Werkout_watch Watch App
//
// Created by Trey Tartt on 6/22/23.
//
import SwiftUI
@main
struct Werkout_watch_Watch_AppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}