Skip to content

Commit 081442e

Browse files
committed
Add new step calculations and fix divide by zero
1 parent fe52702 commit 081442e

4 files changed

Lines changed: 68 additions & 23 deletions

File tree

InfiniLink/Core/Components/Charts/Steps/StepMonthlyChartView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct StepCalendarView: View {
3535
.frame(maxWidth: .infinity)
3636
}
3737
}
38+
// FIXME: poor performance
3839
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: weekdays.count), spacing: 14) {
3940
ForEach(fetchDates(), id: \.id) { value in
4041
ZStack {

InfiniLink/Core/Components/DetailHeaderView.swift

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,39 @@ struct DetailHeaderSubItemView: View {
2929
let title: String
3030
let value: String
3131
let unit: String?
32+
let icon: (String, Color)?
3233

33-
init(title: String, value: String, unit: String? = nil) {
34+
init(title: String, value: String, unit: String? = nil, icon: (String, Color)? = nil) {
3435
self.title = title
3536
self.value = value
3637
self.unit = unit
38+
self.icon = icon
3739
}
3840

3941
var body: some View {
40-
VStack(alignment: .leading, spacing: 3) {
41-
Text(title.uppercased())
42-
.font(.caption)
43-
.foregroundStyle(.gray)
44-
HStack(alignment: .firstTextBaseline, spacing: 2) {
45-
Text(value)
46-
.font(.system(size: 22).weight(.semibold))
47-
if let unit {
48-
Text(unit)
49-
.font(.system(size: 17.5))
50-
.foregroundColor(.primary.opacity(0.75))
42+
HStack(spacing: 9) {
43+
if let name = icon?.0, let color = icon?.1 {
44+
Image(systemName: name)
45+
.foregroundStyle(color)
46+
.font(.system(size: 21).weight(.medium))
47+
.frame(minWidth: 30)
48+
}
49+
VStack(alignment: .leading, spacing: 3) {
50+
Text(title.uppercased())
51+
.font(.caption)
52+
.foregroundStyle(.gray)
53+
HStack(alignment: .firstTextBaseline, spacing: 2) {
54+
Text(value)
55+
.font(.system(size: 22).weight(.semibold))
56+
if let unit {
57+
Text(unit)
58+
.font(.system(size: 17).weight(.medium))
59+
.foregroundColor(.primary.opacity(0.75))
60+
}
5161
}
5262
}
63+
Spacer()
5364
}
54-
.frame(maxWidth: .infinity, alignment: .leading)
5565
.padding(12)
5666
.background(colorScheme == .dark ? AnyShapeStyle(Material.regular) : AnyShapeStyle(Color(.systemBackground)))
5767
.clipShape(RoundedRectangle(cornerRadius: 15))

InfiniLink/Core/StepsView.swift

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ struct StepsView: View {
1919

2020
let exerciseCalculator = FitnessCalculator()
2121

22-
func steps(for date: Date) -> Int {
22+
func steps() -> Int {
2323
for stepCount in chartManager.stepPoints() {
24-
if Calendar.current.isDate(stepCount.timestamp!, inSameDayAs: date) {
24+
if Calendar.current.isDate(stepCount.timestamp!, inSameDayAs: Date()) {
2525
return Int(stepCount.steps)
2626
}
2727
}
@@ -40,12 +40,28 @@ struct StepsView: View {
4040
GeometryReader { geo in
4141
List {
4242
Section {
43-
DetailHeaderView(Header(title: "\(steps(for: Date()))", subtitle: String(deviceManager.settings.stepsGoal), units: "Steps", icon: "figure.walk", accent: .blue), width: geo.size.width) {
44-
HStack {
45-
DetailHeaderSubItemView(title: "Dis",
46-
value: String(format: "%.2f", exerciseCalculator.calculateDistance(steps: steps(for: Date()))),
47-
unit: personalizationController.units == .imperial ? "mi" : "km")
48-
DetailHeaderSubItemView(title: "Kcal", value: String(exerciseCalculator.calculateCaloriesBurned(steps: steps(for: Date()))))
43+
DetailHeaderView(Header(title: "\(steps())", subtitle: String(deviceManager.settings.stepsGoal), units: "Steps", icon: "figure.walk", accent: .blue), width: geo.size.width) {
44+
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())]) {
45+
let unitsFull = personalizationController.units == .imperial ? "mile" : "kilometer"
46+
let units = personalizationController.units == .imperial ? "mi" : "km"
47+
let distance = exerciseCalculator.calculateDistance(steps: steps())
48+
let stepsPerUnit = exerciseCalculator.stepsPerUnit(steps: steps())
49+
50+
DetailHeaderSubItemView(title: "Distance",
51+
value: String(format: "%.2f", distance),
52+
unit: units,
53+
icon: ("ruler", Color.blue))
54+
DetailHeaderSubItemView(title: "Kcal",
55+
value: String(exerciseCalculator.calculateCaloriesBurned(steps: steps())),
56+
icon: ("flame", Color.orange))
57+
DetailHeaderSubItemView(title: "Minutes per \(unitsFull)",
58+
value: String(format: "%.1f", exerciseCalculator.minutesForDistance(distance: distance)),
59+
unit: "min\(distance == 1 ? "" : "s")",
60+
icon: ("stopwatch", Color.primary))
61+
DetailHeaderSubItemView(title: "Steps per \(unitsFull)",
62+
value: String(stepsPerUnit),
63+
unit: "step\(stepsPerUnit == 1 ? "" : "s")",
64+
icon: ("shoeprints.fill", Color.blue))
4965
}
5066
}
5167
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))

InfiniLink/Utils/FitnessCalculator.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class FitnessCalculator {
4848
let personalizationController = PersonalizationController.shared
4949
let bleManager = BLEManager.shared
5050

51-
func calculateDistance(steps: Int, pace: FitnessCalculator.Pace = .avgWalk) -> Double {
51+
func strideLength(pace: FitnessCalculator.Pace = .avgWalk) -> Double {
5252
let avgStrideRatio = personalizationController.gender == .male ? 0.415 : 0.413
5353
let calculatedHeight = personalizationController.calculatedHeight
5454
let height = personalizationController.units == .metric ? (calculatedHeight / 2.54) : (calculatedHeight) // Convert to inches
@@ -69,7 +69,11 @@ class FitnessCalculator {
6969
}()
7070

7171
let strideLength = baseStrideLength * strideMultiplier
72-
var distance = strideLength * Double(steps)
72+
return strideLength
73+
}
74+
75+
func calculateDistance(steps: Int, pace: FitnessCalculator.Pace = .avgWalk) -> Double {
76+
var distance = strideLength(pace: pace) * Double(steps)
7377

7478
if personalizationController.units == .imperial {
7579
distance /= 63360 // Convert inches to miles
@@ -85,6 +89,8 @@ class FitnessCalculator {
8589
let distance = calculateDistance(steps: steps, pace: pace)
8690
let timeMinutes = (distance / pace.milesPerHour) * 60.0
8791

92+
guard steps > 0, timeMinutes > 0 else { return 0 }
93+
8894
let spm = Double(steps) / timeMinutes
8995
return Int(ceil(spm))
9096
}
@@ -96,7 +102,19 @@ class FitnessCalculator {
96102
let calculatedWeight = personalizationController.units == .metric ? weight : (weight * 0.453592)
97103
let durationInHours = Double(steps) / Double(spm) / 60.0
98104

105+
guard durationInHours > 0 else { return 0 }
106+
99107
let caloriesBurned = Int(ceil(pace.metValue * calculatedWeight * durationInHours))
100108
return caloriesBurned
101109
}
110+
111+
func minutesForDistance(distance: Double, pace: FitnessCalculator.Pace = .avgWalk) -> Double {
112+
let speed = personalizationController.units == .imperial ? pace.milesPerHour : (pace.milesPerHour * 1.60934)
113+
return (distance / speed) * 60
114+
}
115+
116+
func stepsPerUnit(steps: Int, pace: FitnessCalculator.Pace = .avgWalk) -> Int {
117+
let unitInInches = personalizationController.units == .imperial ? 63360 : 39370.1
118+
return Int(ceil(unitInInches / strideLength()))
119+
}
102120
}

0 commit comments

Comments
 (0)