From 9dc7a36cc6ed9aa3397f5d8c45bdb445eb0932ec Mon Sep 17 00:00:00 2001 From: Josh Cook Date: Thu, 10 Dec 2020 06:36:51 -0500 Subject: [PATCH 1/3] log: add logging to WorkoutManager --- Workout Spinner WatchKit App/Info.plist | 2 +- Workout Spinner WatchKit Extension/Info.plist | 2 +- .../Logging/Logger.swift | 17 +++++++++ .../Models/ExerciseInfo.swift | 7 ++++ .../Models/ExerciseOptions.swift | 20 +++++++++-- .../Models/HeartRateGraphData.swift | 32 +++++++++-------- .../Models/WorkoutManager.swift | 35 +++++++++++++------ Workout Spinner.xcodeproj/project.pbxproj | 12 +++++++ 8 files changed, 97 insertions(+), 30 deletions(-) create mode 100644 Workout Spinner WatchKit Extension/Logging/Logger.swift diff --git a/Workout Spinner WatchKit App/Info.plist b/Workout Spinner WatchKit App/Info.plist index fc31801..c5e18c5 100644 --- a/Workout Spinner WatchKit App/Info.plist +++ b/Workout Spinner WatchKit App/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 376 + 380 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/Workout Spinner WatchKit Extension/Info.plist b/Workout Spinner WatchKit Extension/Info.plist index 6249840..256ea8b 100644 --- a/Workout Spinner WatchKit Extension/Info.plist +++ b/Workout Spinner WatchKit Extension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 376 + 380 CLKComplicationPrincipalClass $(PRODUCT_MODULE_NAME).ComplicationController CLKComplicationSupportedFamilies diff --git a/Workout Spinner WatchKit Extension/Logging/Logger.swift b/Workout Spinner WatchKit Extension/Logging/Logger.swift new file mode 100644 index 0000000..65a80f7 --- /dev/null +++ b/Workout Spinner WatchKit Extension/Logging/Logger.swift @@ -0,0 +1,17 @@ +// +// Logger.swift +// Workout Spinner WatchKit Extension +// +// Created by Joshua on 12/10/20. +// Copyright © 2020 Joshua Cook. All rights reserved. +// + +import Foundation +import os + +extension Logger { + static let mySubsystem = "com.joshuacook.Workout-Spinner" + static let workoutManagerLogger = Logger(subsystem: Logger.mySubsystem, category: "workout-manager") + static let exerciseOptionsLogger = Logger(subsystem: Logger.mySubsystem, category: "exercise-options") + static let heartRateGraphLogger = Logger(subsystem: Logger.mySubsystem, category: "heart-rate-graph") +} diff --git a/Workout Spinner WatchKit Extension/Models/ExerciseInfo.swift b/Workout Spinner WatchKit Extension/Models/ExerciseInfo.swift index 8d32992..307fc30 100644 --- a/Workout Spinner WatchKit Extension/Models/ExerciseInfo.swift +++ b/Workout Spinner WatchKit Extension/Models/ExerciseInfo.swift @@ -15,6 +15,13 @@ struct ExerciseInfo: Identifiable, Codable, Equatable { let bodyParts: [ExerciseBodyPart] let workoutValue: [String: Float] var active: Bool = true + var shortDescription: String { + "\(displayName) (\(id))" + } + + var longDescription: String { + "\(id). \(displayName) - type: \(type.rawValue), num bodyparts: \(bodyParts.count), active: \(active)" + } static func == (lhs: ExerciseInfo, rhs: ExerciseInfo) -> Bool { return lhs.id == rhs.id diff --git a/Workout Spinner WatchKit Extension/Models/ExerciseOptions.swift b/Workout Spinner WatchKit Extension/Models/ExerciseOptions.swift index 347ae17..b404be6 100644 --- a/Workout Spinner WatchKit Extension/Models/ExerciseOptions.swift +++ b/Workout Spinner WatchKit Extension/Models/ExerciseOptions.swift @@ -7,8 +7,10 @@ // import Foundation +import os class ExerciseOptions: NSObject, ObservableObject { + let logger = Logger.exerciseOptionsLogger @Published var allExercises = [ExerciseInfo]() var exercises: [ExerciseInfo] { @@ -29,26 +31,32 @@ class ExerciseOptions: NSObject, ObservableObject { /// Write exercise array to disk. func saveExercises() { + logger.info("Saving exercises.") let encoder = JSONEncoder() do { let encodedExercises = try encoder.encode(allExercises) UserDefaults.standard.set(encodedExercises, forKey: UserDefaultsKeys.exerciseOptions.rawValue) + logger.log("Finished saving exercises.") } catch { - print("Error when encoding exercises to JSON: \(error.localizedDescription)") + logger.error("Error when encoding exercises to JSON: \(error.localizedDescription, privacy: .public)") } + logger.log("Finished saving exercises.") } /// Read in exercise array from disk. /// - Returns: Array of exercises. func loadExercises() -> [ExerciseInfo] { + logger.info("Loading exercises.") if let codedExercises = UserDefaults.standard.object(forKey: UserDefaultsKeys.exerciseOptions.rawValue) as? Data { do { let decodedExercises = try JSONDecoder().decode([ExerciseInfo].self, from: codedExercises) + logger.log("Loaded \(decodedExercises.count) exercises.") return decodedExercises } catch { - print("Unable to decode exercises: \(error.localizedDescription)") + logger.error("Unable to decode exercises: \(error.localizedDescription, privacy: .public)") } } + logger.log("Did not load any exercises.") return [] } @@ -72,12 +80,14 @@ class ExerciseOptions: NSObject, ObservableObject { extension ExerciseOptions { /// Add a new exercise. func append(_ exercise: ExerciseInfo) { + logger.debug("Adding new exercise: \(exercise.shortDescription, privacy: .public)") allExercises.append(exercise) saveExercises() } /// Replace one exercise with another. func replace(_ exercise: ExerciseInfo, with newExercise: ExerciseInfo) { + logger.debug("Replacing exercise: \(newExercise.shortDescription, privacy: .public) replacing \(exercise.shortDescription, privacy: .public)") if let idx = allExercises.firstIndex(where: { $0 == exercise }) { allExercises[idx] = newExercise saveExercises() @@ -86,6 +96,7 @@ extension ExerciseOptions { /// Remove an exercise. func remove(_ exercise: ExerciseInfo) { + logger.debug("Removing exercise: \(exercise.shortDescription)") let startCount = allExercises.count allExercises = exercises.filter { $0 == exercise } if startCount != allExercises.count { @@ -95,6 +106,7 @@ extension ExerciseOptions { /// Remove multiple exercises. func remove(_ exercisesToRemove: [ExerciseInfo]) { + logger.debug("Removing \(exercisesToRemove.count) exercises.") if exercisesToRemove.count == 0 { return } let startCount = allExercises.count for exercise in exercisesToRemove { @@ -107,6 +119,7 @@ extension ExerciseOptions { /// Update an existing exercise or append it to the end of the options. func updateOrAppend(_ exercise: ExerciseInfo) { + logger.debug("Updating or adding (as necessary) exercise: \(exercise.shortDescription, privacy: .public)") if let idx = allExercises.firstIndex(where: { $0 == exercise }) { allExercises[idx] = exercise } else { @@ -117,11 +130,12 @@ extension ExerciseOptions { /// Resest the list of exercises to default options. func resetExerciseOptions() { + logger.log("Reseting exercise options to defaults.") do { allExercises = try parse(jsonData: readLocalJsonFile(named: "WorkoutSpinnerExercises")) saveExercises() } catch { - print("error in loading workouts: \(error.localizedDescription)") + logger.error("Error in loading workouts: \(error.localizedDescription, privacy: .public)") } } } diff --git a/Workout Spinner WatchKit Extension/Models/HeartRateGraphData.swift b/Workout Spinner WatchKit Extension/Models/HeartRateGraphData.swift index 9327291..7371667 100644 --- a/Workout Spinner WatchKit Extension/Models/HeartRateGraphData.swift +++ b/Workout Spinner WatchKit Extension/Models/HeartRateGraphData.swift @@ -7,6 +7,7 @@ // import Foundation +import os struct HeartRateGraphDatum { let x: Double @@ -16,6 +17,7 @@ struct HeartRateGraphDatum { struct HeartRateGraphData { var data: [HeartRateGraphDatum] + var logger = Logger.heartRateGraphLogger var minX: Double { return data.map { $0.x }.min()! @@ -34,8 +36,8 @@ struct HeartRateGraphData { } init(workoutTraker: WorkoutTracker) { + logger.info("Heart rate graph initialized with workout tracker (\(workoutTraker.numberOfExercises) exercise(s))") data = HeartRateGraphData.workoutTrackerDataToGraphData(workoutTraker) -// self.data = HeartRateGraphData.mockHeartRateData() } static func workoutTrackerDataToGraphData(_ workoutTracker: WorkoutTracker) -> [HeartRateGraphDatum] { @@ -71,19 +73,21 @@ struct HeartRateGraphData { } } -extension HeartRateGraphData { - static func mockHeartRateData() -> [HeartRateGraphDatum] { - var d = [HeartRateGraphDatum]() - var x = 0.0 - for i in 0 ..< 5 { - let n = (5 ... 25).randomElement()! - var y = 50.0 - for _ in 0 ..< n { - x += 1 - y += Double.random(in: -5.0 ... 5.0) - d.append(HeartRateGraphDatum(x: x, y: y, groupIndex: i)) +#if DEBUG + extension HeartRateGraphData { + static func mockHeartRateData() -> [HeartRateGraphDatum] { + var d = [HeartRateGraphDatum]() + var x = 0.0 + for i in 0 ..< 5 { + let n = (5 ... 25).randomElement()! + var y = 50.0 + for _ in 0 ..< n { + x += 1 + y += Double.random(in: -5.0 ... 5.0) + d.append(HeartRateGraphDatum(x: x, y: y, groupIndex: i)) + } } + return d } - return d } -} +#endif diff --git a/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift b/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift index d45656c..2804851 100644 --- a/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift +++ b/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift @@ -9,6 +9,7 @@ import Combine import Foundation import HealthKit +import os enum WorkoutState { case running, paused, ended @@ -45,12 +46,16 @@ class WorkoutManager: NSObject, ObservableObject { var cancellable: Cancellable? var accumulatedTime: Int = 0 + let logger = Logger.workoutManagerLogger + override init() { exerciseInfo = nil + logger.info("WorkoutManager initialized with no arguments.") } init(exerciseInfo: ExerciseInfo) { self.exerciseInfo = exerciseInfo + logger.info("WorkoutManager initialized with exercise information: \(exerciseInfo.displayName, privacy: .public).") } // Set up and start the timer. @@ -96,10 +101,10 @@ class WorkoutManager: NSObject, ObservableObject { // Request authorization for those quantity types. healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead) { _, error in if let error = error { - print("Error requesting data read/share authorization: \(error.localizedDescription)") + self.logger.error("Error requesting data read/share authorization: \(error.localizedDescription, privacy: .public)") return } - print("Successfully requesting authoriation for data reading and sharing.") + self.logger.log("Successfully requesting authoriation for data reading and sharing.") } } @@ -113,13 +118,14 @@ class WorkoutManager: NSObject, ObservableObject { /// Start the workout. internal func setupWorkout() { + logger.log("Setting up workout.") // Create the session and obtain the workout builder. do { session = try HKWorkoutSession(healthStore: healthStore, configuration: workoutConfiguration()) builder = session.associatedWorkoutBuilder() } catch { // Handle any exceptions. - print("Error creating workout: \(error.localizedDescription)") + logger.error("Error creating workout: \(error.localizedDescription, privacy: .public)") return } @@ -130,10 +136,12 @@ class WorkoutManager: NSObject, ObservableObject { // Set the workout builder's data source. builder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: workoutConfiguration()) + logger.log("Finished setting up workout.") } /// Start a workout func startWorkout() { + logger.log("Workout started.") // Start the timer. setUpTimer() accumulatedTime = 0 @@ -145,6 +153,7 @@ class WorkoutManager: NSObject, ObservableObject { /// Pause a workout. func pauseWorkout() { + logger.log("Workout paused.") // Pause the workout. session.pause() // Stop the timer. @@ -156,6 +165,7 @@ class WorkoutManager: NSObject, ObservableObject { /// Resume a previously started workout. func resumeWorkout() { + logger.log("Workout resumed.") // Start the timer. setUpTimer() active = true @@ -165,6 +175,7 @@ class WorkoutManager: NSObject, ObservableObject { /// Reset all of the informational variables. func resetTrackedInformation() { + logger.log("Reset exercise information saved.") accumulatedTime = 0 allHeartRateReadings = [] heartrate = 0 @@ -174,15 +185,15 @@ class WorkoutManager: NSObject, ObservableObject { /// End a workout. func endWorkout() { - print("Ending workout session.") + logger.log("Ending workout session.") builder.endCollection(withEnd: Date()) { success, error in if let error = error { - print("Data collection ended with error: \(error.localizedDescription)") + self.logger.error("Data collection ended with error: \(error.localizedDescription, privacy: .public)") } else if success { - print("Data collection ended successfully.") + self.logger.log("Data collection ended successfully.") } else { - print("Data collection did not end successfully (but without error).") + self.logger.log("Data collection did not end successfully (but without error).") } } @@ -240,22 +251,24 @@ extension WorkoutManager: HKWorkoutSessionDelegate { func workoutSession(_: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date _: Date) { - print("Workout session did change state: \(workoutStateDescription(fromState)) -> \(workoutStateDescription(toState))") + let fromStateH = workoutStateDescription(fromState) + let toStateH = workoutStateDescription(toState) + logger.info("Workout session did change state: \(fromStateH, privacy: .public) -> \(toStateH, privacy: .public)") if toState == .ended { // Wait for the session to transition states before ending the builder. builder.finishWorkout { _, error in // Optionally display a workout summary to the user. if let error = error { - print("Builder did finish with error: \(error.localizedDescription)") + self.logger.error("Builder did finish with error: \(error.localizedDescription, privacy: .public)") } - print("Builder finished successfully.") + self.logger.info("Builder finished successfully.") } } } func workoutSession(_: HKWorkoutSession, didFailWithError error: Error) { - print("Workout session failed: \(error.localizedDescription)") + logger.error("Workout session failed: \(error.localizedDescription, privacy: .public)") } /// Return a description for the workout session state. diff --git a/Workout Spinner.xcodeproj/project.pbxproj b/Workout Spinner.xcodeproj/project.pbxproj index a2bd575..3879c88 100644 --- a/Workout Spinner.xcodeproj/project.pbxproj +++ b/Workout Spinner.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 84BCB975255D574400C83B28 /* SelectExercisesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BCB974255D574400C83B28 /* SelectExercisesView.swift */; }; 84C1FA7425497A050008CE09 /* WorkoutPagingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C1FA7325497A050008CE09 /* WorkoutPagingView.swift */; }; 84C1FA7C25497CD20008CE09 /* WorkoutTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C1FA7B25497CD20008CE09 /* WorkoutTracker.swift */; }; + 84CACD8F258235C9004CD231 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CACD8E258235C9004CD231 /* Logger.swift */; }; 84D0BDD9254ADA51001ABA83 /* ExerciseFinishView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D0BDD8254ADA51001ABA83 /* ExerciseFinishView.swift */; }; 84DD331725445DBF00409E57 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DD331625445DBF00409E57 /* WelcomeView.swift */; }; 84DEC01824FE5ABD002FF9BC /* ExercisePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DEC01724FE5ABD002FF9BC /* ExercisePicker.swift */; }; @@ -131,6 +132,7 @@ 84BCB974255D574400C83B28 /* SelectExercisesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectExercisesView.swift; sourceTree = ""; }; 84C1FA7325497A050008CE09 /* WorkoutPagingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WorkoutPagingView.swift; path = "Workout Spinner WatchKit Extension/Views/WorkoutPagingView.swift"; sourceTree = SOURCE_ROOT; }; 84C1FA7B25497CD20008CE09 /* WorkoutTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutTracker.swift; sourceTree = ""; }; + 84CACD8E258235C9004CD231 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 84D0BDD8254ADA51001ABA83 /* ExerciseFinishView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExerciseFinishView.swift; sourceTree = ""; }; 84DD331625445DBF00409E57 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; 84DEC01724FE5ABD002FF9BC /* ExercisePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExercisePicker.swift; sourceTree = ""; }; @@ -229,6 +231,14 @@ path = "Settings Views"; sourceTree = ""; }; + 84CACD8D25823591004CD231 /* Logging */ = { + isa = PBXGroup; + children = ( + 84CACD8E258235C9004CD231 /* Logger.swift */, + ); + path = Logging; + sourceTree = ""; + }; 84E1503524FD112F007F577D /* Data */ = { isa = PBXGroup; children = ( @@ -271,6 +281,7 @@ isa = PBXGroup; children = ( 848BE23D250B854900F48C7C /* Workout Spinner WatchKit Extension.entitlements */, + 84CACD8D25823591004CD231 /* Logging */, 843F33EA24FFB29F00919AD5 /* Extensions */, 84E372B524FD0B1F00AFB355 /* Views */, 84E372B424FD0AD500AFB355 /* View Models */, @@ -532,6 +543,7 @@ 84E9C0DD255422B9002D461B /* HeartRateGraphView.swift in Sources */, 84A7F866255C0BFB006D26CD /* ButtonStyles.swift in Sources */, 849C732A255EA67100C28F96 /* SectionHeader.swift in Sources */, + 84CACD8F258235C9004CD231 /* Logger.swift in Sources */, 84BCB975255D574400C83B28 /* SelectExercisesView.swift in Sources */, 84E3729624FD0A5D00AFB355 /* HostingController.swift in Sources */, 849C7325255EA64400C28F96 /* LabelWithIndicator.swift in Sources */, From 0d4d5832e1ba9063efa5828da55c5e07055474de Mon Sep 17 00:00:00 2001 From: Josh Cook Date: Fri, 11 Dec 2020 07:00:43 -0500 Subject: [PATCH 2/3] test: logging to Views and Settings --- Workout Spinner WatchKit App/Info.plist | 2 +- Workout Spinner WatchKit Extension/Info.plist | 2 +- .../Logging/Logger.swift | 16 ++++++++++++++++ .../Models/WorkoutManager.swift | 2 +- .../Views/ExercisePicker.swift | 5 +++++ .../HeartRateGraph/HeartRateGraphView.swift | 5 +++++ .../Views/Settings Views/EditExerciseView.swift | 13 ++++++++++++- .../Views/Settings Views/Settings.swift | 9 +++++++++ .../Views/Spinner Subviews/SpinnerSlice.swift | 2 -- .../Views/WelcomeView.swift | 5 +++++ .../Views/WorkoutFinishView.swift | 6 ++++++ .../Views/WorkoutPagingView.swift | 14 +++++++++++++- Workout Spinner.xcodeproj/project.pbxproj | 4 ++-- 13 files changed, 76 insertions(+), 9 deletions(-) diff --git a/Workout Spinner WatchKit App/Info.plist b/Workout Spinner WatchKit App/Info.plist index c5e18c5..ee8614c 100644 --- a/Workout Spinner WatchKit App/Info.plist +++ b/Workout Spinner WatchKit App/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 380 + 386 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/Workout Spinner WatchKit Extension/Info.plist b/Workout Spinner WatchKit Extension/Info.plist index 256ea8b..494aa54 100644 --- a/Workout Spinner WatchKit Extension/Info.plist +++ b/Workout Spinner WatchKit Extension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 380 + 386 CLKComplicationPrincipalClass $(PRODUCT_MODULE_NAME).ComplicationController CLKComplicationSupportedFamilies diff --git a/Workout Spinner WatchKit Extension/Logging/Logger.swift b/Workout Spinner WatchKit Extension/Logging/Logger.swift index 65a80f7..c70a40c 100644 --- a/Workout Spinner WatchKit Extension/Logging/Logger.swift +++ b/Workout Spinner WatchKit Extension/Logging/Logger.swift @@ -11,7 +11,23 @@ import os extension Logger { static let mySubsystem = "com.joshuacook.Workout-Spinner" + + // MARK: - Model loggers + static let workoutManagerLogger = Logger(subsystem: Logger.mySubsystem, category: "workout-manager") static let exerciseOptionsLogger = Logger(subsystem: Logger.mySubsystem, category: "exercise-options") static let heartRateGraphLogger = Logger(subsystem: Logger.mySubsystem, category: "heart-rate-graph") + + // MARK: - View loggers + + static let welcomeViewLogger = Logger(subsystem: Logger.mySubsystem, category: "welcome-view") + static let workoutPagingViewLogger = Logger(subsystem: Logger.mySubsystem, category: "workout-paging-view") + static let exercisePickerLogger = Logger(subsystem: Logger.mySubsystem, category: "exercise-picker") + static let workoutFinishViewLogger = Logger(subsystem: Logger.mySubsystem, category: "workout-finish-view") + static let heartRateGraphViewLogger = Logger(subsystem: Logger.mySubsystem, category: "heart-rate-graph-view") + + // MARK: - Settings + + static let settingsLogger = Logger(subsystem: Logger.mySubsystem, category: "settings") + static let editExercisesViewLogger = Logger(subsystem: Logger.mySubsystem, category: "settings-edit-exercise-view") } diff --git a/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift b/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift index 2804851..97577a1 100644 --- a/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift +++ b/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift @@ -175,7 +175,7 @@ class WorkoutManager: NSObject, ObservableObject { /// Reset all of the informational variables. func resetTrackedInformation() { - logger.log("Reset exercise information saved.") + logger.log("Reset recorded exercise information.") accumulatedTime = 0 allHeartRateReadings = [] heartrate = 0 diff --git a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift index f8bd539..b127551 100644 --- a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift +++ b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift @@ -7,6 +7,7 @@ // import Combine +import os import SwiftUI struct ExercisePicker: View { @@ -27,10 +28,14 @@ struct ExercisePicker: View { return WKInterfaceDevice().crownOrientation == .left ? 1.0 : -1.0 } + let logger = Logger.exercisePickerLogger + init(workoutManager: WorkoutManager, exerciseOptions: ExerciseOptions, exerciseSelected: Binding) { + logger.info("Initializing ExercisePicker.") self.workoutManager = workoutManager self.exerciseOptions = exerciseOptions _exerciseSelected = exerciseSelected + logger.debug("Finished initializing ExercisePicker.") } var body: some View { diff --git a/Workout Spinner WatchKit Extension/Views/HeartRateGraph/HeartRateGraphView.swift b/Workout Spinner WatchKit Extension/Views/HeartRateGraph/HeartRateGraphView.swift index b84145a..692383b 100644 --- a/Workout Spinner WatchKit Extension/Views/HeartRateGraph/HeartRateGraphView.swift +++ b/Workout Spinner WatchKit Extension/Views/HeartRateGraph/HeartRateGraphView.swift @@ -6,6 +6,7 @@ // Copyright © 2020 Joshua Cook. All rights reserved. // +import os import SwiftUI struct HeartRateGraphView: View { @@ -34,10 +35,14 @@ struct HeartRateGraphView: View { var verticalPadding: CGFloat = 10 init(workoutTracker: WorkoutTracker) { + logger.debug("Initializing `HeartRateGraphView`.") self.workoutTracker = workoutTracker graphData = HeartRateGraphData(workoutTraker: workoutTracker) + logger.log("Finished initializing `HeartRateGraphView`.") } + let logger = Logger.heartRateGraphViewLogger + var body: some View { GeometryReader { geo in HStack(alignment: .center, spacing: 0) { diff --git a/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift b/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift index 4eca4b3..ef833da 100644 --- a/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift +++ b/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift @@ -6,6 +6,7 @@ // Copyright © 2020 Joshua Cook. All rights reserved. // +import os import SwiftUI struct ExerciseAmountValuePicker: View { @@ -47,13 +48,19 @@ struct EditExerciseView: View { @Environment(\.presentationMode) var presentationMode + let logger = Logger.editExercisesViewLogger + init(exerciseOptions: ExerciseOptions) { + logger.debug("Initializing `EditExerciseView` with `ExerciseOptions`.") self.exerciseOptions = exerciseOptions exercise = nil bodyparts = BodyPartSelections(with: .none) + logger.log("Finished initializing `EditExerciseView` with `ExerciseOptions`.") } init(exerciseOptions: ExerciseOptions, exercise: ExerciseInfo) { + logger.debug("Initializing `EditExerciseView` with `ExerciseOptions` and `ExerciseInfo`.") + self.exerciseOptions = exerciseOptions self.exercise = exercise bodyparts = BodyPartSelections(fromExerciseInfo: exercise) @@ -69,6 +76,8 @@ struct EditExerciseView: View { _killingVal = .init(initialValue: convert(value: exercise.workoutValue[ExerciseIntensity.killing.rawValue], orUse: killingVal)) _active = .init(initialValue: exercise.active) + + logger.log("Finished initializing `EditExerciseView` with `ExerciseOptions` and `ExerciseInfo`.") } func convert(value: Float?, orUse defaultValue: Int) -> Int { @@ -135,6 +144,8 @@ struct EditExerciseView: View { extension EditExerciseView { func saveAndFinish() { + logger.debug("Saving exercise.") + let bp = bodyparts.bodyparts .filter { $0.enabled } .map { $0.bodypart } @@ -155,7 +166,7 @@ extension EditExerciseView { active: active) exerciseOptions.updateOrAppend(newExercise) - + logger.log("Saved exercise: \(newExercise.shortDescription, privacy: .public).") presentationMode.wrappedValue.dismiss() } } diff --git a/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift b/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift index 5da8bac..d65b4d0 100644 --- a/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift +++ b/Workout Spinner WatchKit Extension/Views/Settings Views/Settings.swift @@ -6,6 +6,7 @@ // Copyright © 2020 Joshua Cook. All rights reserved. // +import os import SwiftUI struct Settings: View { @@ -23,6 +24,8 @@ struct Settings: View { @State private var confirmResetExerciseOptions = false @Environment(\.presentationMode) var presentationMode + let logger = Logger.settingsLogger + var body: some View { Form { Section(header: SectionHeader(imageName: "figure.wave", text: "Preferences")) { @@ -46,6 +49,7 @@ struct Settings: View { LabelWithIndicator(text: "New exercise") } Button(action: { + logger.info("User tapped 'Reset Exercises' button.") confirmResetExerciseOptions.toggle() }) { HStack { @@ -69,18 +73,22 @@ struct Settings: View { title: Text("Reset exercises?"), message: Text("Are you sure you want to reset the list of exercises?"), primaryButton: .destructive(Text("Reset"), action: { + self.logger.info("User confirmed to reset exercises.") self.exerciseOptions.resetExerciseOptions() }), secondaryButton: .cancel() ) } .onAppear { + logger.debug("Settings will appear.") selectedExerciseIntensity = Settings.getSavedExerciseIntensity() } .onDisappear { + logger.debug("Setting will disappear.") self.saveUserDefualts() } .onReceive(NotificationCenter.default.publisher(for: .NSExtensionHostWillResignActive)) { _ in + logger.debug("System notification that Settings will resign active.") self.saveUserDefualts() } } @@ -89,6 +97,7 @@ struct Settings: View { extension Settings { func saveUserDefualts() { let intensity = ExerciseIntensity.allCases[selectedExerciseIntensity] + logger.log("Saving preferences to UserDefaults (intensity: \(intensity.rawValue, privacy: .public))") UserDefaults.standard.set(intensity.rawValue, forKey: UserDefaultsKeys.exerciseIntensity.rawValue) } diff --git a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift index 3a39ffe..ec2502f 100644 --- a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift +++ b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift @@ -39,8 +39,6 @@ struct SpinnerSlice: View { var body: some View { ZStack { -// Color.darkGray -// LinearGradient(gradient: Gradient(colors: [.gray, .darkGray]), startPoint: .leading, endPoint: .trailing) Color.white .clipShape(SpinnerSliceShape(radius: width / 2.0, angle: sliceAngle)) SpinnerSliceShape(radius: width / 2.0, angle: sliceAngle) diff --git a/Workout Spinner WatchKit Extension/Views/WelcomeView.swift b/Workout Spinner WatchKit Extension/Views/WelcomeView.swift index 770099a..360634c 100644 --- a/Workout Spinner WatchKit Extension/Views/WelcomeView.swift +++ b/Workout Spinner WatchKit Extension/Views/WelcomeView.swift @@ -6,6 +6,7 @@ // Copyright © 2020 Joshua Cook. All rights reserved. // +import os import SwiftUI struct WelcomeView: View { @@ -19,6 +20,8 @@ struct WelcomeView: View { @State private var presentSettingsView = false @State private var showInstructions = false + let logger = Logger.welcomeViewLogger + var body: some View { VStack { Spacer(minLength: 0) @@ -49,6 +52,7 @@ struct WelcomeView: View { Spacer() Button(action: { + logger.info("Instruction button tapped.") showInstructions = true }) { Image(systemName: "info.circle").font(.system(size: 18)) @@ -81,6 +85,7 @@ struct WelcomeView: View { }) } .onAppear { + self.logger.info("WelcomeView did appear.") self.arrowButtonSize = 0.9 workoutManager.requestAuthorization() workoutManager.setupWorkout() diff --git a/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift b/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift index 75bd0f8..c912883 100644 --- a/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift +++ b/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift @@ -6,6 +6,7 @@ // Copyright © 2020 Joshua Cook. All rights reserved. // +import os import SwiftUI struct InfoRowView: View { @@ -77,10 +78,13 @@ struct WorkoutFinishView: View { @State private var showAllExercises: Bool = false @State private var showHeartRateChartView: Bool = false + let logger = Logger.workoutFinishViewLogger + var body: some View { VStack { List { LinkedInfoRowView(title: "Number of exercises", titleColor: .green, value: "\(workoutTracker.numberOfExercises)") { + logger.info("Will show list of all exercises.") showAllExercises = true } .sheet(isPresented: $showAllExercises) { @@ -98,6 +102,7 @@ struct WorkoutFinishView: View { LinkedInfoRowView(title: "Average heart rate", titleColor: .red, value: averageHR, showEllipsis: heartRateGraphIsAvailable) { if heartRateGraphIsAvailable { + logger.info("Will show heart rate graph.") showHeartRateChartView = true } } @@ -115,6 +120,7 @@ struct WorkoutFinishView: View { InfoRowView(title: "Min/Max heart rate", titleColor: .red, value: "\(minHR) / \(maxHR)") ListViewDoneButton { + logger.info("Done button tapped.") presentationMode.wrappedValue.dismiss() workoutTracker.clear() } diff --git a/Workout Spinner WatchKit Extension/Views/WorkoutPagingView.swift b/Workout Spinner WatchKit Extension/Views/WorkoutPagingView.swift index 3fe64fa..8ed8b4d 100644 --- a/Workout Spinner WatchKit Extension/Views/WorkoutPagingView.swift +++ b/Workout Spinner WatchKit Extension/Views/WorkoutPagingView.swift @@ -6,6 +6,7 @@ // Copyright © 2020 Joshua Cook. All rights reserved. // +import os import SwiftUI struct WorkoutPagingView: View { @@ -21,6 +22,8 @@ struct WorkoutPagingView: View { @Environment(\.presentationMode) var presentationMode + let logger = Logger.workoutPagingViewLogger + var body: some View { ZStack { if currentPageIndex == 0 { @@ -31,9 +34,11 @@ struct WorkoutPagingView: View { withAnimation(.none) { currentPageIndex = 1 } } }) { - ExerciseStartView(workoutManager: workoutManager, exerciseCanceled: $exerciseCanceled).navigationTitle(Text("")) + ExerciseStartView(workoutManager: workoutManager, exerciseCanceled: $exerciseCanceled) + .navigationTitle(Text("")) } .onLongPressGesture { + logger.info("Long press gesture recognized.") confirmFinishWorkout = true } } else if currentPageIndex == 1 { @@ -48,6 +53,7 @@ struct WorkoutPagingView: View { .toolbar(content: { ToolbarItem(placement: .cancellationAction) { Button("Done") { + logger.info("ExerciseFinishView dismissed.") self.exerciseComplete = false } } @@ -79,8 +85,10 @@ struct WorkoutPagingView: View { extension WorkoutPagingView { /// Complete a single exercise. func finishExercise() { + logger.info("Finish exercise method run.") switch workoutManager.session.state { case .running: + logger.debug("Pasuing workout session.") workoutManager.pauseWorkout() default: break @@ -89,10 +97,13 @@ extension WorkoutPagingView { /// Start an exercise. func startExercise() { + logger.info("Start exercise method run.") switch workoutManager.session.state { case .notStarted, .prepared: + logger.debug("Starting workout session.") workoutManager.startWorkout() case .paused: + logger.debug("Resuming workout session.") workoutManager.resumeWorkout() default: break @@ -101,6 +112,7 @@ extension WorkoutPagingView { /// Complete the entire workout session. func finishWorkout() { + logger.info("Finish exercise method run.") workoutManager.endWorkout() if workoutTracker.data.count > 0 { currentPageIndex = 2 diff --git a/Workout Spinner.xcodeproj/project.pbxproj b/Workout Spinner.xcodeproj/project.pbxproj index 3879c88..9a31eb5 100644 --- a/Workout Spinner.xcodeproj/project.pbxproj +++ b/Workout Spinner.xcodeproj/project.pbxproj @@ -789,7 +789,7 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 6.2; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Debug; }; @@ -808,7 +808,7 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 6.2; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Release; }; From 0a7c24da0e168264d93cb0594bdfaeee21fb26c6 Mon Sep 17 00:00:00 2001 From: Josh Cook Date: Fri, 11 Dec 2020 07:30:41 -0500 Subject: [PATCH 3/3] test: increment version to 1.1 because worked on device - remove auto-increment of build number --- Workout Spinner WatchKit App/Info.plist | 4 +- Workout Spinner WatchKit Extension/Info.plist | 4 +- .../Models/WorkoutManager.swift | 19 ++++--- Workout Spinner.xcodeproj/project.pbxproj | 57 ++++--------------- 4 files changed, 26 insertions(+), 58 deletions(-) diff --git a/Workout Spinner WatchKit App/Info.plist b/Workout Spinner WatchKit App/Info.plist index ee8614c..93683cf 100644 --- a/Workout Spinner WatchKit App/Info.plist +++ b/Workout Spinner WatchKit App/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 386 + $(CURRENT_PROJECT_VERSION) UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/Workout Spinner WatchKit Extension/Info.plist b/Workout Spinner WatchKit Extension/Info.plist index 494aa54..d639c2e 100644 --- a/Workout Spinner WatchKit Extension/Info.plist +++ b/Workout Spinner WatchKit Extension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 386 + $(CURRENT_PROJECT_VERSION) CLKComplicationPrincipalClass $(PRODUCT_MODULE_NAME).ComplicationController CLKComplicationSupportedFamilies diff --git a/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift b/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift index 97577a1..f65f178 100644 --- a/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift +++ b/Workout Spinner WatchKit Extension/Models/WorkoutManager.swift @@ -67,15 +67,15 @@ class WorkoutManager: NSObject, ObservableObject { guard let self = self else { return } self.elapsedSeconds = self.incrementElapsedTime() - #if DEBUG - // Mock exercise stats with a 5 % chance. - if Int.random(in: 0 ... 100) < 5 { - self.updateMockStatistics(quantityType: .heartRate) - } - if Int.random(in: 0 ... 100) < 5 { - self.updateMockStatistics(quantityType: .activeEnergyBurned) - } - #endif +// #if DEBUG +// // Mock exercise stats with a 5 % chance. +// if Int.random(in: 0 ... 100) < 5 { +// self.updateMockStatistics(quantityType: .heartRate) +// } +// if Int.random(in: 0 ... 100) < 5 { +// self.updateMockStatistics(quantityType: .activeEnergyBurned) +// } +// #endif } } @@ -303,6 +303,7 @@ extension WorkoutManager: HKLiveWorkoutBuilderDelegate { func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set) { for type in collectedTypes { + logger.debug("Did collect data of \(type.description, privacy: .public)") guard let quantityType = type as? HKQuantityType else { return // Nothing to do. } diff --git a/Workout Spinner.xcodeproj/project.pbxproj b/Workout Spinner.xcodeproj/project.pbxproj index 9a31eb5..496a92e 100644 --- a/Workout Spinner.xcodeproj/project.pbxproj +++ b/Workout Spinner.xcodeproj/project.pbxproj @@ -390,7 +390,6 @@ buildPhases = ( 84E3727D24FD0A5800AFB355 /* Resources */, 84E372AB24FD0A5E00AFB355 /* Embed App Extensions */, - 845D589E2541948400F2764B /* Increment Build */, ); buildRules = ( ); @@ -409,7 +408,6 @@ 84E3728A24FD0A5D00AFB355 /* Sources */, 84E3728B24FD0A5D00AFB355 /* Frameworks */, 84E3728C24FD0A5D00AFB355 /* Resources */, - 845D589D2541945000F2764B /* Increment Build */, ); buildRules = ( ); @@ -490,45 +488,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 845D589D2541945000F2764B /* Increment Build */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Increment Build"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "buildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n"; - }; - 845D589E2541948400F2764B /* Increment Build */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Increment Build"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "buildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 84E3728A24FD0A5D00AFB355 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -730,6 +689,7 @@ ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CODE_SIGN_ENTITLEMENTS = "Workout Spinner WatchKit Extension/Workout Spinner WatchKit Extension.entitlements"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_ASSET_PATHS = "\"Workout Spinner WatchKit Extension/Preview Content\""; DEVELOPMENT_TEAM = CRMHYJ9F9F; ENABLE_PREVIEWS = YES; @@ -739,6 +699,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = "com.joshuacook.Workout-Spinner.watchkitapp.watchkitextension"; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; @@ -755,6 +716,7 @@ ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CODE_SIGN_ENTITLEMENTS = "Workout Spinner WatchKit Extension/Workout Spinner WatchKit Extension.entitlements"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_ASSET_PATHS = "\"Workout Spinner WatchKit Extension/Preview Content\""; DEVELOPMENT_TEAM = CRMHYJ9F9F; ENABLE_PREVIEWS = YES; @@ -764,6 +726,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = "com.joshuacook.Workout-Spinner.watchkitapp.watchkitextension"; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; @@ -780,9 +743,11 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = CRMHYJ9F9F; IBSC_MODULE = Workout_Spinner_WatchKit_Extension; INFOPLIST_FILE = "Workout Spinner WatchKit App/Info.plist"; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = "com.joshuacook.Workout-Spinner.watchkitapp"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -799,9 +764,11 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = CRMHYJ9F9F; IBSC_MODULE = Workout_Spinner_WatchKit_Extension; INFOPLIST_FILE = "Workout Spinner WatchKit App/Info.plist"; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = "com.joshuacook.Workout-Spinner.watchkitapp"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -816,9 +783,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = CRMHYJ9F9F; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = "com.joshuacook.Workout-Spinner"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -829,9 +796,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = CRMHYJ9F9F; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = "com.joshuacook.Workout-Spinner"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0;