Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DoseMathTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.5.6</string>
<string>1.5.7dev</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
2 changes: 1 addition & 1 deletion Loop Status Extension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.5.6</string>
<string>1.5.7dev</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>AppGroupIdentifier</key>
Expand Down
12 changes: 6 additions & 6 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2055,7 +2055,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer: loudnate@gmail.com (XZN842LDLT)";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 48;
CURRENT_PROJECT_VERSION = 49;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
Expand Down Expand Up @@ -2122,7 +2122,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer: loudnate@gmail.com (XZN842LDLT)";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 48;
CURRENT_PROJECT_VERSION = 49;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
Expand Down Expand Up @@ -2364,11 +2364,11 @@
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 48;
CURRENT_PROJECT_VERSION = 49;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 48;
DYLIB_CURRENT_VERSION = 49;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = LoopUI/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
Expand All @@ -2391,11 +2391,11 @@
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 48;
CURRENT_PROJECT_VERSION = 49;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 48;
DYLIB_CURRENT_VERSION = 49;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = LoopUI/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
Expand Down
3 changes: 2 additions & 1 deletion Loop/Extensions/NSUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ extension UserDefaults {
maximumBasalRatePerHour: maximumBasalRatePerHour,
maximumBolus: maximumBolus,
suspendThreshold: suspendThreshold,
retrospectiveCorrectionEnabled: bool(forKey: "com.loudnate.Loop.RetrospectiveCorrectionEnabled")
retrospectiveCorrectionEnabled: bool(forKey: "com.loudnate.Loop.RetrospectiveCorrectionEnabled"),
integralRetrospectiveCorrectionEnabled: bool(forKey: "com.loopkit.Loop.IntegralRetrospectiveCorrectionEnabled")
)
self.loopSettings = settings

Expand Down
2 changes: 1 addition & 1 deletion Loop/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.5.6</string>
<string>1.5.7dev</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand Down
4 changes: 4 additions & 0 deletions Loop/Managers/AnalyticsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ final class AnalyticsManager: IdentifiableClass {
if newValue.retrospectiveCorrectionEnabled != oldValue.retrospectiveCorrectionEnabled {
logEvent("Retrospective correction enabled change")
}

if newValue.integralRetrospectiveCorrectionEnabled != oldValue.integralRetrospectiveCorrectionEnabled {
logEvent("Integral retrospective correction enabled change")
}
}

// MARK: - Loop Events
Expand Down
254 changes: 172 additions & 82 deletions Loop/Managers/LoopDataManager.swift

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion Loop/Models/LoopSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ struct LoopSettings {
var suspendThreshold: GlucoseThreshold? = nil

var retrospectiveCorrectionEnabled = true

var integralRetrospectiveCorrectionEnabled = true
}


Expand Down Expand Up @@ -74,13 +76,18 @@ extension LoopSettings: RawRepresentable {
if let retrospectiveCorrectionEnabled = rawValue["retrospectiveCorrectionEnabled"] as? Bool {
self.retrospectiveCorrectionEnabled = retrospectiveCorrectionEnabled
}

if let integralRetrospectiveCorrectionEnabled = rawValue["integralRetrospectiveCorrectionEnabled"] as? Bool {
self.integralRetrospectiveCorrectionEnabled = integralRetrospectiveCorrectionEnabled
}
}

var rawValue: RawValue {
var raw: RawValue = [
"version": LoopSettings.version,
"dosingEnabled": dosingEnabled,
"retrospectiveCorrectionEnabled": retrospectiveCorrectionEnabled
"retrospectiveCorrectionEnabled": retrospectiveCorrectionEnabled,
"integralRetrospectiveCorrectionEnabled": integralRetrospectiveCorrectionEnabled
]

raw["glucoseTargetRangeSchedule"] = glucoseTargetRangeSchedule?.rawValue
Expand Down
103 changes: 79 additions & 24 deletions Loop/View Controllers/PredictionTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas
static let count = 3
}

fileprivate enum SettingsRow: Int, CaseCountable {
case retrospectiveCorrection
case integralRetrospectiveCorrection

static let count = 2
}

private var eventualGlucoseDescription: String?

private var availableInputs: [PredictionInputEffect] = [.carbs, .insulin, .momentum, .retrospection]
Expand All @@ -204,7 +211,7 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas
case .inputs:
return availableInputs.count
case .settings:
return 1
return SettingsRow.count
}
}

Expand All @@ -231,12 +238,22 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas
case .settings:
let cell = tableView.dequeueReusableCell(withIdentifier: SwitchTableViewCell.className, for: indexPath) as! SwitchTableViewCell

cell.titleLabel?.text = NSLocalizedString("Enable Retrospective Correction", comment: "Title of the switch which toggles retrospective correction effects")
cell.subtitleLabel?.text = NSLocalizedString("This will more aggresively increase or decrease basal delivery when glucose movement doesn't match the carbohydrate and insulin-based model.", comment: "The description of the switch which toggles retrospective correction effects")
cell.`switch`?.isOn = deviceManager.loopManager.settings.retrospectiveCorrectionEnabled
cell.`switch`?.addTarget(self, action: #selector(retrospectiveCorrectionSwitchChanged(_:)), for: .valueChanged)

cell.contentView.layoutMargins.left = tableView.separatorInset.left
switch SettingsRow(rawValue: indexPath.row)! {
case .retrospectiveCorrection:
cell.titleLabel?.text = NSLocalizedString("Retrospective Correction", comment: "Title of the switch which toggles retrospective correction effects")
cell.subtitleLabel?.text = NSLocalizedString("More agressively increase or decrease basal delivery when glucose movement over past 30 min doesn't match the carbohydrate and insulin-based model.", comment: "The description of the switch which toggles retrospective correction effects")
cell.`switch`?.isOn = deviceManager.loopManager.settings.retrospectiveCorrectionEnabled
cell.`switch`?.addTarget(self, action: #selector(retrospectiveCorrectionSwitchChanged(_:)), for: .valueChanged)

cell.contentView.layoutMargins.left = tableView.separatorInset.left
case .integralRetrospectiveCorrection:
cell.titleLabel?.text = NSLocalizedString("Integral Retrospective Correction", comment: "Title of the switch which toggles integral retrospective correction effects")
cell.subtitleLabel?.text = NSLocalizedString("Respond more aggressively to persistent discrepancies in glucose movement.", comment: "The description of the switch which toggles integral retrospective correction effects")
cell.`switch`?.isOn = deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled
cell.`switch`?.addTarget(self, action: #selector(integralRetrospectiveCorrectionSwitchChanged(_:)), for: .valueChanged)

cell.contentView.layoutMargins.left = tableView.separatorInset.left
}

return cell
}
Expand Down Expand Up @@ -267,22 +284,48 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas

var subtitleText = input.localizedDescription(forGlucoseUnit: charts.glucoseUnit) ?? ""

if input == .retrospection,
let startGlucose = retrospectivePredictedGlucose?.first,
let endGlucose = retrospectivePredictedGlucose?.last,
let currentGlucose = self.deviceManager.loopManager.glucoseStore.latestGlucose
{
let formatter = NumberFormatter.glucoseFormatter(for: charts.glucoseUnit)
let values = [startGlucose, endGlucose, currentGlucose].map { formatter.string(from: NSNumber(value: $0.quantity.doubleValue(for: charts.glucoseUnit))) ?? "?" }
let showRetrospectiveCorrectionEffect = formatter.string(from: NSNumber(value: self.deviceManager.loopManager.overallRetrospectiveCorrection.doubleValue(for: charts.glucoseUnit))) ?? "?"
let integralRCIndicator = self.deviceManager.loopManager.integralRectrospectiveCorrectionIndicator

let retro = String(
format: NSLocalizedString("Last comparison: %1$@ → %2$@ vs %3$@, RC: %4$@", comment: "Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose)(4: Overall retrospective correction effect)"),
values[0], values[1], values[2], showRetrospectiveCorrectionEffect
) + integralRCIndicator

subtitleText = String(format: "%@\n%@", subtitleText, retro)
if input == .retrospection {
if deviceManager.loopManager.settings.retrospectiveCorrectionEnabled,
let startGlucose = retrospectivePredictedGlucose?.first,
let endGlucose = retrospectivePredictedGlucose?.last,
let currentGlucose = self.deviceManager.loopManager.glucoseStore.latestGlucose
{
let formatter = NumberFormatter.glucoseFormatter(for: charts.glucoseUnit)
let values = [startGlucose, endGlucose, currentGlucose].map { formatter.string(from: NSNumber(value: $0.quantity.doubleValue(for: charts.glucoseUnit))) ?? "?" }
let endGlucoseValue = endGlucose.quantity.doubleValue(for: charts.glucoseUnit)
let currentGlucoseValue = currentGlucose.quantity.doubleValue(for: charts.glucoseUnit)
let currentDiscrepancyValue = currentGlucoseValue - endGlucoseValue
let currentDiscrepancy = formatter.string(from: NSNumber(value: currentDiscrepancyValue))!
var integralEffect = "none"
var retrospectiveCorrection = "none"
if self.deviceManager.loopManager.overallRetrospectiveCorrection != nil {
//Retrospective Correction effect included in glucose prediction
let overallRetrospectiveCorrectionValue = self.deviceManager.loopManager.overallRetrospectiveCorrection!.doubleValue(for: charts.glucoseUnit)
let integralEffectValue = overallRetrospectiveCorrectionValue - currentDiscrepancyValue
integralEffect = formatter.string(from: NSNumber(value: integralEffectValue))!
retrospectiveCorrection = formatter.string(from: NSNumber(value: overallRetrospectiveCorrectionValue))!
}
if !deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled {
integralEffect = "disabled"
}
let retroComparison = String(
format: NSLocalizedString("Last 30 min comparison: %1$@ → %2$@ vs %3$@", comment: "Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose)"),
values[0], values[1], values[2])
let retroCorrection = String(
format: NSLocalizedString("RC effect: %1$@, Integral effect: %2$@\nTotal glucose effect: %3$@", comment: "Format string describing retrospective correction. (1: Current discrepancy)(2: Integral retrospective correction effect)(3: Total retrospective correction effect)"), currentDiscrepancy, integralEffect, retrospectiveCorrection)

subtitleText = String(format: "%@\n%@", retroComparison, retroCorrection)

} else {
// Retrospective Correction disabled or not included in glucose prediction for other reasons
if deviceManager.loopManager.settings.retrospectiveCorrectionEnabled {
let inactiveRetrospectiveCorrection = String(format: NSLocalizedString("Temporarily inactive due to recent calibration or missing data", comment: "Format string describing inactive retrospective correction."))
subtitleText = String(format: "%@", inactiveRetrospectiveCorrection)
} else {
let disabledRetrospectiveCorrection = String(format: NSLocalizedString("⚠️ Retrospective correction is disabled", comment: "Format string describing disabled retrospective correction."))
subtitleText = String(format: "%@\n%@", subtitleText, disabledRetrospectiveCorrection)
}
}
}

cell.subtitleLabel?.text = subtitleText
Expand Down Expand Up @@ -330,11 +373,23 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas

@objc private func retrospectiveCorrectionSwitchChanged(_ sender: UISwitch) {
deviceManager.loopManager.settings.retrospectiveCorrectionEnabled = sender.isOn

// if retrospective correction is disabled, integral retrospective correction must also be disabled
if !sender.isOn {
deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled = sender.isOn
}

if let row = availableInputs.index(where: { $0 == .retrospection }),
let cell = tableView.cellForRow(at: IndexPath(row: row, section: Section.inputs.rawValue)) as? PredictionInputEffectTableViewCell
{
cell.enabled = self.deviceManager.loopManager.settings.retrospectiveCorrectionEnabled
}
}

@objc private func integralRetrospectiveCorrectionSwitchChanged(_ sender: UISwitch) {
deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled = sender.isOn
// if integral retrospective correction is enabled, retrospective correction must also be enabled
if sender.isOn {
deviceManager.loopManager.settings.retrospectiveCorrectionEnabled = sender.isOn
}
}
}
2 changes: 1 addition & 1 deletion LoopTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.5.6</string>
<string>1.5.7dev</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
2 changes: 1 addition & 1 deletion LoopUI/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.5.6</string>
<string>1.5.7dev</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
2 changes: 1 addition & 1 deletion WatchApp Extension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.5.6</string>
<string>1.5.7dev</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
2 changes: 1 addition & 1 deletion WatchApp/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.5.6</string>
<string>1.5.7dev</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down