split files up, fix complete workout screen not showing if remove workout details screen
This commit is contained in:
@@ -9,6 +9,14 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1C0494812C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494802C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift */; };
|
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 */; };
|
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 */; };
|
||||||
|
1C04948A2C25CB4F003D18BB /* BridgeModule+WorkoutActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0494892C25CB4F003D18BB /* BridgeModule+WorkoutActions.swift */; };
|
||||||
|
1C04948C2C25CB80003D18BB /* AudioEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04948B2C25CB80003D18BB /* AudioEngine.swift */; };
|
||||||
|
1C04948D2C25CC93003D18BB /* AudioEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04948B2C25CB80003D18BB /* AudioEngine.swift */; };
|
||||||
|
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 */; };
|
||||||
1C31C8842A53AE3E00350540 /* short_beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 1C31C8822A53AE3E00350540 /* short_beep.m4a */; };
|
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 */; };
|
1C31C8852A53AE3E00350540 /* long_beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 1C31C8832A53AE3E00350540 /* long_beep.m4a */; };
|
||||||
1C31C8872A55B2CC00350540 /* PlayerUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C31C8862A55B2CC00350540 /* PlayerUIView.swift */; };
|
1C31C8872A55B2CC00350540 /* PlayerUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C31C8862A55B2CC00350540 /* PlayerUIView.swift */; };
|
||||||
@@ -148,6 +156,10 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1C0494802C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WatchMainViewModel+WorkoutActions.swift"; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
||||||
1C31C8822A53AE3E00350540 /* short_beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = short_beep.m4a; 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>"; };
|
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>"; };
|
1C31C8862A55B2CC00350540 /* PlayerUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerUIView.swift; sourceTree = "<group>"; };
|
||||||
@@ -344,9 +356,13 @@
|
|||||||
1CF65AB22A452F360042FFBD /* WatchPackageModel.swift */,
|
1CF65AB22A452F360042FFBD /* WatchPackageModel.swift */,
|
||||||
1CF65A822A42347D0042FFBD /* Extensions.swift */,
|
1CF65A822A42347D0042FFBD /* Extensions.swift */,
|
||||||
1CF65A272A3972840042FFBD /* Persistence.swift */,
|
1CF65A272A3972840042FFBD /* Persistence.swift */,
|
||||||
|
1C04948B2C25CB80003D18BB /* AudioEngine.swift */,
|
||||||
1CD0C6662A5CA19600970E52 /* BaseURLs.swift */,
|
1CD0C6662A5CA19600970E52 /* BaseURLs.swift */,
|
||||||
1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */,
|
1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */,
|
||||||
1CF65A4F2A3A1EA90042FFBD /* BridgeModule.swift */,
|
1CF65A4F2A3A1EA90042FFBD /* BridgeModule.swift */,
|
||||||
|
1C0494922C25CEF0003D18BB /* BridgeModule+Timer.swift */,
|
||||||
|
1C0494862C23E7BD003D18BB /* BridgeModule+Watch.swift */,
|
||||||
|
1C0494892C25CB4F003D18BB /* BridgeModule+WorkoutActions.swift */,
|
||||||
1CF65A802A412AA30042FFBD /* DataStore.swift */,
|
1CF65A802A412AA30042FFBD /* DataStore.swift */,
|
||||||
1CF65AB92A4894430042FFBD /* UserStore.swift */,
|
1CF65AB92A4894430042FFBD /* UserStore.swift */,
|
||||||
1C6BF28E2A56602B00450FD7 /* Keychain.swift */,
|
1C6BF28E2A56602B00450FD7 /* Keychain.swift */,
|
||||||
@@ -565,7 +581,7 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastSwiftUpdateCheck = 1430;
|
LastSwiftUpdateCheck = 1430;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1530;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
1CF65A212A3972840042FFBD = {
|
1CF65A212A3972840042FFBD = {
|
||||||
CreatedOnToolsVersion = 14.3;
|
CreatedOnToolsVersion = 14.3;
|
||||||
@@ -640,6 +656,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
1C04948A2C25CB4F003D18BB /* BridgeModule+WorkoutActions.swift in Sources */,
|
||||||
1CF65A7F2A4129320042FFBD /* Fetchables.swift in Sources */,
|
1CF65A7F2A4129320042FFBD /* Fetchables.swift in Sources */,
|
||||||
1CF65A812A412AA30042FFBD /* DataStore.swift in Sources */,
|
1CF65A812A412AA30042FFBD /* DataStore.swift in Sources */,
|
||||||
1CF65A6B2A3C1EAC0042FFBD /* CreateWorkoutMainView.swift in Sources */,
|
1CF65A6B2A3C1EAC0042FFBD /* CreateWorkoutMainView.swift in Sources */,
|
||||||
@@ -697,9 +714,12 @@
|
|||||||
1C485C8C2A49D65600A6F896 /* WorkoutHistoryView.swift in Sources */,
|
1C485C8C2A49D65600A6F896 /* WorkoutHistoryView.swift in Sources */,
|
||||||
1CAF4D952A52180600B00E50 /* PlanWorkoutView.swift in Sources */,
|
1CAF4D952A52180600B00E50 /* PlanWorkoutView.swift in Sources */,
|
||||||
1C5190C62A589CC100885849 /* ActionsView.swift in Sources */,
|
1C5190C62A589CC100885849 /* ActionsView.swift in Sources */,
|
||||||
|
1C0494932C25CEF0003D18BB /* BridgeModule+Timer.swift in Sources */,
|
||||||
1CC092F52C1FAE7B0004E1E6 /* ShowNextUpView.swift in Sources */,
|
1CC092F52C1FAE7B0004E1E6 /* ShowNextUpView.swift in Sources */,
|
||||||
1CF65A5B2A3BF4BE0042FFBD /* Equipment.swift in Sources */,
|
1CF65A5B2A3BF4BE0042FFBD /* Equipment.swift in Sources */,
|
||||||
1C4AFF152A60F25F0027710B /* ThotStyle.swift in Sources */,
|
1C4AFF152A60F25F0027710B /* ThotStyle.swift in Sources */,
|
||||||
|
1C0494872C23E7BD003D18BB /* BridgeModule+Watch.swift in Sources */,
|
||||||
|
1C04948C2C25CB80003D18BB /* AudioEngine.swift in Sources */,
|
||||||
1C5190C82A589CDA00885849 /* CurrentWorkoutElapsedTimeView.swift in Sources */,
|
1C5190C82A589CDA00885849 /* CurrentWorkoutElapsedTimeView.swift in Sources */,
|
||||||
1CC092F92C1FB1420004E1E6 /* AllExerciseView.swift in Sources */,
|
1CC092F92C1FB1420004E1E6 /* AllExerciseView.swift in Sources */,
|
||||||
1CC093012C20B0E90004E1E6 /* TitleView.swift in Sources */,
|
1CC093012C20B0E90004E1E6 /* TitleView.swift in Sources */,
|
||||||
@@ -719,13 +739,16 @@
|
|||||||
1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */,
|
1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */,
|
||||||
1C4AFF192A65CD6F0027710B /* Superset.swift in Sources */,
|
1C4AFF192A65CD6F0027710B /* Superset.swift in Sources */,
|
||||||
1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */,
|
1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */,
|
||||||
|
1C0494882C23E7C5003D18BB /* BridgeModule+Watch.swift in Sources */,
|
||||||
1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */,
|
1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */,
|
||||||
1C4AFF162A60F27E0027710B /* ThotStyle.swift in Sources */,
|
1C4AFF162A60F27E0027710B /* ThotStyle.swift in Sources */,
|
||||||
1C4AFF212A8801090027710B /* AudioQueue.swift in Sources */,
|
1C4AFF212A8801090027710B /* AudioQueue.swift in Sources */,
|
||||||
1CF65AB12A452E1A0042FFBD /* BridgeModule.swift in Sources */,
|
1CF65AB12A452E1A0042FFBD /* BridgeModule.swift in Sources */,
|
||||||
1C0494832C23C56E003D18BB /* WatchMainViewModel+WCSessionDelegate.swift in Sources */,
|
1C0494832C23C56E003D18BB /* WatchMainViewModel+WCSessionDelegate.swift in Sources */,
|
||||||
1CF65AAA2A452D9C0042FFBD /* RegisteredUser.swift in Sources */,
|
1CF65AAA2A452D9C0042FFBD /* RegisteredUser.swift in Sources */,
|
||||||
|
1C04948D2C25CC93003D18BB /* AudioEngine.swift in Sources */,
|
||||||
1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */,
|
1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */,
|
||||||
|
1C0494942C25CEF4003D18BB /* BridgeModule+Timer.swift in Sources */,
|
||||||
1CD0C6682A5CA1A200970E52 /* BaseURLs.swift in Sources */,
|
1CD0C6682A5CA1A200970E52 /* BaseURLs.swift in Sources */,
|
||||||
1CF65AA72A452D9C0042FFBD /* Muscle.swift in Sources */,
|
1CF65AA72A452D9C0042FFBD /* Muscle.swift in Sources */,
|
||||||
1C4AFF1C2A65FB2B0027710B /* CurrentWorkoutInfo.swift in Sources */,
|
1C4AFF1C2A65FB2B0027710B /* CurrentWorkoutInfo.swift in Sources */,
|
||||||
@@ -733,6 +756,7 @@
|
|||||||
1CF65AA52A452D9C0042FFBD /* CompletedWorkout.swift in Sources */,
|
1CF65AA52A452D9C0042FFBD /* CompletedWorkout.swift in Sources */,
|
||||||
1CF65AA82A452D9C0042FFBD /* Exercise.swift in Sources */,
|
1CF65AA82A452D9C0042FFBD /* Exercise.swift in Sources */,
|
||||||
1C5190D22A59ACA400885849 /* WatchControlView.swift in Sources */,
|
1C5190D22A59ACA400885849 /* WatchControlView.swift in Sources */,
|
||||||
|
1C04948E2C25CD3D003D18BB /* BridgeModule+WorkoutActions.swift in Sources */,
|
||||||
1C5190D42A59AEDE00885849 /* MainWatchView.swift in Sources */,
|
1C5190D42A59AEDE00885849 /* MainWatchView.swift in Sources */,
|
||||||
1CF65AB42A4530200042FFBD /* WatchPackageModel.swift in Sources */,
|
1CF65AB42A4530200042FFBD /* WatchPackageModel.swift in Sources */,
|
||||||
1C0494812C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift in Sources */,
|
1C0494812C23C53E003D18BB /* WatchMainViewModel+WorkoutActions.swift in Sources */,
|
||||||
@@ -783,9 +807,11 @@
|
|||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
@@ -841,9 +867,11 @@
|
|||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@@ -869,6 +897,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Werkout_ios/Werkout_ios.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Werkout_ios/Werkout_ios.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Werkout_ios/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Werkout_ios/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -913,6 +942,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Werkout_ios/Werkout_ios.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Werkout_ios/Werkout_ios.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Werkout_ios/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Werkout_ios/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1530"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1530"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
66
iphone/Werkout_ios/AudioEngine.swift
Normal file
66
iphone/Werkout_ios/AudioEngine.swift
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// AudioEngine.swift
|
||||||
|
// Werkout_ios
|
||||||
|
//
|
||||||
|
// Created by Trey Tartt on 6/21/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AVKit
|
||||||
|
|
||||||
|
class AudioEngine {
|
||||||
|
// var audioPlayer: AVAudioPlayer?
|
||||||
|
// var avPlayer: AVPlayer?
|
||||||
|
|
||||||
|
static func playRemoteAudio(fromURL url: URL) {
|
||||||
|
#if os(iOS)
|
||||||
|
let playerItem = AVPlayerItem(url: url)
|
||||||
|
do {
|
||||||
|
try AVAudioSession.sharedInstance().setCategory(.playback,
|
||||||
|
mode: .default,
|
||||||
|
options: [.mixWithOthers])
|
||||||
|
try AVAudioSession.sharedInstance().setActive(true)
|
||||||
|
|
||||||
|
let avPlayer = AVPlayer(playerItem: playerItem)
|
||||||
|
avPlayer.play()
|
||||||
|
} catch {
|
||||||
|
print("ERROR")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static func playBeep() {
|
||||||
|
#if os(iOS)
|
||||||
|
if let path = Bundle.main.path(forResource: "short_beep", ofType: "m4a") {
|
||||||
|
do {
|
||||||
|
try AVAudioSession.sharedInstance().setCategory(.playback,
|
||||||
|
mode: .default,
|
||||||
|
options: [.mixWithOthers])
|
||||||
|
try AVAudioSession.sharedInstance().setActive(true)
|
||||||
|
|
||||||
|
let audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
|
||||||
|
audioPlayer.play()
|
||||||
|
} catch {
|
||||||
|
print("ERROR")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static func playFinished() {
|
||||||
|
#if os(iOS)
|
||||||
|
if let path = Bundle.main.path(forResource: "long_beep", ofType: "m4a") {
|
||||||
|
do {
|
||||||
|
try AVAudioSession.sharedInstance().setCategory(.playback,
|
||||||
|
mode: .default,
|
||||||
|
options: [.mixWithOthers])
|
||||||
|
try AVAudioSession.sharedInstance().setActive(true)
|
||||||
|
|
||||||
|
let audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
|
||||||
|
audioPlayer.play()
|
||||||
|
} catch {
|
||||||
|
print("ERROR")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
58
iphone/Werkout_ios/BridgeModule+Timer.swift
Normal file
58
iphone/Werkout_ios/BridgeModule+Timer.swift
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// BridgeModule+Timer.swift
|
||||||
|
// Werkout_ios
|
||||||
|
//
|
||||||
|
// Created by Trey Tartt on 6/21/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension BridgeModule {
|
||||||
|
func startWorkoutTimer() {
|
||||||
|
currentWorkoutRunTimer?.invalidate()
|
||||||
|
currentWorkoutRunTimer = nil
|
||||||
|
currentWorkoutRunTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
|
||||||
|
self.currentWorkoutRunTimeInSeconds += 1
|
||||||
|
})
|
||||||
|
currentWorkoutRunTimer?.fire()
|
||||||
|
}
|
||||||
|
|
||||||
|
func startExerciseTimerWith(duration: Int) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.currentExerciseTimer?.invalidate()
|
||||||
|
self.currentExerciseTimer = nil
|
||||||
|
self.currentExerciseTimeLeft = duration
|
||||||
|
self.currentExerciseTimer = Timer.scheduledTimer(timeInterval: 1,
|
||||||
|
target: self,
|
||||||
|
selector: #selector(self.updateCurrentExerciseTimer),
|
||||||
|
userInfo: nil,
|
||||||
|
repeats: true)
|
||||||
|
self.currentExerciseTimer?.fire()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func updateCurrentExerciseTimer() {
|
||||||
|
if currentExerciseTimeLeft > 1 {
|
||||||
|
currentExerciseTimeLeft -= 1
|
||||||
|
|
||||||
|
if let currentExercise = currentExerciseInfo.allSupersetExecercise, let audioQueues = currentExercise.audioQueues {
|
||||||
|
if let audioQueue = audioQueues.first(where: {
|
||||||
|
$0.playAt == currentExerciseTimeLeft
|
||||||
|
}) {
|
||||||
|
switch audioQueue.audioType {
|
||||||
|
|
||||||
|
case .shortBeep:
|
||||||
|
AudioEngine.playBeep()
|
||||||
|
case .finishBeep:
|
||||||
|
AudioEngine.playFinished()
|
||||||
|
case .remoteURL(let url):
|
||||||
|
AudioEngine.playRemoteAudio(fromURL: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendCurrentExerciseToWatch()
|
||||||
|
} else {
|
||||||
|
nextExercise()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
140
iphone/Werkout_ios/BridgeModule+Watch.swift
Normal file
140
iphone/Werkout_ios/BridgeModule+Watch.swift
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
//
|
||||||
|
// BridgeModule+Watch.swift
|
||||||
|
// Werkout_ios
|
||||||
|
//
|
||||||
|
// Created by Trey Tartt on 6/19/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import WatchConnectivity
|
||||||
|
import AVFoundation
|
||||||
|
import HealthKit
|
||||||
|
|
||||||
|
extension BridgeModule: WCSessionDelegate {
|
||||||
|
func sendResetToWatch() {
|
||||||
|
let watchModel = PhoneToWatchActions.reset
|
||||||
|
let data = try! JSONEncoder().encode(watchModel)
|
||||||
|
|
||||||
|
// user transferUserInfo b/c its guranteed to reach
|
||||||
|
// and end the workout
|
||||||
|
self.session.transferUserInfo(["package": data])
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendStartWorkoutToWatch() {
|
||||||
|
let model = PhoneToWatchActions.startWorkout
|
||||||
|
let data = try! JSONEncoder().encode(model)
|
||||||
|
|
||||||
|
// user transferUserInfo b/c its guranteed to reach
|
||||||
|
// and start the workout
|
||||||
|
self.session.transferUserInfo(["package": data])
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendWorkoutCompleteToWatch() {
|
||||||
|
let model = PhoneToWatchActions.endWorkout
|
||||||
|
let data = try! JSONEncoder().encode(model)
|
||||||
|
|
||||||
|
// user transferUserInfo b/c its guranteed to reach
|
||||||
|
// and end the workout
|
||||||
|
self.session.transferUserInfo(["package": data])
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendCurrentExerciseToWatch() {
|
||||||
|
if let currentExercise = currentExerciseInfo.currentExercise,
|
||||||
|
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 {
|
||||||
|
if let currentExercise = currentExerciseInfo.currentExercise,
|
||||||
|
let reps = currentExercise.reps,
|
||||||
|
reps > 0 {
|
||||||
|
|
||||||
|
// 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: currentExercise.exercise.name, currentTimeLeft: reps, 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 {
|
||||||
|
case .nextExercise:
|
||||||
|
nextExercise()
|
||||||
|
AudioEngine.playFinished()
|
||||||
|
case .workoutComplete(let data):
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data)
|
||||||
|
self.healthKitUUID = model.healthKitUUID
|
||||||
|
}
|
||||||
|
case .restartExercise:
|
||||||
|
restartExercise()
|
||||||
|
case .previousExercise:
|
||||||
|
previousExercise()
|
||||||
|
case .stopWorkout:
|
||||||
|
completeWorkout()
|
||||||
|
case .pauseWorkout:
|
||||||
|
pauseWorkout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func session(_ session: WCSession,
|
||||||
|
activationDidCompleteWith activationState: WCSessionActivationState,
|
||||||
|
error: Error?) {
|
||||||
|
switch activationState {
|
||||||
|
case .notActivated:
|
||||||
|
print("notActivated")
|
||||||
|
case .inactive:
|
||||||
|
print("inactive")
|
||||||
|
case .activated:
|
||||||
|
print("activated")
|
||||||
|
#if os(iOS)
|
||||||
|
let workoutConfiguration = HKWorkoutConfiguration()
|
||||||
|
workoutConfiguration.activityType = .functionalStrengthTraining
|
||||||
|
workoutConfiguration.locationType = .indoor
|
||||||
|
if WCSession.isSupported(), session.activationState == .activated, session.isWatchAppInstalled {
|
||||||
|
HKHealthStore().startWatchApp(with: workoutConfiguration, completion: { (success, error) in
|
||||||
|
print(error.debugDescription)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@unknown default:
|
||||||
|
print("default")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if os(iOS)
|
||||||
|
func sessionDidBecomeInactive(_ session: WCSession) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sessionDidDeactivate(_ session: WCSession) {
|
||||||
|
session.activate()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
func send(_ data: Data) {
|
||||||
|
guard session.activationState == .activated else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
#if os(iOS)
|
||||||
|
guard session.isWatchAppInstalled else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
guard session.isCompanionAppInstalled else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
session.sendMessageData(data, replyHandler: nil) { error in
|
||||||
|
print("Cannot send message: \(String(describing: error))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
iphone/Werkout_ios/BridgeModule+WorkoutActions.swift
Normal file
123
iphone/Werkout_ios/BridgeModule+WorkoutActions.swift
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
//
|
||||||
|
// BridgeModule+WorkoutActions.swift
|
||||||
|
// Werkout_ios
|
||||||
|
//
|
||||||
|
// Created by Trey Tartt on 6/21/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import WatchConnectivity
|
||||||
|
|
||||||
|
extension BridgeModule {
|
||||||
|
func pauseWorkout() {
|
||||||
|
if let _ = currentExerciseTimer {
|
||||||
|
currentExerciseTimer?.invalidate()
|
||||||
|
currentExerciseTimer = nil
|
||||||
|
isPaused = true
|
||||||
|
} else {
|
||||||
|
isPaused = false
|
||||||
|
startExerciseTimerWith(duration: currentExerciseTimeLeft)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextExercise() {
|
||||||
|
if let nextSupersetExercise = currentExerciseInfo.goToNextExercise {
|
||||||
|
updateCurrent(exercise: nextSupersetExercise)
|
||||||
|
} else {
|
||||||
|
completeWorkout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func previousExercise() {
|
||||||
|
if let nextSupersetExercise = currentExerciseInfo.previousExercise {
|
||||||
|
updateCurrent(exercise: nextSupersetExercise)
|
||||||
|
} else {
|
||||||
|
completeWorkout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restartExercise() {
|
||||||
|
if let currentExercise = currentExerciseInfo.currentExercise {
|
||||||
|
updateCurrent(exercise: currentExercise)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCurrent(exercise: SupersetExercise) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.currentExerciseTimer?.invalidate()
|
||||||
|
self.currentExerciseTimer = nil
|
||||||
|
|
||||||
|
if let duration = exercise.duration, duration > 0 {
|
||||||
|
self.startExerciseTimerWith(duration: duration)
|
||||||
|
}
|
||||||
|
self.sendCurrentExerciseToWatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func completeWorkout() {
|
||||||
|
self.currentExerciseTimer?.invalidate()
|
||||||
|
self.currentExerciseTimer = nil
|
||||||
|
self.isInWorkout = false
|
||||||
|
|
||||||
|
workoutEndDate = Date()
|
||||||
|
if let completedWorkout = completedWorkout {
|
||||||
|
completedWorkout()
|
||||||
|
self.completedWorkout = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(workout: Workout) {
|
||||||
|
currentExerciseInfo.complete = {
|
||||||
|
self.completeWorkout()
|
||||||
|
}
|
||||||
|
|
||||||
|
currentExerciseInfo.start(workout: workout)
|
||||||
|
currentWorkoutRunTimeInSeconds = 0
|
||||||
|
currentWorkoutRunTimer?.invalidate()
|
||||||
|
currentWorkoutRunTimer = nil
|
||||||
|
isPaused = false
|
||||||
|
|
||||||
|
sendStartWorkoutToWatch()
|
||||||
|
|
||||||
|
if let superetExercise = currentExerciseInfo.currentExercise {
|
||||||
|
updateCurrent(exercise: superetExercise)
|
||||||
|
startWorkoutTimer()
|
||||||
|
workoutStartDate = Date()
|
||||||
|
isInWorkout = true
|
||||||
|
|
||||||
|
if WCSession.isSupported() {
|
||||||
|
session.delegate = self
|
||||||
|
session.activate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func goToExerciseAt(section: Int, row: Int) {
|
||||||
|
if let superetExercise = currentExerciseInfo.goToWorkoutAt(supersetIndex: section,
|
||||||
|
exerciseIndex: row) {
|
||||||
|
updateCurrent(exercise: superetExercise)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextExerciseObject: SupersetExercise? {
|
||||||
|
currentExerciseInfo.goToNextExercise
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetCurrentWorkout() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.currentWorkoutRunTimeInSeconds = 0
|
||||||
|
self.currentWorkoutRunTimer?.invalidate()
|
||||||
|
self.currentWorkoutRunTimer = nil
|
||||||
|
|
||||||
|
self.currentExerciseTimer?.invalidate()
|
||||||
|
self.currentExerciseTimer = nil
|
||||||
|
|
||||||
|
self.currentWorkoutRunTimeInSeconds = -1
|
||||||
|
self.currentExerciseInfo.reset()
|
||||||
|
|
||||||
|
self.isInWorkout = false
|
||||||
|
self.workoutStartDate = nil
|
||||||
|
self.workoutEndDate = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ enum PhoneToWatchActions: Codable {
|
|||||||
case inExercise(WatchPackageModel)
|
case inExercise(WatchPackageModel)
|
||||||
case reset
|
case reset
|
||||||
case endWorkout
|
case endWorkout
|
||||||
|
case startWorkout
|
||||||
}
|
}
|
||||||
|
|
||||||
class BridgeModule: NSObject, ObservableObject {
|
class BridgeModule: NSObject, ObservableObject {
|
||||||
@@ -34,11 +35,11 @@ class BridgeModule: NSObject, ObservableObject {
|
|||||||
@Published var isInWorkout = false
|
@Published var isInWorkout = false
|
||||||
var completedWorkout: (() -> Void)?
|
var completedWorkout: (() -> Void)?
|
||||||
@Published var currentWorkoutRunTimeInSeconds: Int = -1
|
@Published var currentWorkoutRunTimeInSeconds: Int = -1
|
||||||
private var currentWorkoutRunTimer: Timer?
|
var currentWorkoutRunTimer: Timer?
|
||||||
|
|
||||||
public private(set) var workoutStartDate: Date?
|
public var workoutStartDate: Date?
|
||||||
|
|
||||||
private var currentExerciseTimer: Timer?
|
public var currentExerciseTimer: Timer?
|
||||||
|
|
||||||
@Published public private(set) var currentExerciseInfo = CurrentWorkoutInfo()
|
@Published public private(set) var currentExerciseInfo = CurrentWorkoutInfo()
|
||||||
@Published var previewWorkout: Workout?
|
@Published var previewWorkout: Workout?
|
||||||
@@ -48,346 +49,9 @@ class BridgeModule: NSObject, ObservableObject {
|
|||||||
private var isWatchConnected = false
|
private var isWatchConnected = false
|
||||||
// workoutEndDate fills out WatchPackageModel.workoutEndDate which
|
// workoutEndDate fills out WatchPackageModel.workoutEndDate which
|
||||||
// tells the watch app to stop the workout
|
// tells the watch app to stop the workout
|
||||||
public private(set) var workoutEndDate: Date?
|
public var workoutEndDate: Date?
|
||||||
@Published public private(set) var healthKitUUID: UUID?
|
@Published var healthKitUUID: UUID?
|
||||||
@Published var isPaused = false
|
@Published var isPaused = false
|
||||||
|
|
||||||
var audioPlayer: AVAudioPlayer?
|
let session: WCSession = WCSession.default
|
||||||
var avPlayer: AVPlayer?
|
|
||||||
private let session: WCSession = WCSession.default
|
|
||||||
|
|
||||||
func start(workout: Workout) {
|
|
||||||
currentExerciseInfo.complete = {
|
|
||||||
self.completeWorkout()
|
|
||||||
}
|
|
||||||
|
|
||||||
currentExerciseInfo.start(workout: workout)
|
|
||||||
currentWorkoutRunTimeInSeconds = 0
|
|
||||||
currentWorkoutRunTimer?.invalidate()
|
|
||||||
currentWorkoutRunTimer = nil
|
|
||||||
isPaused = false
|
|
||||||
|
|
||||||
if let superetExercise = currentExerciseInfo.currentExercise {
|
|
||||||
updateCurrent(exercise: superetExercise)
|
|
||||||
startWorkoutTimer()
|
|
||||||
workoutStartDate = Date()
|
|
||||||
isInWorkout = true
|
|
||||||
|
|
||||||
if WCSession.isSupported() {
|
|
||||||
session.delegate = self
|
|
||||||
session.activate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func goToExerciseAt(section: Int, row: Int) {
|
|
||||||
if let superetExercise = currentExerciseInfo.goToWorkoutAt(supersetIndex: section,
|
|
||||||
exerciseIndex: row) {
|
|
||||||
updateCurrent(exercise: superetExercise)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextExerciseObject: SupersetExercise? {
|
|
||||||
currentExerciseInfo.goToNextExercise
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetCurrentWorkout() {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.currentWorkoutRunTimeInSeconds = 0
|
|
||||||
self.currentWorkoutRunTimer?.invalidate()
|
|
||||||
self.currentWorkoutRunTimer = nil
|
|
||||||
|
|
||||||
self.currentExerciseTimer?.invalidate()
|
|
||||||
self.currentExerciseTimer = nil
|
|
||||||
|
|
||||||
self.currentWorkoutRunTimeInSeconds = -1
|
|
||||||
self.currentExerciseInfo.reset()
|
|
||||||
|
|
||||||
self.isInWorkout = false
|
|
||||||
self.workoutStartDate = nil
|
|
||||||
self.workoutEndDate = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func startWorkoutTimer() {
|
|
||||||
currentWorkoutRunTimer?.invalidate()
|
|
||||||
currentWorkoutRunTimer = nil
|
|
||||||
currentWorkoutRunTimer = Timer.scheduledTimer(timeInterval: 1,
|
|
||||||
target: self,
|
|
||||||
selector: #selector(addOneToWorkoutRunTime),
|
|
||||||
userInfo: nil,
|
|
||||||
repeats: true)
|
|
||||||
currentWorkoutRunTimer?.fire()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func startExerciseTimerWith(duration: Int) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.currentExerciseTimer?.invalidate()
|
|
||||||
self.currentExerciseTimer = nil
|
|
||||||
self.currentExerciseTimeLeft = duration
|
|
||||||
self.currentExerciseTimer = Timer.scheduledTimer(timeInterval: 1,
|
|
||||||
target: self,
|
|
||||||
selector: #selector(self.updateCurrentExerciseTimer),
|
|
||||||
userInfo: nil,
|
|
||||||
repeats: true)
|
|
||||||
self.currentExerciseTimer?.fire()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateCurrentExerciseTimer() {
|
|
||||||
if currentExerciseTimeLeft > 1 {
|
|
||||||
currentExerciseTimeLeft -= 1
|
|
||||||
|
|
||||||
if let currentExercise = currentExerciseInfo.allSupersetExecercise, let audioQueues = currentExercise.audioQueues {
|
|
||||||
if let audioQueue = audioQueues.first(where: {
|
|
||||||
$0.playAt == currentExerciseTimeLeft
|
|
||||||
}) {
|
|
||||||
switch audioQueue.audioType {
|
|
||||||
|
|
||||||
case .shortBeep:
|
|
||||||
playBeep()
|
|
||||||
case .finishBeep:
|
|
||||||
playFinished()
|
|
||||||
case .remoteURL(let url):
|
|
||||||
playRemoteAudio(fromURL: url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sendCurrentExerciseToWatch()
|
|
||||||
} else {
|
|
||||||
nextExercise()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pauseWorkout() {
|
|
||||||
if let _ = currentExerciseTimer {
|
|
||||||
currentExerciseTimer?.invalidate()
|
|
||||||
currentExerciseTimer = nil
|
|
||||||
isPaused = true
|
|
||||||
} else {
|
|
||||||
isPaused = false
|
|
||||||
startExerciseTimerWith(duration: currentExerciseTimeLeft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextExercise() {
|
|
||||||
if let nextSupersetExercise = currentExerciseInfo.goToNextExercise {
|
|
||||||
updateCurrent(exercise: nextSupersetExercise)
|
|
||||||
} else {
|
|
||||||
completeWorkout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func previousExercise() {
|
|
||||||
if let nextSupersetExercise = currentExerciseInfo.previousExercise {
|
|
||||||
updateCurrent(exercise: nextSupersetExercise)
|
|
||||||
} else {
|
|
||||||
completeWorkout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func restartExercise() {
|
|
||||||
if let currentExercise = currentExerciseInfo.currentExercise {
|
|
||||||
updateCurrent(exercise: currentExercise)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func addOneToWorkoutRunTime() {
|
|
||||||
currentWorkoutRunTimeInSeconds += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateCurrent(exercise: SupersetExercise) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.currentExerciseTimer?.invalidate()
|
|
||||||
self.currentExerciseTimer = nil
|
|
||||||
|
|
||||||
if let duration = exercise.duration, duration > 0 {
|
|
||||||
self.startExerciseTimerWith(duration: duration)
|
|
||||||
}
|
|
||||||
self.sendCurrentExerciseToWatch()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func completeWorkout() {
|
|
||||||
self.currentExerciseTimer?.invalidate()
|
|
||||||
self.currentExerciseTimer = nil
|
|
||||||
self.isInWorkout = false
|
|
||||||
|
|
||||||
workoutEndDate = Date()
|
|
||||||
if let completedWorkout = completedWorkout {
|
|
||||||
completedWorkout()
|
|
||||||
self.completedWorkout = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func playRemoteAudio(fromURL url: URL) {
|
|
||||||
#if os(iOS)
|
|
||||||
let playerItem = AVPlayerItem(url: url)
|
|
||||||
do {
|
|
||||||
try AVAudioSession.sharedInstance().setCategory(.playback,
|
|
||||||
mode: .default,
|
|
||||||
options: [.mixWithOthers])
|
|
||||||
try AVAudioSession.sharedInstance().setActive(true)
|
|
||||||
|
|
||||||
avPlayer = AVPlayer(playerItem: playerItem)
|
|
||||||
avPlayer?.play()
|
|
||||||
} catch {
|
|
||||||
print("ERROR")
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func playBeep() {
|
|
||||||
#if os(iOS)
|
|
||||||
if let path = Bundle.main.path(forResource: "short_beep", ofType: "m4a") {
|
|
||||||
do {
|
|
||||||
try AVAudioSession.sharedInstance().setCategory(.playback,
|
|
||||||
mode: .default,
|
|
||||||
options: [.mixWithOthers])
|
|
||||||
try AVAudioSession.sharedInstance().setActive(true)
|
|
||||||
|
|
||||||
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
|
|
||||||
audioPlayer?.play()
|
|
||||||
} catch {
|
|
||||||
print("ERROR")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func playFinished() {
|
|
||||||
#if os(iOS)
|
|
||||||
if let path = Bundle.main.path(forResource: "long_beep", ofType: "m4a") {
|
|
||||||
do {
|
|
||||||
try AVAudioSession.sharedInstance().setCategory(.playback,
|
|
||||||
mode: .default,
|
|
||||||
options: [.mixWithOthers])
|
|
||||||
try AVAudioSession.sharedInstance().setActive(true)
|
|
||||||
|
|
||||||
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
|
|
||||||
audioPlayer?.play()
|
|
||||||
} catch {
|
|
||||||
print("ERROR")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension BridgeModule: WCSessionDelegate {
|
|
||||||
func sendResetToWatch() {
|
|
||||||
let watchModel = PhoneToWatchActions.reset
|
|
||||||
let data = try! JSONEncoder().encode(watchModel)
|
|
||||||
|
|
||||||
// user transferUserInfo b/c its guranteed to reach
|
|
||||||
// and end the workout
|
|
||||||
self.session.transferUserInfo(["package": data])
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendWorkoutCompleteToWatch() {
|
|
||||||
let model = PhoneToWatchActions.endWorkout
|
|
||||||
let data = try! JSONEncoder().encode(model)
|
|
||||||
|
|
||||||
// user transferUserInfo b/c its guranteed to reach
|
|
||||||
// and end the workout
|
|
||||||
self.session.transferUserInfo(["package": data])
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendCurrentExerciseToWatch() {
|
|
||||||
if let currentExercise = currentExerciseInfo.currentExercise,
|
|
||||||
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 {
|
|
||||||
if let currentExercise = currentExerciseInfo.currentExercise,
|
|
||||||
let reps = currentExercise.reps,
|
|
||||||
reps > 0 {
|
|
||||||
|
|
||||||
// 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: currentExercise.exercise.name, currentTimeLeft: reps, 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 {
|
|
||||||
case .nextExercise:
|
|
||||||
nextExercise()
|
|
||||||
playFinished()
|
|
||||||
case .workoutComplete(let data):
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data)
|
|
||||||
self.healthKitUUID = model.healthKitUUID
|
|
||||||
}
|
|
||||||
case .restartExercise:
|
|
||||||
restartExercise()
|
|
||||||
case .previousExercise:
|
|
||||||
previousExercise()
|
|
||||||
case .stopWorkout:
|
|
||||||
completeWorkout()
|
|
||||||
case .pauseWorkout:
|
|
||||||
pauseWorkout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func session(_ session: WCSession,
|
|
||||||
activationDidCompleteWith activationState: WCSessionActivationState,
|
|
||||||
error: Error?) {
|
|
||||||
switch activationState {
|
|
||||||
case .notActivated:
|
|
||||||
print("notActivated")
|
|
||||||
case .inactive:
|
|
||||||
print("inactive")
|
|
||||||
case .activated:
|
|
||||||
print("activated")
|
|
||||||
#if os(iOS)
|
|
||||||
let workoutConfiguration = HKWorkoutConfiguration()
|
|
||||||
workoutConfiguration.activityType = .functionalStrengthTraining
|
|
||||||
workoutConfiguration.locationType = .indoor
|
|
||||||
if WCSession.isSupported(), session.activationState == .activated, session.isWatchAppInstalled {
|
|
||||||
HKHealthStore().startWatchApp(with: workoutConfiguration, completion: { (success, error) in
|
|
||||||
print(error.debugDescription)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@unknown default:
|
|
||||||
print("default")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if os(iOS)
|
|
||||||
func sessionDidBecomeInactive(_ session: WCSession) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func sessionDidDeactivate(_ session: WCSession) {
|
|
||||||
session.activate()
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
func send(_ data: Data) {
|
|
||||||
guard session.activationState == .activated else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
#if os(iOS)
|
|
||||||
guard session.isWatchAppInstalled else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
guard session.isCompanionAppInstalled else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
session.sendMessageData(data, replyHandler: nil) { error in
|
|
||||||
print("Cannot send message: \(String(describing: error))")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,6 +172,14 @@ struct WorkoutDetailView: View {
|
|||||||
avPlayer.play()
|
avPlayer.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridgeModule.completedWorkout = {
|
||||||
|
if let workoutData = createWorkoutData() {
|
||||||
|
workoutComplete = .completedWorkout(workoutData)
|
||||||
|
} else {
|
||||||
|
bridgeModule.resetCurrentWorkout()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onReceive(NotificationCenter.default.publisher(
|
.onReceive(NotificationCenter.default.publisher(
|
||||||
for: UIScene.willEnterForegroundNotification)) { _ in
|
for: UIScene.willEnterForegroundNotification)) { _ in
|
||||||
@@ -194,14 +202,6 @@ struct WorkoutDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startWorkout(workout: Workout) {
|
func startWorkout(workout: Workout) {
|
||||||
bridgeModule.completedWorkout = {
|
|
||||||
if let workoutData = createWorkoutData() {
|
|
||||||
workoutComplete = .completedWorkout(workoutData)
|
|
||||||
} else {
|
|
||||||
bridgeModule.resetCurrentWorkout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bridgeModule.start(workout: workout)
|
bridgeModule.start(workout: workout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ extension WatchMainViewModel {
|
|||||||
}
|
}
|
||||||
isInWorkout = true
|
isInWorkout = true
|
||||||
} else {
|
} else {
|
||||||
print("didn not init workout")
|
print("did not init workout")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ class WatchMainViewModel: NSObject, ObservableObject {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
switch model {
|
switch model {
|
||||||
case .inExercise(let data):
|
case .inExercise(let data):
|
||||||
if self.isInWorkout == false {
|
|
||||||
self.startWorkout()
|
|
||||||
}
|
|
||||||
self.watchPackageModel = data
|
self.watchPackageModel = data
|
||||||
case .reset:
|
case .reset:
|
||||||
self.isInWorkout = false
|
self.isInWorkout = false
|
||||||
@@ -99,6 +96,11 @@ class WatchMainViewModel: NSObject, ObservableObject {
|
|||||||
self.isInWorkout = false
|
self.isInWorkout = false
|
||||||
self.watchPackageModel = WatchMainViewModel.defualtPackageModle
|
self.watchPackageModel = WatchMainViewModel.defualtPackageModle
|
||||||
self.stopWorkout(sendDetails: true)
|
self.stopWorkout(sendDetails: true)
|
||||||
|
case .startWorkout:
|
||||||
|
if self.isInWorkout {
|
||||||
|
self.stopWorkout(sendDetails: false)
|
||||||
|
}
|
||||||
|
self.startWorkout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user