StorageCurrentGlucoseStateProvider.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // LoopFollow
  2. // StorageCurrentGlucoseStateProvider.swift
  3. import Foundation
  4. /// Reads the latest glucose state from LoopFollow's Storage and Observable layers.
  5. /// This is the only file in the pipeline that is allowed to touch Storage.shared
  6. /// or Observable.shared — all other layers read exclusively from this provider.
  7. struct StorageCurrentGlucoseStateProvider: CurrentGlucoseStateProviding {
  8. // MARK: - Core Glucose
  9. var glucoseMgdl: Double? {
  10. guard let bg = Observable.shared.bg.value, bg > 0 else { return nil }
  11. return Double(bg)
  12. }
  13. var deltaMgdl: Double? {
  14. Storage.shared.lastDeltaMgdl.value
  15. }
  16. var projectedMgdl: Double? {
  17. Storage.shared.projectedBgMgdl.value
  18. }
  19. var updatedAt: Date? {
  20. guard let t = Storage.shared.lastBgReadingTimeSeconds.value else { return nil }
  21. return Date(timeIntervalSince1970: t)
  22. }
  23. var trendCode: String? {
  24. Storage.shared.lastTrendCode.value
  25. }
  26. // MARK: - Secondary Metrics
  27. var iob: Double? {
  28. Storage.shared.lastIOB.value
  29. }
  30. var cob: Double? {
  31. Storage.shared.lastCOB.value
  32. }
  33. // MARK: - Extended Metrics
  34. var override: String? {
  35. Observable.shared.override.value
  36. }
  37. var recBolus: Double? {
  38. Observable.shared.deviceRecBolus.value
  39. }
  40. var battery: Double? {
  41. Observable.shared.deviceBatteryLevel.value
  42. }
  43. var pumpBattery: Double? {
  44. Observable.shared.pumpBatteryLevel.value
  45. }
  46. var basalRate: String {
  47. Storage.shared.lastBasal.value
  48. }
  49. var pumpReservoirU: Double? {
  50. Storage.shared.lastPumpReservoirU.value
  51. }
  52. var autosens: Double? {
  53. Storage.shared.lastAutosens.value
  54. }
  55. var tdd: Double? {
  56. Storage.shared.lastTdd.value
  57. }
  58. var targetLowMgdl: Double? {
  59. Storage.shared.lastTargetLowMgdl.value
  60. }
  61. var targetHighMgdl: Double? {
  62. Storage.shared.lastTargetHighMgdl.value
  63. }
  64. var isfMgdlPerU: Double? {
  65. Storage.shared.lastIsfMgdlPerU.value
  66. }
  67. var carbRatio: Double? {
  68. Storage.shared.lastCarbRatio.value
  69. }
  70. var carbsToday: Double? {
  71. Storage.shared.lastCarbsToday.value
  72. }
  73. var profileName: String? {
  74. let raw = Storage.shared.lastProfileName.value
  75. return raw.isEmpty ? nil : raw
  76. }
  77. var sageInsertTime: TimeInterval {
  78. Storage.shared.sageInsertTime.value
  79. }
  80. var cageInsertTime: TimeInterval {
  81. Storage.shared.cageInsertTime.value
  82. }
  83. var iageInsertTime: TimeInterval {
  84. Storage.shared.iageInsertTime.value
  85. }
  86. var minBgMgdl: Double? {
  87. Storage.shared.lastMinBgMgdl.value
  88. }
  89. var maxBgMgdl: Double? {
  90. Storage.shared.lastMaxBgMgdl.value
  91. }
  92. // MARK: - Loop Status
  93. var isNotLooping: Bool {
  94. let lastLoopTime = Storage.shared.lastLoopTime.value
  95. guard lastLoopTime > 0, !Storage.shared.url.value.isEmpty else { return false }
  96. return Date().timeIntervalSince1970 - lastLoopTime >= 15 * 60
  97. }
  98. // MARK: - Renewal
  99. var showRenewalOverlay: Bool {
  100. #if targetEnvironment(macCatalyst)
  101. return false
  102. #else
  103. // iOS 17.2+ renews silently via push-to-start at the deadline, so the
  104. // pre-emptive 30-minute "tap to update" overlay would be misleading
  105. // during normal operation. Only show it once renewal has actually
  106. // failed (no token, bad creds, rate-limited) — that is genuinely
  107. // user-actionable. iOS 16.x keeps the time-based warning because
  108. // renewal there requires the user to foreground the app.
  109. if #available(iOS 17.2, *) {
  110. return Storage.shared.laRenewalFailed.value
  111. } else {
  112. let renewBy = Storage.shared.laRenewBy.value
  113. let now = Date().timeIntervalSince1970
  114. return renewBy > 0 && now >= renewBy - LiveActivityManager.renewalWarning
  115. }
  116. #endif
  117. }
  118. }