GlucoseChartView.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import Charts
  2. import Foundation
  3. import SwiftUI
  4. struct GlucoseChartView: ChartContent {
  5. let glucoseData: [GlucoseStored]
  6. let units: GlucoseUnits
  7. let highGlucose: Decimal
  8. let lowGlucose: Decimal
  9. let currentGlucoseTarget: Decimal
  10. let isSmoothingEnabled: Bool
  11. let glucoseColorScheme: GlucoseColorScheme
  12. var body: some ChartContent {
  13. drawGlucoseChart()
  14. }
  15. private func drawGlucoseChart() -> some ChartContent {
  16. ForEach(glucoseData) { item in
  17. let glucoseToDisplay = units == .mgdL ? Decimal(item.glucose) : Decimal(item.glucose).asMmolL
  18. // TODO: workaround for now: set low value to 55, to have dynamic color shades between 55 and user-set low (approx. 70); same for high glucose
  19. let hardCodedLow = Decimal(55)
  20. let hardCodedHigh = Decimal(220)
  21. let isDynamicColorScheme = glucoseColorScheme == .dynamicColor
  22. let pointMarkColor: Color = Trio.getDynamicGlucoseColor(
  23. glucoseValue: Decimal(item.glucose),
  24. highGlucoseColorValue: isDynamicColorScheme ? hardCodedHigh : highGlucose,
  25. lowGlucoseColorValue: isDynamicColorScheme ? hardCodedLow : lowGlucose,
  26. targetGlucose: currentGlucoseTarget,
  27. glucoseColorScheme: glucoseColorScheme
  28. )
  29. PointMark(
  30. x: .value("Time", item.date ?? Date(), unit: .second),
  31. y: .value("Value", glucoseToDisplay)
  32. )
  33. .foregroundStyle(pointMarkColor)
  34. .symbolSize(20)
  35. .symbol {
  36. if item.isManual {
  37. Image(systemName: "drop.fill")
  38. .font(.caption2)
  39. .symbolRenderingMode(.monochrome)
  40. .bold()
  41. .foregroundStyle(.red)
  42. } else {
  43. Image(systemName: "circle.fill")
  44. .font(.system(size: 5))
  45. .bold()
  46. .foregroundStyle(pointMarkColor)
  47. }
  48. }
  49. if isSmoothingEnabled, let smoothedGlucose = item.smoothedGlucose, smoothedGlucose != 0 {
  50. let smoothedGlucoseForDisplay: Decimal = units == .mgdL ? smoothedGlucose.decimalValue : smoothedGlucose
  51. .decimalValue.asMmolL
  52. LineMark(
  53. x: .value("Time", item.date ?? Date(), unit: .second),
  54. y: .value("Value", smoothedGlucoseForDisplay),
  55. series: .value("Type", "Smoothed")
  56. )
  57. .foregroundStyle(Color.secondary)
  58. }
  59. }
  60. }
  61. }
  62. #Preview {
  63. struct PreviewWrapper: View {
  64. @State private var previewStack: CoreDataStack? = nil
  65. @State private var glucoseData: [GlucoseStored] = []
  66. @State private var isLoading = true
  67. var body: some View {
  68. NavigationView {
  69. Group {
  70. if isLoading {
  71. ProgressView("Loading data...")
  72. } else {
  73. VStack {
  74. Chart {
  75. GlucoseChartView(
  76. glucoseData: glucoseData,
  77. units: .mgdL,
  78. highGlucose: 180,
  79. lowGlucose: 70,
  80. currentGlucoseTarget: 100,
  81. isSmoothingEnabled: false,
  82. glucoseColorScheme: .dynamicColor
  83. )
  84. }
  85. .frame(height: 200)
  86. .padding()
  87. }
  88. }
  89. }
  90. .navigationTitle("Glucose Chart")
  91. .task {
  92. // Use the preview stack that's initialized asynchronously in CoreDataStack
  93. previewStack = try? await CoreDataStack.preview()
  94. // Now you can safely create preview data
  95. if let stack = previewStack {
  96. glucoseData = GlucoseStored.makePreviewGlucose(count: 24, provider: stack)
  97. isLoading = false
  98. }
  99. }
  100. }
  101. }
  102. }
  103. return PreviewWrapper()
  104. }