diff --git a/Werkout-ios-Info.plist b/Werkout-ios-Info.plist
index 6a6654d..fc47036 100644
--- a/Werkout-ios-Info.plist
+++ b/Werkout-ios-Info.plist
@@ -2,6 +2,10 @@
+ NSHealthShareUsageDescription
+ Read your heart reate
+ NSHealthUpdateUsageDescription
+ Read your heart reate
NSAppTransportSecurity
NSAllowsArbitraryLoads
diff --git a/Werkout-watch-Watch-App-Info.plist b/Werkout-watch-Watch-App-Info.plist
new file mode 100644
index 0000000..2b0f574
--- /dev/null
+++ b/Werkout-watch-Watch-App-Info.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ UIBackgroundModes
+
+ WKBackgroundModes
+
+ workout-processing
+
+
+
diff --git a/Werkout_ios.xcodeproj/project.pbxproj b/Werkout_ios.xcodeproj/project.pbxproj
index 1defcc8..42b8769 100644
--- a/Werkout_ios.xcodeproj/project.pbxproj
+++ b/Werkout_ios.xcodeproj/project.pbxproj
@@ -43,8 +43,53 @@
1CF65A852A43E8060042FFBD /* CompletedWorkout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A842A43E8060042FFBD /* CompletedWorkout.swift */; };
1CF65A872A4400E10042FFBD /* ToDo in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A862A4400E10042FFBD /* ToDo */; };
1CF65A8E2A44B78B0042FFBD /* CompletedWorkoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A8D2A44B78B0042FFBD /* CompletedWorkoutView.swift */; };
+ 1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A952A452D270042FFBD /* Werkout_watchApp.swift */; };
+ 1CF65A982A452D270042FFBD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A972A452D270042FFBD /* ContentView.swift */; };
+ 1CF65A9A2A452D290042FFBD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A992A452D290042FFBD /* Assets.xcassets */; };
+ 1CF65A9D2A452D290042FFBD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A9C2A452D290042FFBD /* Preview Assets.xcassets */; };
+ 1CF65AA12A452D290042FFBD /* Werkout_watch Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 1CF65A932A452D270042FFBD /* Werkout_watch Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 1CF65AA52A452D9C0042FFBD /* CompletedWorkout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A842A43E8060042FFBD /* CompletedWorkout.swift */; };
+ 1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A5A2A3BF4BE0042FFBD /* Equipment.swift */; };
+ 1CF65AA72A452D9C0042FFBD /* Muscle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A582A3BF4B60042FFBD /* Muscle.swift */; };
+ 1CF65AA82A452D9C0042FFBD /* Exercise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A442A39FB550042FFBD /* Exercise.swift */; };
+ 1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A422A39FB410042FFBD /* Workout.swift */; };
+ 1CF65AAA2A452D9C0042FFBD /* RegisteredUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A462A39FB6C0042FFBD /* RegisteredUser.swift */; };
+ 1CF65AAB2A452DAC0042FFBD /* PreviewWorkout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A512A3A90A00042FFBD /* PreviewWorkout.swift */; };
+ 1CF65AAC2A452DF50042FFBD /* WorkoutOne.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A492A39FBB10042FFBD /* WorkoutOne.json */; };
+ 1CF65AAD2A452DF50042FFBD /* Exercises.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A662A3BFF840042FFBD /* Exercises.json */; };
+ 1CF65AAE2A452DF50042FFBD /* AllWorkouts.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A642A3BF6BE0042FFBD /* AllWorkouts.json */; };
+ 1CF65AAF2A452DF50042FFBD /* AllMuscles.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A562A3BF3830042FFBD /* AllMuscles.json */; };
+ 1CF65AB02A452DF50042FFBD /* Equipment.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A5E2A3BF5A60042FFBD /* Equipment.json */; };
+ 1CF65AB12A452E1A0042FFBD /* BridgeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A4F2A3A1EA90042FFBD /* BridgeModule.swift */; };
+ 1CF65AB32A452F360042FFBD /* WatchPackageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65AB22A452F360042FFBD /* WatchPackageModel.swift */; };
+ 1CF65AB42A4530200042FFBD /* WatchPackageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65AB22A452F360042FFBD /* WatchPackageModel.swift */; };
+ 1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65AB52A4532940042FFBD /* WatchMainViewModel.swift */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 1CF65A9E2A452D290042FFBD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 1CF65A1A2A3972840042FFBD /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 1CF65A922A452D270042FFBD;
+ remoteInfo = "Werkout_watch Watch App";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 1CF65AA02A452D290042FFBD /* Embed Watch Content */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
+ dstSubfolderSpec = 16;
+ files = (
+ 1CF65AA12A452D290042FFBD /* Werkout_watch Watch App.app in Embed Watch Content */,
+ );
+ name = "Embed Watch Content";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
/* Begin PBXFileReference section */
1CF65A222A3972840042FFBD /* Werkout_ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Werkout_ios.app; sourceTree = BUILT_PRODUCTS_DIR; };
1CF65A252A3972840042FFBD /* Werkout_iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Werkout_iosApp.swift; sourceTree = ""; };
@@ -85,6 +130,15 @@
1CF65A842A43E8060042FFBD /* CompletedWorkout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletedWorkout.swift; sourceTree = ""; };
1CF65A862A4400E10042FFBD /* ToDo */ = {isa = PBXFileReference; lastKnownFileType = text; path = ToDo; sourceTree = ""; };
1CF65A8D2A44B78B0042FFBD /* CompletedWorkoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletedWorkoutView.swift; sourceTree = ""; };
+ 1CF65A932A452D270042FFBD /* Werkout_watch Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Werkout_watch Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 1CF65A952A452D270042FFBD /* Werkout_watchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Werkout_watchApp.swift; sourceTree = ""; };
+ 1CF65A972A452D270042FFBD /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 1CF65A992A452D290042FFBD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 1CF65A9C2A452D290042FFBD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 1CF65AB22A452F360042FFBD /* WatchPackageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchPackageModel.swift; sourceTree = ""; };
+ 1CF65AB52A4532940042FFBD /* WatchMainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchMainViewModel.swift; sourceTree = ""; };
+ 1CF65AB72A4534DC0042FFBD /* Werkout_watch Watch App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Werkout_watch Watch App.entitlements"; sourceTree = ""; };
+ 1CF65AB82A45387B0042FFBD /* Werkout-watch-Watch-App-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Werkout-watch-Watch-App-Info.plist"; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -95,6 +149,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 1CF65A902A452D270042FFBD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -102,6 +163,7 @@
isa = PBXGroup;
children = (
1CF65A242A3972840042FFBD /* Werkout_ios */,
+ 1CF65A942A452D270042FFBD /* Werkout_watch Watch App */,
1CF65A232A3972840042FFBD /* Products */,
);
sourceTree = "";
@@ -110,6 +172,7 @@
isa = PBXGroup;
children = (
1CF65A222A3972840042FFBD /* Werkout_ios.app */,
+ 1CF65A932A452D270042FFBD /* Werkout_watch Watch App.app */,
);
name = Products;
sourceTree = "";
@@ -120,6 +183,7 @@
1CF65A552A3AA6800042FFBD /* Werkout-ios-Info.plist */,
1CF65A482A39FB910042FFBD /* JSON */,
1CF65A252A3972840042FFBD /* Werkout_iosApp.swift */,
+ 1CF65AB22A452F360042FFBD /* WatchPackageModel.swift */,
1CF65A822A42347D0042FFBD /* Extensions.swift */,
1CF65A272A3972840042FFBD /* Persistence.swift */,
1CF65A4F2A3A1EA90042FFBD /* BridgeModule.swift */,
@@ -247,6 +311,28 @@
path = CompletedWorkout;
sourceTree = "";
};
+ 1CF65A942A452D270042FFBD /* Werkout_watch Watch App */ = {
+ isa = PBXGroup;
+ children = (
+ 1CF65AB82A45387B0042FFBD /* Werkout-watch-Watch-App-Info.plist */,
+ 1CF65AB72A4534DC0042FFBD /* Werkout_watch Watch App.entitlements */,
+ 1CF65A952A452D270042FFBD /* Werkout_watchApp.swift */,
+ 1CF65A972A452D270042FFBD /* ContentView.swift */,
+ 1CF65AB52A4532940042FFBD /* WatchMainViewModel.swift */,
+ 1CF65A992A452D290042FFBD /* Assets.xcassets */,
+ 1CF65A9B2A452D290042FFBD /* Preview Content */,
+ );
+ path = "Werkout_watch Watch App";
+ sourceTree = "";
+ };
+ 1CF65A9B2A452D290042FFBD /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 1CF65A9C2A452D290042FFBD /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -257,14 +343,33 @@
1CF65A1E2A3972840042FFBD /* Sources */,
1CF65A1F2A3972840042FFBD /* Frameworks */,
1CF65A202A3972840042FFBD /* Resources */,
+ 1CF65AA02A452D290042FFBD /* Embed Watch Content */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 1CF65A9F2A452D290042FFBD /* PBXTargetDependency */,
+ );
+ name = Werkout_ios;
+ productName = Werkout_ios;
+ productReference = 1CF65A222A3972840042FFBD /* Werkout_ios.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 1CF65A922A452D270042FFBD /* Werkout_watch Watch App */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1CF65AA22A452D290042FFBD /* Build configuration list for PBXNativeTarget "Werkout_watch Watch App" */;
+ buildPhases = (
+ 1CF65A8F2A452D270042FFBD /* Sources */,
+ 1CF65A902A452D270042FFBD /* Frameworks */,
+ 1CF65A912A452D270042FFBD /* Resources */,
);
buildRules = (
);
dependencies = (
);
- name = Werkout_ios;
- productName = Werkout_ios;
- productReference = 1CF65A222A3972840042FFBD /* Werkout_ios.app */;
+ name = "Werkout_watch Watch App";
+ productName = "Werkout_watch Watch App";
+ productReference = 1CF65A932A452D270042FFBD /* Werkout_watch Watch App.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -280,6 +385,9 @@
1CF65A212A3972840042FFBD = {
CreatedOnToolsVersion = 14.3;
};
+ 1CF65A922A452D270042FFBD = {
+ CreatedOnToolsVersion = 14.3;
+ };
};
};
buildConfigurationList = 1CF65A1D2A3972840042FFBD /* Build configuration list for PBXProject "Werkout_ios" */;
@@ -296,6 +404,7 @@
projectRoot = "";
targets = (
1CF65A212A3972840042FFBD /* Werkout_ios */,
+ 1CF65A922A452D270042FFBD /* Werkout_watch Watch App */,
);
};
/* End PBXProject section */
@@ -317,6 +426,20 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 1CF65A912A452D270042FFBD /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1CF65AAF2A452DF50042FFBD /* AllMuscles.json in Resources */,
+ 1CF65AAC2A452DF50042FFBD /* WorkoutOne.json in Resources */,
+ 1CF65AAD2A452DF50042FFBD /* Exercises.json in Resources */,
+ 1CF65AB02A452DF50042FFBD /* Equipment.json in Resources */,
+ 1CF65A9D2A452D290042FFBD /* Preview Assets.xcassets in Resources */,
+ 1CF65A9A2A452D290042FFBD /* Assets.xcassets in Resources */,
+ 1CF65AAE2A452DF50042FFBD /* AllWorkouts.json in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -334,6 +457,7 @@
1CF65A692A3C018F0042FFBD /* AccountView.swift in Sources */,
1CF65A4E2A39FF200042FFBD /* WorkoutDetailViewModel.swift in Sources */,
1CF65A472A39FB6C0042FFBD /* RegisteredUser.swift in Sources */,
+ 1CF65AB32A452F360042FFBD /* WatchPackageModel.swift in Sources */,
1CF65A522A3A90A00042FFBD /* PreviewWorkout.swift in Sources */,
1CF65A852A43E8060042FFBD /* CompletedWorkout.swift in Sources */,
1CF65A6E2A3F60480042FFBD /* CreateViewModels.swift in Sources */,
@@ -354,8 +478,35 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 1CF65A8F2A452D270042FFBD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1CF65A982A452D270042FFBD /* ContentView.swift in Sources */,
+ 1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */,
+ 1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */,
+ 1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */,
+ 1CF65AB12A452E1A0042FFBD /* BridgeModule.swift in Sources */,
+ 1CF65AAA2A452D9C0042FFBD /* RegisteredUser.swift in Sources */,
+ 1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */,
+ 1CF65AA72A452D9C0042FFBD /* Muscle.swift in Sources */,
+ 1CF65AAB2A452DAC0042FFBD /* PreviewWorkout.swift in Sources */,
+ 1CF65AA52A452D9C0042FFBD /* CompletedWorkout.swift in Sources */,
+ 1CF65AA82A452D9C0042FFBD /* Exercise.swift in Sources */,
+ 1CF65AB42A4530200042FFBD /* WatchPackageModel.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 1CF65A9F2A452D290042FFBD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 1CF65A922A452D270042FFBD /* Werkout_watch Watch App */;
+ targetProxy = 1CF65A9E2A452D290042FFBD /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin XCBuildConfiguration section */
1CF65A342A3972850042FFBD /* Debug */ = {
isa = XCBuildConfiguration;
@@ -469,6 +620,7 @@
1CF65A372A3972850042FFBD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Werkout_ios/Werkout_ios.entitlements;
@@ -508,6 +660,7 @@
1CF65A382A3972850042FFBD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Werkout_ios/Werkout_ios.entitlements;
@@ -544,6 +697,75 @@
};
name = Release;
};
+ 1CF65AA32A452D290042FFBD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = "Werkout_watch Watch App/Werkout_watch Watch App.entitlements";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"Werkout_watch Watch App/Preview Content\"";
+ DEVELOPMENT_TEAM = V3PF3M6B6U;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "Werkout-watch-Watch-App-Info.plist";
+ INFOPLIST_KEY_CFBundleDisplayName = Werkout_watch;
+ INFOPLIST_KEY_NSHealthShareUsageDescription = "Read your heart reate";
+ INFOPLIST_KEY_NSHealthUpdateUsageDescription = "Read your heart reate";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "com.t-t.Werkout-ios";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.t-t.Werkout-ios.watchkitapp";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = watchos;
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 4;
+ WATCHOS_DEPLOYMENT_TARGET = 9.4;
+ };
+ name = Debug;
+ };
+ 1CF65AA42A452D290042FFBD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = "Werkout_watch Watch App/Werkout_watch Watch App.entitlements";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"Werkout_watch Watch App/Preview Content\"";
+ DEVELOPMENT_TEAM = V3PF3M6B6U;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "Werkout-watch-Watch-App-Info.plist";
+ INFOPLIST_KEY_CFBundleDisplayName = Werkout_watch;
+ INFOPLIST_KEY_NSHealthShareUsageDescription = "Read your heart reate";
+ INFOPLIST_KEY_NSHealthUpdateUsageDescription = "Read your heart reate";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "com.t-t.Werkout-ios";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.t-t.Werkout-ios.watchkitapp";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = watchos;
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 4;
+ VALIDATE_PRODUCT = YES;
+ WATCHOS_DEPLOYMENT_TARGET = 9.4;
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -565,6 +787,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 1CF65AA22A452D290042FFBD /* Build configuration list for PBXNativeTarget "Werkout_watch Watch App" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1CF65AA32A452D290042FFBD /* Debug */,
+ 1CF65AA42A452D290042FFBD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
diff --git a/Werkout_ios.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist b/Werkout_ios.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist
index 8aad30a..2076216 100644
--- a/Werkout_ios.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/Werkout_ios.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -9,6 +9,11 @@
orderHint
0
+ Werkout_watch Watch App.xcscheme_^#shared#^_
+
+ orderHint
+ 1
+
diff --git a/Werkout_ios/BridgeModule.swift b/Werkout_ios/BridgeModule.swift
index 85ddbbe..aee132f 100644
--- a/Werkout_ios/BridgeModule.swift
+++ b/Werkout_ios/BridgeModule.swift
@@ -6,8 +6,15 @@
//
import Foundation
+import WatchConnectivity
+
+enum WatchActions: String, Codable {
+ case nextExercise
+}
+
+class BridgeModule: NSObject, ObservableObject {
+ private let kMessageKey = "message"
-class BridgeModule: ObservableObject {
static let shared = BridgeModule()
@Published var isShowingOnExternalDisplay = false
@@ -19,6 +26,7 @@ class BridgeModule: ObservableObject {
var currentExerciseIdx: Int = -1
var workoutStartDate: Date?
+ var workoutEndDate: Date?
@Published var currentWorkoutRunTimeInSeconds: Int = -1
private var currentWorkoutRunTimer: Timer?
@@ -36,6 +44,11 @@ class BridgeModule: ObservableObject {
startWorkoutTimer()
workoutStartDate = Date()
isInWorkout = true
+
+ if WCSession.isSupported() {
+ WCSession.default.delegate = self
+ WCSession.default.activate()
+ }
}
func resetCurrentWorkout() {
@@ -52,8 +65,14 @@ class BridgeModule: ObservableObject {
currentExercise = nil
currentWorkout = nil
+ if isInWorkout {
+ let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date())
+ let data = try! JSONEncoder().encode(watchModel)
+ send(data)
+ }
isInWorkout = false
workoutStartDate = nil
+ workoutEndDate = nil
}
private func startWorkoutTimer() {
@@ -82,6 +101,11 @@ class BridgeModule: ObservableObject {
@objc func updateCounter() {
if timeLeft > 0 {
timeLeft -= 1
+
+ let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date())
+ let data = try! JSONEncoder().encode(watchModel)
+ send(data)
+
} else {
timer?.invalidate()
timer = nil
@@ -114,3 +138,46 @@ class BridgeModule: ObservableObject {
}
}
}
+
+extension BridgeModule: WCSessionDelegate {
+ func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
+ if let model = try? JSONDecoder().decode(WatchActions.self, from: messageData) {
+ switch model {
+ case .nextExercise:
+ nextExercise()
+ }
+ }
+ }
+
+
+ func session(_ session: WCSession,
+ activationDidCompleteWith activationState: WCSessionActivationState,
+ error: Error?) {}
+
+#if os(iOS)
+ func sessionDidBecomeInactive(_ session: WCSession) {}
+ func sessionDidDeactivate(_ session: WCSession) {
+ session.activate()
+ }
+#endif
+
+
+ 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))")
+ }
+ }
+}
diff --git a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift
index aa998ab..a04b8ea 100644
--- a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift
+++ b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift
@@ -33,11 +33,11 @@ struct WorkoutDetailView: View {
CountdownView()
ExerciseListView(workout: workout)
ActionsView(completedWorkout: {
+ bridgeModule.workoutEndDate = Date()
if let workoutData = createWorkoutData() {
presentedSheet = .completedWorkout(workoutData)
bridgeModule.resetCurrentWorkout()
}
-
}, workout: workout)
.frame(height: 44)
@@ -59,12 +59,14 @@ struct WorkoutDetailView: View {
func createWorkoutData() -> [String:Any]? {
guard let workoutid = bridgeModule.currentWorkout?.id,
- let startTime = bridgeModule.workoutStartDate?.timeFormatForUpload else {
+ let startTime = bridgeModule.workoutStartDate?.timeFormatForUpload,
+ let endTime = bridgeModule.workoutEndDate?.timeFormatForUpload else {
return nil
}
let postBody = [
"difficulty": 1,
"workout_start_time": startTime,
+ "workout_end_time": endTime,
"workout": workoutid,
"total_time": bridgeModule.currentWorkoutRunTimeInSeconds
] as [String : Any]
diff --git a/Werkout_ios/WatchPackageModel.swift b/Werkout_ios/WatchPackageModel.swift
new file mode 100644
index 0000000..2b55273
--- /dev/null
+++ b/Werkout_ios/WatchPackageModel.swift
@@ -0,0 +1,15 @@
+//
+// WatchPackageModel.swift
+// Werkout_ios
+//
+// Created by Trey Tartt on 6/22/23.
+//
+
+import Foundation
+
+struct WatchPackageModel: Codable {
+ var currentExerciseName: String
+ var currentTimeLeft: Int
+ var workoutStartDate: Date
+ var workoutEndDate: Date?
+}
diff --git a/Werkout_watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json b/Werkout_watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/Werkout_watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Werkout_watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json b/Werkout_watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..49c81cd
--- /dev/null
+++ b/Werkout_watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,13 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "watchos",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Werkout_watch Watch App/Assets.xcassets/Contents.json b/Werkout_watch Watch App/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Werkout_watch Watch App/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Werkout_watch Watch App/ContentView.swift b/Werkout_watch Watch App/ContentView.swift
new file mode 100644
index 0000000..2990cd7
--- /dev/null
+++ b/Werkout_watch Watch App/ContentView.swift
@@ -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()
+ }
+}
diff --git a/Werkout_watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json b/Werkout_watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Werkout_watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Werkout_watch Watch App/WatchMainViewModel.swift b/Werkout_watch Watch App/WatchMainViewModel.swift
new file mode 100644
index 0000000..966e594
--- /dev/null
+++ b/Werkout_watch Watch App/WatchMainViewModel.swift
@@ -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) {
+ 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)")
+ }
+}
diff --git a/Werkout_watch Watch App/Werkout_watch Watch App.entitlements b/Werkout_watch Watch App/Werkout_watch Watch App.entitlements
new file mode 100644
index 0000000..2ab14a2
--- /dev/null
+++ b/Werkout_watch Watch App/Werkout_watch Watch App.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.developer.healthkit
+
+ com.apple.developer.healthkit.access
+
+
+
diff --git a/Werkout_watch Watch App/Werkout_watchApp.swift b/Werkout_watch Watch App/Werkout_watchApp.swift
new file mode 100644
index 0000000..ff19861
--- /dev/null
+++ b/Werkout_watch Watch App/Werkout_watchApp.swift
@@ -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()
+ }
+ }
+}