From 6a35dc7242115ab0d9c664d805c1ae6337a02ae1 Mon Sep 17 00:00:00 2001 From: Josh Cook Date: Fri, 29 Jan 2021 07:37:39 -0500 Subject: [PATCH 1/4] feat: object to manage haptics settings and playing --- .../Models/HapticsSettings.swift | 59 +++++++++++++++++++ ...el.swift => ExercisePickerViewModel.swift} | 0 2 files changed, 59 insertions(+) create mode 100644 Workout Spinner WatchKit Extension/Models/HapticsSettings.swift rename Workout Spinner WatchKit Extension/View Models/{WorkoutPickerViewModel.swift => ExercisePickerViewModel.swift} (100%) diff --git a/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift b/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift new file mode 100644 index 0000000..778e1b3 --- /dev/null +++ b/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift @@ -0,0 +1,59 @@ +// +// HapticsSettings.swift +// Workout Spinner WatchKit Extension +// +// Created by Joshua on 1/29/21. +// Copyright © 2021 Joshua Cook. All rights reserved. +// + +import SwiftUI +import WatchKit + +class HapticsSettings: ObservableObject { + private(set) var successfulWheelSpin: Bool + private(set) var startExercise: Bool + private(set) var endExercise: Bool + + init() { + HapticsSettings.checkInitialRegistration() + successfulWheelSpin = HapticsSettings.setting(for: .successfulWheelSpin) + startExercise = HapticsSettings.setting(for: .startExercise) + endExercise = HapticsSettings.setting(for: .endExercise) + } + + enum HapticSetting: String, CaseIterable { + case successfulWheelSpin + case startExercise + case endExercise + } + + public func save(_ setting: HapticSetting, as value: Bool) { + UserDefaults.standard.setValue(value, forKey: setting.rawValue) + } + + internal static func setting(for setting: HapticSetting) -> Bool { + UserDefaults.standard.bool(forKey: setting.rawValue) + } + + private static func checkInitialRegistration() { + let initialCheckKey = "HapticSettingsInitialCheck" + if UserDefaults.standard.bool(forKey: initialCheckKey) { return } + for key in HapticSetting.allCases { + UserDefaults.standard.setValue(true, forKey: key.rawValue) + } + UserDefaults.standard.setValue(true, forKey: initialCheckKey) + } + + public func play(soundFor action: HapticSetting, ifSet playHaptic: Bool) { + if !playHaptic { return } + + switch action { + case .successfulWheelSpin: + WKInterfaceDevice.current().play(.success) + case .startExercise: + WKInterfaceDevice.current().play(.start) + case .endExercise: + WKInterfaceDevice.current().play(.stop) + } + } +} diff --git a/Workout Spinner WatchKit Extension/View Models/WorkoutPickerViewModel.swift b/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift similarity index 100% rename from Workout Spinner WatchKit Extension/View Models/WorkoutPickerViewModel.swift rename to Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift From 312dfb0dd950f99a927e7c0405a76459429d41db Mon Sep 17 00:00:00 2001 From: Josh Cook Date: Fri, 29 Jan 2021 07:38:05 -0500 Subject: [PATCH 2/4] feat: insert haptics into key locations of app --- .../Models/WheelVelocityTracker.swift | 17 ++++------------- .../View Models/ExercisePickerViewModel.swift | 1 + .../Views/ContentView.swift | 2 ++ .../Views/ExercisePicker.swift | 3 ++- .../Views/ExerciseStartView.swift | 4 ++++ .../Views/ExerciseView.swift | 5 +++++ Workout Spinner.xcodeproj/project.pbxproj | 12 ++++++++---- 7 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Workout Spinner WatchKit Extension/Models/WheelVelocityTracker.swift b/Workout Spinner WatchKit Extension/Models/WheelVelocityTracker.swift index 4520fad..1da71ce 100644 --- a/Workout Spinner WatchKit Extension/Models/WheelVelocityTracker.swift +++ b/Workout Spinner WatchKit Extension/Models/WheelVelocityTracker.swift @@ -7,25 +7,16 @@ // import Foundation +import WatchKit class WheelVelocityTracker: ObservableObject { + let velocityThreshold: Double + let memory: Int private var history = [Double]() - var velocityThreshold: Double = 50.0 private(set) var didPassThreshold: Bool = false - var memory: Int = 10 private(set) var currentVelocity = 0.0 - init() {} - - init(memory: Int) { - self.memory = memory - } - - init(velocityThreshold: Double) { - self.velocityThreshold = velocityThreshold - } - - init(velocityThreshold: Double, memory: Int) { + init(velocityThreshold: Double = 5, memory: Int = 3) { self.velocityThreshold = velocityThreshold self.memory = memory } diff --git a/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift b/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift index c568619..eb052c3 100644 --- a/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift +++ b/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift @@ -19,6 +19,7 @@ extension ExercisePicker { func rotationEffectDidFinish() { if velocityTracker.didPassThreshold { + hapticsSettings.play(soundFor: .successfulWheelSpin, ifSet: hapticsSettings.successfulWheelSpin) exerciseSelected = true velocityTracker.resetThreshold() } diff --git a/Workout Spinner WatchKit Extension/Views/ContentView.swift b/Workout Spinner WatchKit Extension/Views/ContentView.swift index 10d9011..a51448c 100644 --- a/Workout Spinner WatchKit Extension/Views/ContentView.swift +++ b/Workout Spinner WatchKit Extension/Views/ContentView.swift @@ -12,9 +12,11 @@ struct ContentView: View { let workoutManager = WorkoutManager() let workoutTracker = WorkoutTracker() let exerciseOptions = ExerciseOptions() + let hapticsSettings = HapticsSettings() var body: some View { WelcomeView(workoutManager: workoutManager, workoutTracker: workoutTracker, exerciseOptions: exerciseOptions) + .environmentObject(hapticsSettings) } } diff --git a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift index 2c788ad..22483b9 100644 --- a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift +++ b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift @@ -16,6 +16,7 @@ struct ExercisePicker: View { // Global objects. @ObservedObject var workoutManager: WorkoutManager @ObservedObject var exerciseOptions: ExerciseOptions + @EnvironmentObject var hapticsSettings: HapticsSettings var numExercises: Int { exerciseOptions.exercisesBlacklistFiltered.count @@ -29,7 +30,7 @@ struct ExercisePicker: View { // Spinning wheel constants. var spinDirection: Double { - return WKInterfaceDevice().crownOrientation == .left ? 1.0 : -1.0 + return WKInterfaceDevice.current().crownOrientation == .left ? 1.0 : -1.0 } internal var crownVelocityMultiplier = UserDefaults.readCrownVelocityMultiplier() diff --git a/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift b/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift index fc669e4..a0b2ada 100644 --- a/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift +++ b/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift @@ -7,9 +7,11 @@ // import SwiftUI +import WatchKit struct ExerciseStartView: View { @ObservedObject var workoutManager: WorkoutManager + @EnvironmentObject var hapticsSettings: HapticsSettings @Binding private var exerciseCanceled: Bool // Workout information @@ -75,6 +77,8 @@ struct ExerciseStartView: View { if self.timeRemaining > 0 { self.timeRemaining -= 1 } else if self.timeRemaining <= 0 { + // haptic feedback + hapticsSettings.play(soundFor: .endExercise, ifSet: hapticsSettings.endExercise) exerciseCanceled = false self.presentationMode.wrappedValue.dismiss() } diff --git a/Workout Spinner WatchKit Extension/Views/ExerciseView.swift b/Workout Spinner WatchKit Extension/Views/ExerciseView.swift index 5b99129..3564cde 100644 --- a/Workout Spinner WatchKit Extension/Views/ExerciseView.swift +++ b/Workout Spinner WatchKit Extension/Views/ExerciseView.swift @@ -7,10 +7,12 @@ // import SwiftUI +import WatchKit struct ExerciseView: View { @ObservedObject var workoutManager: WorkoutManager @ObservedObject var workoutTracker: WorkoutTracker + @EnvironmentObject var hapticsSettings: HapticsSettings @Binding private var exerciseComplete: Bool var workoutInfo: ExerciseInfo? @@ -101,6 +103,9 @@ struct ExerciseView: View { extension ExerciseView { /// Called when the exercise is complete and the 'Done" button is tapped. func finishExercise() { + // haptic feedback + hapticsSettings.play(soundFor: .startExercise, ifSet: hapticsSettings.startExercise) + // Add data from exercise to the workout tracker and clear the info from the workout manager. workoutTracker.addData(info: workoutManager.exerciseInfo!, duration: Double(workoutManager.elapsedSeconds), activeCalories: workoutManager.activeCalories, heartRate: workoutManager.allHeartRateReadings) workoutManager.resetTrackedInformation() diff --git a/Workout Spinner.xcodeproj/project.pbxproj b/Workout Spinner.xcodeproj/project.pbxproj index ad8ee27..98f4c62 100644 --- a/Workout Spinner.xcodeproj/project.pbxproj +++ b/Workout Spinner.xcodeproj/project.pbxproj @@ -7,9 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 8404613625C4310900BFDB80 /* HapticsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8404613525C4310900BFDB80 /* HapticsSettings.swift */; }; 840D3F65254C1D6200C9E2EF /* WorkoutFinishView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D3F64254C1D6200C9E2EF /* WorkoutFinishView.swift */; }; 841D04D1250648AB00786074 /* ExerciseStartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D0250648AB00786074 /* ExerciseStartView.swift */; }; - 841D04D325064A8800786074 /* WorkoutPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D225064A8800786074 /* WorkoutPickerViewModel.swift */; }; + 841D04D325064A8800786074 /* ExercisePickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D225064A8800786074 /* ExercisePickerViewModel.swift */; }; 841D04D525064DE600786074 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D425064DE600786074 /* Settings.swift */; }; 841D04D925066CF400786074 /* UserDefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D825066CF400786074 /* UserDefaultsKeys.swift */; }; 841D04DB250673DB00786074 /* BodyPartSelectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04DA250673DB00786074 /* BodyPartSelectionListView.swift */; }; @@ -110,9 +111,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8404613525C4310900BFDB80 /* HapticsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticsSettings.swift; sourceTree = ""; }; 840D3F64254C1D6200C9E2EF /* WorkoutFinishView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutFinishView.swift; sourceTree = ""; }; 841D04D0250648AB00786074 /* ExerciseStartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExerciseStartView.swift; sourceTree = ""; }; - 841D04D225064A8800786074 /* WorkoutPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutPickerViewModel.swift; sourceTree = ""; }; + 841D04D225064A8800786074 /* ExercisePickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExercisePickerViewModel.swift; sourceTree = ""; }; 841D04D425064DE600786074 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; 841D04D825066CF400786074 /* UserDefaultsKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsKeys.swift; sourceTree = ""; }; 841D04DA250673DB00786074 /* BodyPartSelectionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyPartSelectionListView.swift; sourceTree = ""; }; @@ -348,6 +350,7 @@ 8483502825403C010081D374 /* WorkoutManager.swift */, 84C1FA7B25497CD20008CE09 /* WorkoutTracker.swift */, 84E9C1002554234D002D461B /* HeartRateGraphData.swift */, + 8404613525C4310900BFDB80 /* HapticsSettings.swift */, ); path = Models; sourceTree = ""; @@ -355,7 +358,7 @@ 84E372B424FD0AD500AFB355 /* View Models */ = { isa = PBXGroup; children = ( - 841D04D225064A8800786074 /* WorkoutPickerViewModel.swift */, + 841D04D225064A8800786074 /* ExercisePickerViewModel.swift */, ); path = "View Models"; sourceTree = ""; @@ -546,6 +549,7 @@ 84E3729624FD0A5D00AFB355 /* HostingController.swift in Sources */, 849C7325255EA64400C28F96 /* LabelWithIndicator.swift in Sources */, 849458F12503B86200C361F7 /* SpinnerRotationModifier.swift in Sources */, + 8404613625C4310900BFDB80 /* HapticsSettings.swift in Sources */, 843F33EE24FFB2E500919AD5 /* Color-extension.swift in Sources */, 84C1FA7C25497CD20008CE09 /* WorkoutTracker.swift in Sources */, 848642F52500FA3E00E3ED9B /* SpinnerSlice.swift in Sources */, @@ -564,7 +568,7 @@ 84E9C0ED25542309002D461B /* GraphBackgroundSegment.swift in Sources */, 84EFD6792508E32500806EF4 /* BodyPartSelections.swift in Sources */, 84C1FA7425497A050008CE09 /* WorkoutPagingView.swift in Sources */, - 841D04D325064A8800786074 /* WorkoutPickerViewModel.swift in Sources */, + 841D04D325064A8800786074 /* ExercisePickerViewModel.swift in Sources */, 84E9C0E8255422DE002D461B /* Double-extension.swift in Sources */, 84C10C1F2588D3B500111502 /* CannotSpinView.swift in Sources */, 848364E82565438800B357FA /* InstructionsView.swift in Sources */, From c687796be1516191763c6bcdc2618f3dfea442dd Mon Sep 17 00:00:00 2001 From: Josh Cook Date: Mon, 1 Feb 2021 06:10:49 -0500 Subject: [PATCH 3/4] feat: Haptics manager and feedback for exercises - light-weight struct for managing Haptics - add feedback to: - successsful wheelspin - start an exercise - end exercise --- .../Models/HapticsSettings.swift | 31 ++++++++++--------- .../View Models/ExercisePickerViewModel.swift | 2 +- .../Views/ContentView.swift | 2 -- .../Views/ExercisePicker.swift | 3 +- .../Views/ExerciseStartView.swift | 6 ++-- .../Views/ExerciseView.swift | 15 +++++---- 6 files changed, 31 insertions(+), 28 deletions(-) diff --git a/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift b/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift index 778e1b3..dcca96a 100644 --- a/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift +++ b/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift @@ -9,16 +9,13 @@ import SwiftUI import WatchKit -class HapticsSettings: ObservableObject { - private(set) var successfulWheelSpin: Bool - private(set) var startExercise: Bool - private(set) var endExercise: Bool +struct HapticsSettings { + private(set) var successfulWheelSpin: Bool = setting(for: .successfulWheelSpin) + private(set) var startExercise: Bool = setting(for: .startExercise) + private(set) var endExercise: Bool = setting(for: .endExercise) init() { - HapticsSettings.checkInitialRegistration() - successfulWheelSpin = HapticsSettings.setting(for: .successfulWheelSpin) - startExercise = HapticsSettings.setting(for: .startExercise) - endExercise = HapticsSettings.setting(for: .endExercise) + checkInitialRegistration() } enum HapticSetting: String, CaseIterable { @@ -35,7 +32,7 @@ class HapticsSettings: ObservableObject { UserDefaults.standard.bool(forKey: setting.rawValue) } - private static func checkInitialRegistration() { + private func checkInitialRegistration() { let initialCheckKey = "HapticSettingsInitialCheck" if UserDefaults.standard.bool(forKey: initialCheckKey) { return } for key in HapticSetting.allCases { @@ -44,16 +41,20 @@ class HapticsSettings: ObservableObject { UserDefaults.standard.setValue(true, forKey: initialCheckKey) } - public func play(soundFor action: HapticSetting, ifSet playHaptic: Bool) { - if !playHaptic { return } - + public func play(soundFor action: HapticSetting) { switch action { case .successfulWheelSpin: - WKInterfaceDevice.current().play(.success) + if successfulWheelSpin { + WKInterfaceDevice.current().play(.success) + } case .startExercise: - WKInterfaceDevice.current().play(.start) + if startExercise { + WKInterfaceDevice.current().play(.start) + } case .endExercise: - WKInterfaceDevice.current().play(.stop) + if endExercise { + WKInterfaceDevice.current().play(.stop) + } } } } diff --git a/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift b/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift index eb052c3..14d49a8 100644 --- a/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift +++ b/Workout Spinner WatchKit Extension/View Models/ExercisePickerViewModel.swift @@ -19,7 +19,7 @@ extension ExercisePicker { func rotationEffectDidFinish() { if velocityTracker.didPassThreshold { - hapticsSettings.play(soundFor: .successfulWheelSpin, ifSet: hapticsSettings.successfulWheelSpin) + haptics.play(soundFor: .successfulWheelSpin) exerciseSelected = true velocityTracker.resetThreshold() } diff --git a/Workout Spinner WatchKit Extension/Views/ContentView.swift b/Workout Spinner WatchKit Extension/Views/ContentView.swift index a51448c..10d9011 100644 --- a/Workout Spinner WatchKit Extension/Views/ContentView.swift +++ b/Workout Spinner WatchKit Extension/Views/ContentView.swift @@ -12,11 +12,9 @@ struct ContentView: View { let workoutManager = WorkoutManager() let workoutTracker = WorkoutTracker() let exerciseOptions = ExerciseOptions() - let hapticsSettings = HapticsSettings() var body: some View { WelcomeView(workoutManager: workoutManager, workoutTracker: workoutTracker, exerciseOptions: exerciseOptions) - .environmentObject(hapticsSettings) } } diff --git a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift index 22483b9..7fd9a3a 100644 --- a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift +++ b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift @@ -16,7 +16,8 @@ struct ExercisePicker: View { // Global objects. @ObservedObject var workoutManager: WorkoutManager @ObservedObject var exerciseOptions: ExerciseOptions - @EnvironmentObject var hapticsSettings: HapticsSettings + + let haptics = HapticsSettings() var numExercises: Int { exerciseOptions.exercisesBlacklistFiltered.count diff --git a/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift b/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift index a0b2ada..8947145 100644 --- a/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift +++ b/Workout Spinner WatchKit Extension/Views/ExerciseStartView.swift @@ -11,7 +11,6 @@ import WatchKit struct ExerciseStartView: View { @ObservedObject var workoutManager: WorkoutManager - @EnvironmentObject var hapticsSettings: HapticsSettings @Binding private var exerciseCanceled: Bool // Workout information @@ -34,6 +33,8 @@ struct ExerciseStartView: View { return intensity.rawValue } + let haptics = HapticsSettings() + @Environment(\.presentationMode) var presentationMode init(workoutManager: WorkoutManager, exerciseCanceled: Binding) { @@ -77,8 +78,7 @@ struct ExerciseStartView: View { if self.timeRemaining > 0 { self.timeRemaining -= 1 } else if self.timeRemaining <= 0 { - // haptic feedback - hapticsSettings.play(soundFor: .endExercise, ifSet: hapticsSettings.endExercise) + haptics.play(soundFor: .startExercise) // haptic feedback exerciseCanceled = false self.presentationMode.wrappedValue.dismiss() } diff --git a/Workout Spinner WatchKit Extension/Views/ExerciseView.swift b/Workout Spinner WatchKit Extension/Views/ExerciseView.swift index 3564cde..6dee40f 100644 --- a/Workout Spinner WatchKit Extension/Views/ExerciseView.swift +++ b/Workout Spinner WatchKit Extension/Views/ExerciseView.swift @@ -12,10 +12,9 @@ import WatchKit struct ExerciseView: View { @ObservedObject var workoutManager: WorkoutManager @ObservedObject var workoutTracker: WorkoutTracker - @EnvironmentObject var hapticsSettings: HapticsSettings @Binding private var exerciseComplete: Bool var workoutInfo: ExerciseInfo? - + let haptics = HapticsSettings() let intensity: ExerciseIntensity = ExerciseStartView.loadExerciseIntensity() init(workoutManager: WorkoutManager, workoutTracker: WorkoutTracker, exerciseComplete: Binding) { @@ -103,11 +102,13 @@ struct ExerciseView: View { extension ExerciseView { /// Called when the exercise is complete and the 'Done" button is tapped. func finishExercise() { - // haptic feedback - hapticsSettings.play(soundFor: .startExercise, ifSet: hapticsSettings.startExercise) + haptics.play(soundFor: .endExercise) // haptic feedback // Add data from exercise to the workout tracker and clear the info from the workout manager. - workoutTracker.addData(info: workoutManager.exerciseInfo!, duration: Double(workoutManager.elapsedSeconds), activeCalories: workoutManager.activeCalories, heartRate: workoutManager.allHeartRateReadings) + workoutTracker.addData(info: workoutManager.exerciseInfo!, + duration: Double(workoutManager.elapsedSeconds), + activeCalories: workoutManager.activeCalories, + heartRate: workoutManager.allHeartRateReadings) workoutManager.resetTrackedInformation() exerciseComplete = true @@ -127,7 +128,9 @@ extension ExerciseView { static var previews: some View { Group { ForEach(workoutOptions.exercises) { info in - ExerciseView(workoutManager: WorkoutManager(exerciseInfo: info), workoutTracker: WorkoutTracker(), exerciseComplete: .constant(false)) + ExerciseView(workoutManager: WorkoutManager(exerciseInfo: info), + workoutTracker: WorkoutTracker(), + exerciseComplete: .constant(false)) .previewDisplayName(info.displayName) } } From 2605b4ad92df65966b86b7a0c1fb2a1913224125 Mon Sep 17 00:00:00 2001 From: Josh Cook Date: Mon, 1 Feb 2021 06:28:21 -0500 Subject: [PATCH 4/4] feat: Control haptics in settings - a single toggle for each instance of haptic feedback --- .../Models/HapticsSettings.swift | 12 +++++++++--- .../Views/Settings Views/Settings.swift | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift b/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift index dcca96a..234d613 100644 --- a/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift +++ b/Workout Spinner WatchKit Extension/Models/HapticsSettings.swift @@ -10,9 +10,9 @@ import SwiftUI import WatchKit struct HapticsSettings { - private(set) var successfulWheelSpin: Bool = setting(for: .successfulWheelSpin) - private(set) var startExercise: Bool = setting(for: .startExercise) - private(set) var endExercise: Bool = setting(for: .endExercise) + var successfulWheelSpin: Bool = setting(for: .successfulWheelSpin) + var startExercise: Bool = setting(for: .startExercise) + var endExercise: Bool = setting(for: .endExercise) init() { checkInitialRegistration() @@ -24,6 +24,12 @@ struct HapticsSettings { case endExercise } + public func saveAll() { + save(.successfulWheelSpin, as: successfulWheelSpin) + save(.startExercise, as: startExercise) + save(.endExercise, as: endExercise) + } + public func save(_ setting: HapticSetting, as value: Bool) { UserDefaults.standard.setValue(value, forKey: setting.rawValue) } diff --git a/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift b/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift index 01dc7cb..013f21d 100644 --- a/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift +++ b/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift @@ -34,6 +34,8 @@ struct Settings: View { self.exerciseOptions = exerciseOptions } + @State private var haptics = HapticsSettings() + var body: some View { Form { Section(header: SectionHeader(imageName: "figure.wave", text: "Preferences")) { @@ -72,6 +74,18 @@ struct Settings: View { PlusMinusStepper(value: $crownVelocityMultiplier, step: 1, min: 1, max: 10, label: Text("\(Int(crownVelocityMultiplier))")) } + Section(header: SectionHeader(imageName: "applewatch.radiowaves.left.and.right", text: "Haptics")) { + Toggle(isOn: $haptics.successfulWheelSpin) { + Text("Wheel spin") + } + Toggle(isOn: $haptics.startExercise) { + Text("Begin exercise") + } + Toggle(isOn: $haptics.endExercise) { + Text("End exercise") + } + } + Section(header: SectionHeader(imageName: "info.circle", text: "About")) { HStack { Text("Dev") @@ -120,6 +134,8 @@ extension Settings { UserDefaults.standard.set(crownVelocityMultiplier, forKey: UserDefaultsKeys.crownVelocityMultiplier.rawValue) + + haptics.saveAll() } static func getSavedExerciseIntensity() -> Int {