Skip to content

Commit cf77081

Browse files
committed
Denote stale glucose, and other watch updates
1 parent 8d5d709 commit cf77081

File tree

13 files changed

+170
-62
lines changed

13 files changed

+170
-62
lines changed

Common/Models/WatchContext.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ final class WatchContext: RawRepresentable {
2121
var glucose: HKQuantity?
2222
var glucoseTrendRawValue: Int?
2323
var glucoseDate: Date?
24+
var glucoseSyncIdentifier: String?
2425

2526
var predictedGlucose: WatchPredictedGlucose?
2627
var eventualGlucose: HKQuantity? {
@@ -57,6 +58,7 @@ final class WatchContext: RawRepresentable {
5758

5859
glucoseTrendRawValue = rawValue["gt"] as? Int
5960
glucoseDate = rawValue["gd"] as? Date
61+
glucoseSyncIdentifier = rawValue["gs"] as? String
6062
iob = rawValue["iob"] as? Double
6163
reservoir = rawValue["r"] as? Double
6264
reservoirPercentage = rawValue["rp"] as? Double
@@ -94,6 +96,7 @@ final class WatchContext: RawRepresentable {
9496

9597
raw["gt"] = glucoseTrendRawValue
9698
raw["gd"] = glucoseDate
99+
raw["gs"] = glucoseSyncIdentifier
97100
raw["iob"] = iob
98101
raw["ld"] = loopLastRunDate
99102
raw["r"] = reservoir
@@ -116,3 +119,12 @@ extension WatchContext {
116119
}
117120
}
118121
}
122+
123+
extension WatchContext {
124+
var newGlucoseSample: NewGlucoseSample? {
125+
if let quantity = glucose, let date = glucoseDate, let syncIdentifier = glucoseSyncIdentifier {
126+
return NewGlucoseSample(date: date, quantity: quantity, isDisplayOnly: false, syncIdentifier: syncIdentifier, syncVersion: 0)
127+
}
128+
return nil
129+
}
130+
}

Loop Status Extension/StatusViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,12 @@ class StatusViewController: UIViewController, NCWidgetProviding {
256256
return
257257
}
258258

259-
if let lastGlucose = glucose.last {
259+
if let lastGlucose = glucose.last, let recencyInterval = defaults.loopSettings?.recencyInterval {
260260
self.hudView.glucoseHUD.setGlucoseQuantity(
261261
lastGlucose.quantity.doubleValue(for: unit),
262262
at: lastGlucose.startDate,
263263
unit: unit,
264+
staleGlucoseAge: recencyInterval,
264265
sensor: context.sensor
265266
)
266267
}

Loop.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@
364364
C17824A51E1AD4D100D9D25C /* BolusRecommendation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17824A41E1AD4D100D9D25C /* BolusRecommendation.swift */; };
365365
C17824A61E1AF91F00D9D25C /* BolusRecommendation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17824A41E1AD4D100D9D25C /* BolusRecommendation.swift */; };
366366
C1814B86225E507C008D2D8E /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1814B85225E507C008D2D8E /* Sequence.swift */; };
367+
C19E96DF23D275F8003F79B0 /* LoopCompletionFreshness.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19E96DD23D2733F003F79B0 /* LoopCompletionFreshness.swift */; };
368+
C19E96E023D275FA003F79B0 /* LoopCompletionFreshness.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19E96DD23D2733F003F79B0 /* LoopCompletionFreshness.swift */; };
367369
C1A3EED2235233E1007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED1235233E1007672E3 /* DerivedAssets.xcassets */; };
368370
C1A3EED423523551007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED323523551007672E3 /* DerivedAssets.xcassets */; };
369371
C1A3EED523535FFF007672E3 /* DefaultAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 894F71E11FFEC4D8007D365C /* DefaultAssets.xcassets */; };
@@ -1015,6 +1017,7 @@
10151017
C18A491322FCC22900FDA733 /* make_scenario.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = make_scenario.py; sourceTree = "<group>"; };
10161018
C18A491422FCC22900FDA733 /* build-derived-watch-assets.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "build-derived-watch-assets.sh"; sourceTree = "<group>"; };
10171019
C18A491522FCC22900FDA733 /* copy-plugins.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "copy-plugins.sh"; sourceTree = "<group>"; };
1020+
C19E96DD23D2733F003F79B0 /* LoopCompletionFreshness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopCompletionFreshness.swift; sourceTree = "<group>"; };
10181021
C1A3EED1235233E1007672E3 /* DerivedAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssets.xcassets; sourceTree = "<group>"; };
10191022
C1A3EED323523551007672E3 /* DerivedAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssets.xcassets; sourceTree = "<group>"; };
10201023
C1C6591B1E1B1FDA0025CC58 /* recommend_temp_basal_dropping_then_rising.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = recommend_temp_basal_dropping_then_rising.json; sourceTree = "<group>"; };
@@ -1427,6 +1430,7 @@
14271430
43C05CB721EBEA54006FB252 /* HKUnit.swift */,
14281431
434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */,
14291432
430B298C2041F56500BA9F93 /* LoopSettings.swift */,
1433+
C19E96DD23D2733F003F79B0 /* LoopCompletionFreshness.swift */,
14301434
430B29892041F54A00BA9F93 /* NSUserDefaults.swift */,
14311435
431E73471FF95A900069B5F7 /* PersistenceController.swift */,
14321436
43D848AF1E7DCBE100DADCBC /* Result.swift */,
@@ -2675,6 +2679,7 @@
26752679
files = (
26762680
43C05CB821EBEA54006FB252 /* HKUnit.swift in Sources */,
26772681
4345E3F421F036FC009E00E5 /* Result.swift in Sources */,
2682+
C19E96E023D275FA003F79B0 /* LoopCompletionFreshness.swift in Sources */,
26782683
43D9002021EB209400AF44BF /* NSTimeInterval.swift in Sources */,
26792684
43C05CA921EB2B26006FB252 /* PersistenceController.swift in Sources */,
26802685
431EA87221EB29150076EC1A /* InsulinModelSettings.swift in Sources */,
@@ -2726,6 +2731,7 @@
27262731
files = (
27272732
43C05CB921EBEA54006FB252 /* HKUnit.swift in Sources */,
27282733
4345E3F521F036FC009E00E5 /* Result.swift in Sources */,
2734+
C19E96DF23D275F8003F79B0 /* LoopCompletionFreshness.swift in Sources */,
27292735
43D9FFFB21EAF3D300AF44BF /* NSTimeInterval.swift in Sources */,
27302736
43C05CA821EB2B26006FB252 /* PersistenceController.swift in Sources */,
27312737
431EA87321EB29160076EC1A /* InsulinModelSettings.swift in Sources */,

Loop/Managers/WatchDataManager.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,18 @@ final class WatchDataManager: NSObject {
160160
if let trend = self.deviceManager.cgmManager?.sensorState?.trendType {
161161
context.glucoseTrendRawValue = trend.rawValue
162162
}
163+
164+
if let glucose = glucose {
165+
updateGroup.enter()
166+
manager.glucoseStore.getCachedGlucoseSamples(start: glucose.startDate) { (samples) in
167+
if let sample = samples.last {
168+
context.glucose = sample.quantity
169+
context.glucoseDate = sample.startDate
170+
context.glucoseSyncIdentifier = sample.syncIdentifier
171+
}
172+
updateGroup.leave()
173+
}
174+
}
163175

164176
updateGroup.enter()
165177
manager.doseStore.insulinOnBoard(at: Date()) { (result) in

Loop/View Controllers/StatusTableViewController.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ final class StatusTableViewController: ChartsTableViewController {
487487
hudView.glucoseHUD.setGlucoseQuantity(glucose.quantity.doubleValue(for: unit),
488488
at: glucose.startDate,
489489
unit: unit,
490+
staleGlucoseAge: self.deviceManager.loopManager.settings.recencyInterval,
490491
sensor: self.deviceManager.sensorState
491492
)
492493
}

LoopCore/LoopSettings.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public struct LoopSettings: Equatable {
3838

3939
/// The amount of time since a given date that data should be considered valid
4040
public let recencyInterval = TimeInterval(minutes: 15)
41+
42+
/// Loop completion aging category limits
43+
public let completionFreshLimit = TimeInterval(minutes: 6)
44+
public let completionAgingLimit = TimeInterval(minutes: 16)
45+
public let completionStaleLimit = TimeInterval(hours: 12)
4146

4247
public let batteryReplacementDetectionThreshold = 0.5
4348

LoopUI/Views/GlucoseHUDView.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,27 +91,27 @@ public final class GlucoseHUDView: BaseHUDView {
9191
}
9292
}
9393

94-
public func setGlucoseQuantity(_ glucoseQuantity: Double, at glucoseStartDate: Date, unit: HKUnit, sensor: SensorDisplayable?) {
94+
public func setGlucoseQuantity(_ glucoseQuantity: Double, at glucoseStartDate: Date, unit: HKUnit, staleGlucoseAge: TimeInterval, sensor: SensorDisplayable?) {
9595
var accessibilityStrings = [String]()
9696

9797
let time = timeFormatter.string(from: glucoseStartDate)
9898
caption?.text = time
9999

100-
let sensorDataCurrent = glucoseStartDate.timeIntervalSinceNow > TimeInterval(minutes: -15)
100+
let glucoseValueCurrent = glucoseStartDate.timeIntervalSinceNow > -staleGlucoseAge
101101

102102
let numberFormatter = NumberFormatter.glucoseFormatter(for: unit)
103103
if let valueString = numberFormatter.string(from: glucoseQuantity) {
104-
if sensorDataCurrent {
104+
if glucoseValueCurrent {
105105
glucoseLabel.text = valueString
106106
} else {
107-
glucoseLabel.text = "-"
107+
glucoseLabel.text = "---"
108108
}
109109
accessibilityStrings.append(String(format: LocalizedString("%1$@ at %2$@", comment: "Accessbility format value describing glucose: (1: glucose number)(2: glucose time)"), valueString, time))
110110
}
111111

112112
var unitStrings = [unit.localizedShortUnitString]
113113

114-
if let trend = sensor?.trendType, sensorDataCurrent {
114+
if let trend = sensor?.trendType, glucoseValueCurrent {
115115
unitStrings.append(trend.symbol)
116116
accessibilityStrings.append(trend.localizedDescription)
117117
}

LoopUI/Views/LoopCompletionHUDView.swift

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import UIKit
1010
import LoopKitUI
11+
import LoopCore
1112

1213
public final class LoopCompletionHUDView: BaseHUDView {
1314

@@ -17,14 +18,7 @@ public final class LoopCompletionHUDView: BaseHUDView {
1718
return 1
1819
}
1920

20-
enum Freshness {
21-
case fresh
22-
case aging
23-
case stale
24-
case unknown
25-
}
26-
27-
private(set) var freshness = Freshness.unknown {
21+
private(set) var freshness = LoopCompletionFreshness.unknown {
2822
didSet {
2923
updateTintColor()
3024
}
@@ -128,17 +122,8 @@ public final class LoopCompletionHUDView: BaseHUDView {
128122
@objc private func updateDisplay(_: Timer?) {
129123
if let date = lastLoopCompleted {
130124
let ago = abs(min(0, date.timeIntervalSinceNow))
131-
132-
switch ago {
133-
case let t where t <= .minutes(6):
134-
freshness = .fresh
135-
case let t where t <= .minutes(16):
136-
freshness = .aging
137-
case let t where t <= .hours(12):
138-
freshness = .stale
139-
default:
140-
freshness = .unknown
141-
}
125+
126+
freshness = LoopCompletionFreshness(age: ago)
142127

143128
if let timeString = formatter.string(from: ago) {
144129
switch traitCollection.preferredContentSizeCategory {

WatchApp Extension/ComplicationController.swift

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,24 @@ final class ComplicationController: NSObject, CLKComplicationDataSource {
7676
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: (@escaping (CLKComplicationTimelineEntry?) -> Void)) {
7777
updateChartManagerIfNeeded(completion: {
7878
let entry: CLKComplicationTimelineEntry?
79-
80-
if let context = ExtensionDelegate.shared().loopManager.activeContext,
81-
let glucoseDate = context.glucoseDate,
82-
glucoseDate.timeIntervalSinceNow.minutes >= -15,
83-
let template = CLKComplicationTemplate.templateForFamily(complication.family, from: context, chartGenerator: self.makeChart)
79+
80+
let settings = ExtensionDelegate.shared().loopManager.settings
81+
let timelineDate = Date()
82+
83+
if let context = ExtensionDelegate.shared().loopManager.activeContext,
84+
let template = CLKComplicationTemplate.templateForFamily(complication.family,
85+
from: context,
86+
at: timelineDate,
87+
recencyInterval: settings.recencyInterval,
88+
chartGenerator: self.makeChart)
8489
{
8590
switch complication.family {
8691
case .graphicRectangular:
8792
break
8893
default:
8994
template.tintColor = .tintColor
9095
}
91-
entry = CLKComplicationTimelineEntry(date: glucoseDate, complicationTemplate: template)
96+
entry = CLKComplicationTimelineEntry(date: timelineDate, complicationTemplate: template)
9297
} else {
9398
entry = nil
9499
}
@@ -97,22 +102,31 @@ final class ComplicationController: NSObject, CLKComplicationDataSource {
97102
})
98103
}
99104

100-
func getTimelineEntries(for complication: CLKComplication, before date: Date, limit: Int, withHandler handler: (@escaping ([CLKComplicationTimelineEntry]?) -> Void)) {
101-
// Call the handler with the timeline entries prior to the given date
102-
handler(nil)
103-
}
104-
105105
func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: (@escaping ([CLKComplicationTimelineEntry]?) -> Void)) {
106106
updateChartManagerIfNeeded {
107107
let entries: [CLKComplicationTimelineEntry]?
108-
109-
if let context = ExtensionDelegate.shared().loopManager.activeContext,
110-
let glucoseDate = context.glucoseDate,
111-
glucoseDate.timeIntervalSince(date) > 0,
112-
let template = CLKComplicationTemplate.templateForFamily(complication.family, from: context, chartGenerator: self.makeChart)
108+
109+
let settings = ExtensionDelegate.shared().loopManager.settings
110+
111+
guard let context = ExtensionDelegate.shared().loopManager.activeContext,
112+
let glucoseDate = context.glucoseDate else
113+
{
114+
handler(nil)
115+
return
116+
}
117+
118+
// Stale date is just a second after glucose expires
119+
let staleDate = glucoseDate + settings.recencyInterval + 1
120+
121+
if staleDate > date,
122+
let template = CLKComplicationTemplate.templateForFamily(complication.family,
123+
from: context,
124+
at: staleDate,
125+
recencyInterval: settings.recencyInterval,
126+
chartGenerator: self.makeChart)
113127
{
114128
template.tintColor = UIColor.tintColor
115-
entries = [CLKComplicationTimelineEntry(date: glucoseDate, complicationTemplate: template)]
129+
entries = [CLKComplicationTimelineEntry(date: staleDate, complicationTemplate: template)]
116130
} else {
117131
entries = nil
118132
}

WatchApp Extension/Controllers/HUDInterfaceController.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class HUDInterfaceController: WKInterfaceController {
2929
}
3030
}
3131
}
32+
33+
loopManager.requestGlucoseBackfillIfNecessary()
3234
}
3335

3436
override func didDeactivate() {
@@ -48,9 +50,10 @@ class HUDInterfaceController: WKInterfaceController {
4850
return
4951
}
5052

51-
glucoseLabel.setHidden(true)
53+
glucoseLabel.setText("---")
54+
glucoseLabel.setHidden(false)
5255
eventualGlucoseLabel.setHidden(true)
53-
if let glucose = activeContext.glucose, let unit = activeContext.preferredGlucoseUnit {
56+
if let glucose = activeContext.glucose, let glucoseDate = activeContext.glucoseDate, let unit = activeContext.preferredGlucoseUnit, glucoseDate.timeIntervalSinceNow > -loopManager.settings.recencyInterval {
5457
let formatter = NumberFormatter.glucoseFormatter(for: unit)
5558

5659
if let glucoseValue = formatter.string(from: glucose.doubleValue(for: unit)) {

0 commit comments

Comments
 (0)