From a317c98468e9575321a47a2eec74e91020ee29c2 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sat, 24 Oct 2026 18:12:47 -0500 Subject: [PATCH] Report Guard + Runway in analytics Add it to AnalyticsState alongside the other two experiments: the user property, the algorithm experiment change event and the daily summary. Daily summaries stored before this lack the field and decode it as off. --- Loop/Managers/AnalyticsServicesManager.swift | 24 ++++++++++++++++++++ Loop/Managers/LoopAppManager.swift | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Loop/Managers/AnalyticsServicesManager.swift b/Loop/Managers/AnalyticsServicesManager.swift index 6ca7e0144f..f24b3aa8d6 100644 --- a/Loop/Managers/AnalyticsServicesManager.swift +++ b/Loop/Managers/AnalyticsServicesManager.swift @@ -235,6 +235,7 @@ final class AnalyticsServicesManager { identify("Dosing Strategy", value: state.dosingStrategy) identify("Integral Retrospective Correction", value: state.integralRetrospectiveCorrection ? "On" : "Off") identify("Glucose Based Partial Application", value: state.glucoseBasedPartialApplication ? "On" : "Off") + identify("Guard + Runway", value: state.guardAndRunway ? "On" : "Off") lastState = state } @@ -274,6 +275,9 @@ final class AnalyticsServicesManager { if newState.glucoseBasedPartialApplication != oldState.glucoseBasedPartialApplication { logEvent("Algorithm experiment change", withProperties: ["experiment": "Glucose Based Partial Application", "enabled": newState.glucoseBasedPartialApplication]) } + if newState.guardAndRunway != oldState.guardAndRunway { + logEvent("Algorithm experiment change", withProperties: ["experiment": "Guard + Runway", "enabled": newState.guardAndRunway]) + } } func didDisplayAlert(identifier: String, interruptionLevel: Alert.InterruptionLevel, scheduled: Bool) { @@ -333,6 +337,25 @@ struct AnalyticsState: Codable, Equatable { var dosingStrategy: String var integralRetrospectiveCorrection: Bool var glucoseBasedPartialApplication: Bool + var guardAndRunway: Bool + + init(closedLoop: Bool, dosingStrategy: String, integralRetrospectiveCorrection: Bool, glucoseBasedPartialApplication: Bool, guardAndRunway: Bool) { + self.closedLoop = closedLoop + self.dosingStrategy = dosingStrategy + self.integralRetrospectiveCorrection = integralRetrospectiveCorrection + self.glucoseBasedPartialApplication = glucoseBasedPartialApplication + self.guardAndRunway = guardAndRunway + } + + // Summaries stored before guardAndRunway was added lack it + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + closedLoop = try container.decode(Bool.self, forKey: .closedLoop) + dosingStrategy = try container.decode(String.self, forKey: .dosingStrategy) + integralRetrospectiveCorrection = try container.decode(Bool.self, forKey: .integralRetrospectiveCorrection) + glucoseBasedPartialApplication = try container.decode(Bool.self, forKey: .glucoseBasedPartialApplication) + guardAndRunway = try container.decodeIfPresent(Bool.self, forKey: .guardAndRunway) ?? false + } } struct AnalyticsDailySummary: Codable, Equatable { @@ -365,6 +388,7 @@ struct AnalyticsDailySummary: Codable, Equatable { properties["dosingStrategy"] = state.dosingStrategy properties["integralRetrospectiveCorrection"] = state.integralRetrospectiveCorrection properties["glucoseBasedPartialApplication"] = state.glucoseBasedPartialApplication + properties["guardAndRunway"] = state.guardAndRunway } return properties } diff --git a/Loop/Managers/LoopAppManager.swift b/Loop/Managers/LoopAppManager.swift index d5e4a31766..6b26304595 100644 --- a/Loop/Managers/LoopAppManager.swift +++ b/Loop/Managers/LoopAppManager.swift @@ -490,7 +490,8 @@ class LoopAppManager: NSObject { closedLoop: settingsManager.dosingEnabled, dosingStrategy: settingsManager.loopSettings.automaticDosingStrategy.analyticsValue, integralRetrospectiveCorrection: UserDefaults.standard.integralRetrospectiveCorrectionEnabled, - glucoseBasedPartialApplication: UserDefaults.standard.glucoseBasedApplicationFactorEnabled + glucoseBasedPartialApplication: UserDefaults.standard.glucoseBasedApplicationFactorEnabled, + guardAndRunway: UserDefaults.standard.guardAndRunwayEnabled ) } analyticsServicesManager.identifyState()