From 03cb84cc1476ea82cd0fb400f7a53934d1de1c21 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Fri, 28 Sep 2018 22:57:27 -0500 Subject: [PATCH 1/2] Refactor RC as a timeline To provide support for a safe, accurate IRC implementation, this patch provides retrospective correction discrepancies as a timeline, which can be extended for use in integration. --- Common/Models/LoopSettings.swift | 6 +- Loop/Base.lproj/Localizable.strings | 4 +- Loop/Extensions/CollectionType+Loop.swift | 2 +- Loop/Managers/DoseMath.swift | 2 +- Loop/Managers/LoopDataManager.swift | 128 ++++++++---------- .../PredictionTableViewController.swift | 17 ++- Loop/de.lproj/Localizable.strings | 3 - Loop/es.lproj/Localizable.strings | 3 - Loop/fr.lproj/Localizable.strings | 3 - Loop/it.lproj/Localizable.strings | 3 - Loop/nb.lproj/Localizable.strings | 3 - Loop/nl.lproj/Localizable.strings | 3 - Loop/pl.lproj/Localizable.strings | 3 - Loop/ru.lproj/Localizable.strings | 3 - Loop/zh-Hans.lproj/Localizable.strings | 3 - LoopUI/Extensions/CollectionType.swift | 8 +- 16 files changed, 79 insertions(+), 115 deletions(-) diff --git a/Common/Models/LoopSettings.swift b/Common/Models/LoopSettings.swift index 3e932b4813..1dcb0cd83a 100644 --- a/Common/Models/LoopSettings.swift +++ b/Common/Models/LoopSettings.swift @@ -23,7 +23,11 @@ struct LoopSettings { var retrospectiveCorrectionEnabled = true - let retrospectiveCorrectionInterval = TimeInterval(minutes: 30) + /// The interval over which to aggregate changes in glucose for retrospective correction + let retrospectiveCorrectionGroupingInterval = TimeInterval(minutes: 30) + + /// The maximum duration over which to integrate retrospective correction changes + let retrospectiveCorrectionIntegrationInterval = TimeInterval(minutes: 30) /// The amount of time since a given date that data should be considered valid let recencyInterval = TimeInterval(minutes: 15) diff --git a/Loop/Base.lproj/Localizable.strings b/Loop/Base.lproj/Localizable.strings index 795ecbbb05..fdcaafce26 100644 --- a/Loop/Base.lproj/Localizable.strings +++ b/Loop/Base.lproj/Localizable.strings @@ -247,8 +247,8 @@ /* The title text for the issue report cell */ "Issue Report" = "Issue Report"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Last comparison: %1$@ → %2$@ vs %3$@"; +/* Format string describing retrospective glucose prediction comparison. (1: Predicted glucose)(2: Actual glucose)(3: difference) */ +"prediction-description-retrospective-correction" = "Predicted: %1$@\nActual: %2$@ (%3$@)"; /* Glucose HUD accessibility hint */ "Launches CGM app" = "Launches CGM app"; diff --git a/Loop/Extensions/CollectionType+Loop.swift b/Loop/Extensions/CollectionType+Loop.swift index 2af61c8f4e..6833b7a84c 100644 --- a/Loop/Extensions/CollectionType+Loop.swift +++ b/Loop/Extensions/CollectionType+Loop.swift @@ -10,7 +10,7 @@ import Foundation import LoopKit -public extension Sequence where Iterator.Element: TimelineValue { +public extension Sequence where Element: TimelineValue { /// Returns the closest element index in the sorted sequence prior to the specified date /// /// - parameter date: The date to use in the search diff --git a/Loop/Managers/DoseMath.swift b/Loop/Managers/DoseMath.swift index 1192d01cc5..3366170c4a 100644 --- a/Loop/Managers/DoseMath.swift +++ b/Loop/Managers/DoseMath.swift @@ -201,7 +201,7 @@ private func targetGlucoseValue(percentEffectDuration: Double, minValue: Double, } -extension Collection where Iterator.Element == GlucoseValue { +extension Collection where Element == GlucoseValue { /// For a collection of glucose prediction, determine the least amount of insulin delivered at /// `date` to correct the predicted glucose to the middle of `correctionRange` at the time of prediction. diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index 350415712e..32a455df17 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -22,8 +22,6 @@ final class LoopDataManager { static let LoopUpdateContextKey = "com.loudnate.Loop.LoopDataManager.LoopUpdateContext" - fileprivate typealias GlucoseChange = (start: GlucoseValue, end: GlucoseValue) - let carbStore: CarbStore let doseStore: DoseStore @@ -108,7 +106,6 @@ final class LoopDataManager { self.logger.info("Received notification of glucose samples changing") self.glucoseMomentumEffect = nil - self.retrospectiveGlucoseChange = nil self.notify(forChange: .glucose) } @@ -136,7 +133,7 @@ final class LoopDataManager { predictedGlucose = nil // Carb data may be back-dated, so re-calculate the retrospective glucose. - retrospectivePredictedGlucose = nil + retrospectiveGlucoseDiscrepancies = nil } } private var insulinEffect: [GlucoseEffect]? { @@ -155,12 +152,12 @@ final class LoopDataManager { } } - /// The change in glucose over the reflection time interval (default is 30 min) - fileprivate var retrospectiveGlucoseChange: GlucoseChange? { + private var retrospectiveGlucoseDiscrepancies: [GlucoseEffect]? { didSet { - retrospectivePredictedGlucose = nil + retrospectiveGlucoseDiscrepanciesSummed = retrospectiveGlucoseDiscrepancies?.combinedSums(of: settings.retrospectiveCorrectionGroupingInterval * 1.01) } } + private var retrospectiveGlucoseDiscrepanciesSummed: [GlucoseChange]? fileprivate var predictedGlucose: [GlucoseValue]? { didSet { @@ -168,11 +165,7 @@ final class LoopDataManager { recommendedBolus = nil } } - fileprivate var retrospectivePredictedGlucose: [GlucoseValue]? { - didSet { - retrospectiveGlucoseEffect = [] - } - } + fileprivate var recommendedTempBasal: (recommendation: TempBasalRecommendation, date: Date)? fileprivate var recommendedBolus: (recommendation: BolusRecommendation, date: Date)? @@ -615,19 +608,11 @@ extension LoopDataManager { throw LoopError.missingDataError(.glucose) } - let retrospectiveStart = lastGlucoseDate.addingTimeInterval(-settings.retrospectiveCorrectionInterval) + let retrospectiveStart = lastGlucoseDate.addingTimeInterval(-settings.retrospectiveCorrectionIntegrationInterval) let earliestEffectDate = Date(timeIntervalSinceNow: .hours(-24)) let nextEffectDate = insulinCounteractionEffects.last?.endDate ?? earliestEffectDate - if retrospectiveGlucoseChange == nil { - updateGroup.enter() - glucoseStore.getGlucoseChange(start: retrospectiveStart) { (change) in - self.retrospectiveGlucoseChange = change - updateGroup.leave() - } - } - if glucoseMomentumEffect == nil { updateGroup.enter() glucoseStore.getRecentMomentumEffect { (effects) -> Void in @@ -638,7 +623,7 @@ extension LoopDataManager { if insulinEffect == nil { updateGroup.enter() - doseStore.getGlucoseEffects(start: min(retrospectiveStart, nextEffectDate)) { (result) -> Void in + doseStore.getGlucoseEffects(start: nextEffectDate) { (result) -> Void in switch result { case .failure(let error): self.logger.error(error) @@ -700,7 +685,7 @@ extension LoopDataManager { _ = updateGroup.wait(timeout: .distantFuture) - if retrospectivePredictedGlucose == nil { + if retrospectiveGlucoseDiscrepancies == nil { do { try updateRetrospectiveGlucoseEffect() } catch let error { @@ -794,7 +779,7 @@ extension LoopDataManager { var prediction = LoopMath.predictGlucose(startingAt: glucose, momentum: momentum, effects: effects) // Dosing requires prediction entries at least as long as the insulin model duration. - // If our prediciton is shorter than that, then extend it here. + // If our prediction is shorter than that, then extend it here. let finalDate = glucose.startDate.addingTimeInterval(model.effectDuration) if let last = prediction.last, last.startDate < finalDate { prediction.append(PredictedGlucoseValue(startDate: finalDate, quantity: last.quantity)) @@ -803,52 +788,39 @@ extension LoopDataManager { return prediction } - /** - Runs the glucose retrospective analysis using the latest effect data. - - *This method should only be called from the `dataAccessQueue`* - */ + /// Generates an effect based on how large the discrepancy is between the current glucose and its predicted value. + /// + /// - Parameter effectDuration: The length of time to extend the effect + /// - Throws: LoopError.missingDataError private func updateRetrospectiveGlucoseEffect(effectDuration: TimeInterval = TimeInterval(minutes: 60)) throws { dispatchPrecondition(condition: .onQueue(dataAccessQueue)) - - guard let carbEffect = self.carbEffect else { - self.retrospectivePredictedGlucose = nil + + guard let carbEffects = self.carbEffect else { + retrospectiveGlucoseDiscrepancies = nil + retrospectiveGlucoseEffect = [] throw LoopError.missingDataError(.carbEffect) } - guard let insulinEffect = self.insulinEffect else { - self.retrospectivePredictedGlucose = nil - throw LoopError.missingDataError(.insulinEffect) - } + retrospectiveGlucoseDiscrepancies = insulinCounteractionEffects.subtracting(carbEffects, withUniformInterval: carbStore.delta) - guard let change = retrospectiveGlucoseChange else { - self.retrospectivePredictedGlucose = nil - return // Expected case for calibrations + // Our last change should be recent, otherwise clear the effects + guard let discrepancy = retrospectiveGlucoseDiscrepanciesSummed?.last, + Date().timeIntervalSince(discrepancy.endDate) <= settings.recencyInterval + else { + retrospectiveGlucoseEffect = [] + return } - // Run a retrospective prediction over the duration of the recorded glucose change, using the current carb and insulin effects - let startDate = change.start.startDate - let endDate = change.end.endDate - let retrospectivePrediction = LoopMath.predictGlucose(startingAt: change.start, effects: - carbEffect.filterDateRange(startDate, endDate), - insulinEffect.filterDateRange(startDate, endDate) - ) - - self.retrospectivePredictedGlucose = retrospectivePrediction - - guard let lastGlucose = retrospectivePrediction.last else { return } - let glucoseUnit = HKUnit.milligramsPerDeciliter - let velocityUnit = glucoseUnit.unitDivided(by: HKUnit.second()) - - let discrepancy = change.end.quantity.doubleValue(for: glucoseUnit) - lastGlucose.quantity.doubleValue(for: glucoseUnit) // mg/dL + guard let glucose = self.glucoseStore.latestGlucose else { + retrospectiveGlucoseEffect = [] + throw LoopError.missingDataError(.glucose) + } - // Determine the interval of discrepancy, requiring a minimum of the configured interval to avoid magnifying effects from short intervals - let discrepancyTime = max(change.end.endDate.timeIntervalSince(change.start.endDate), settings.retrospectiveCorrectionInterval) - let velocity = HKQuantity(unit: velocityUnit, doubleValue: discrepancy / discrepancyTime) - let type = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodGlucose)! - let glucose = HKQuantitySample(type: type, quantity: change.end.quantity, start: change.end.startDate, end: change.end.endDate) + let unit = HKUnit.milligramsPerDeciliter + let discrepancyTime = max(discrepancy.endDate.timeIntervalSince(discrepancy.startDate), settings.retrospectiveCorrectionGroupingInterval) + let velocity = HKQuantity(unit: unit.unitDivided(by: .second()), doubleValue: discrepancy.quantity.doubleValue(for: unit) / discrepancyTime) - self.retrospectiveGlucoseEffect = glucose.decayEffect(atRate: velocity, for: effectDuration) + retrospectiveGlucoseEffect = glucose.decayEffect(atRate: velocity, for: effectDuration) } /// Runs the glucose prediction on the latest effect data. @@ -1003,9 +975,9 @@ protocol LoopState { var recommendedTempBasal: (recommendation: TempBasalRecommendation, date: Date)? { get } var recommendedBolus: (recommendation: BolusRecommendation, date: Date)? { get } - - /// The retrospective prediction over a recent period of glucose samples - var retrospectivePredictedGlucose: [GlucoseValue]? { get } + + /// The difference in predicted vs actual glucose over a recent period + var retrospectiveGlucoseDiscrepancies: [GlucoseChange]? { get } /// Calculates a new prediction from the current data using the specified effect inputs /// @@ -1063,9 +1035,9 @@ extension LoopDataManager { return loopDataManager.recommendedBolus } - var retrospectivePredictedGlucose: [GlucoseValue]? { + var retrospectiveGlucoseDiscrepancies: [GlucoseChange]? { dispatchPrecondition(condition: .onQueue(loopDataManager.dataAccessQueue)) - return loopDataManager.retrospectivePredictedGlucose + return loopDataManager.retrospectiveGlucoseDiscrepanciesSummed } func predictGlucose(using inputs: PredictionInputEffect) throws -> [GlucoseValue] { @@ -1105,7 +1077,7 @@ extension LoopDataManager { func generateDiagnosticReport(_ completion: @escaping (_ report: String) -> Void) { getLoopState { (manager, state) in - var entries = [ + var entries: [String] = [ "## LoopDataManager", "settings: \(String(reflecting: manager.settings))", @@ -1117,16 +1089,16 @@ extension LoopDataManager { "]", "insulinEffect: [", - "* GlucoseEffect(start, end, md/dL", + "* GlucoseEffect(start, mg/dL)", (manager.insulinEffect ?? []).reduce(into: "", { (entries, entry) in - entries.append("* \(entry.startDate), \(entry.endDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + entries.append("* \(entry.startDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") }), "]", "carbEffect: [", - "* GlucoseEffect(start, end, md/dL", + "* GlucoseEffect(start, mg/dL)", (manager.carbEffect ?? []).reduce(into: "", { (entries, entry) in - entries.append("* \(entry.startDate), \(entry.endDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + entries.append("* \(entry.startDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") }), "]", @@ -1137,13 +1109,25 @@ extension LoopDataManager { }), "]", - "retrospectivePredictedGlucose: \(state.retrospectivePredictedGlucose ?? [])", + "retrospectiveGlucoseDiscrepancies: [", + "* GlucoseEffect(start, mg/dL)", + (manager.retrospectiveGlucoseDiscrepancies ?? []).reduce(into: "", { (entries, entry) in + entries.append("* \(entry.startDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + }), + "]", + + "retrospectiveGlucoseDiscrepanciesSummed: [", + "* GlucoseChange(start, end, mg/dL)", + (manager.retrospectiveGlucoseDiscrepanciesSummed ?? []).reduce(into: "", { (entries, entry) in + entries.append("* \(entry.startDate), \(entry.endDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + }), + "]", + "glucoseMomentumEffect: \(manager.glucoseMomentumEffect ?? [])", "retrospectiveGlucoseEffect: \(manager.retrospectiveGlucoseEffect)", "recommendedTempBasal: \(String(describing: state.recommendedTempBasal))", "recommendedBolus: \(String(describing: state.recommendedBolus))", "lastBolus: \(String(describing: manager.lastRequestedBolus))", - "retrospectiveGlucoseChange: \(String(describing: manager.retrospectiveGlucoseChange))", "lastLoopCompleted: \(String(describing: manager.lastLoopCompleted))", "lastTempBasal: \(String(describing: state.lastTempBasal))", "carbsOnBoard: \(String(describing: state.carbsOnBoard))", diff --git a/Loop/View Controllers/PredictionTableViewController.swift b/Loop/View Controllers/PredictionTableViewController.swift index fdb529f954..14e8375b56 100644 --- a/Loop/View Controllers/PredictionTableViewController.swift +++ b/Loop/View Controllers/PredictionTableViewController.swift @@ -67,7 +67,7 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas // MARK: - State - private var retrospectivePredictedGlucose: [GlucoseValue]? + private var retrospectiveGlucoseDiscrepancies: [GlucoseChange]? private var refreshContext = RefreshContext.all @@ -113,7 +113,7 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas _ = self.refreshContext.remove(.status) reloadGroup.enter() self.deviceManager.loopManager.getLoopState { (manager, state) in - self.retrospectivePredictedGlucose = state.retrospectivePredictedGlucose + self.retrospectiveGlucoseDiscrepancies = state.retrospectiveGlucoseDiscrepancies self.charts.setPredictedGlucoseValues(state.predictedGlucose ?? []) do { @@ -256,15 +256,18 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas var subtitleText = input.localizedDescription(forGlucoseUnit: charts.glucoseUnit) ?? "" if input == .retrospection, - let startGlucose = retrospectivePredictedGlucose?.first, - let endGlucose = retrospectivePredictedGlucose?.last, + let lastDiscrepancy = retrospectiveGlucoseDiscrepancies?.last, let currentGlucose = self.deviceManager.loopManager.glucoseStore.latestGlucose { - let formatter = NumberFormatter.glucoseFormatter(for: charts.glucoseUnit) - let values = [startGlucose, endGlucose, currentGlucose].map { formatter.string(from: $0.quantity.doubleValue(for: charts.glucoseUnit)) ?? "?" } + let formatter = QuantityFormatter() + formatter.setPreferredNumberFormatter(for: charts.glucoseUnit) + let predicted = HKQuantity(unit: charts.glucoseUnit, doubleValue: currentGlucose.quantity.doubleValue(for: charts.glucoseUnit) - lastDiscrepancy.quantity.doubleValue(for: charts.glucoseUnit)) + var values = [predicted, currentGlucose.quantity].map { formatter.string(from: $0, for: charts.glucoseUnit) ?? "?" } + formatter.numberFormatter.positivePrefix = formatter.numberFormatter.plusSign + values.append(formatter.string(from: lastDiscrepancy.quantity, for: charts.glucoseUnit) ?? "?" ) let retro = String( - format: NSLocalizedString("Last comparison: %1$@ → %2$@ vs %3$@", comment: "Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose)"), + format: NSLocalizedString("prediction-description-retrospective-correction", comment: "Format string describing retrospective glucose prediction comparison. (1: Predicted glucose)(2: Actual glucose)(3: difference)"), values[0], values[1], values[2] ) diff --git a/Loop/de.lproj/Localizable.strings b/Loop/de.lproj/Localizable.strings index b3a0814f9f..73db5816ed 100644 --- a/Loop/de.lproj/Localizable.strings +++ b/Loop/de.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Problembericht"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Letzter Vergleich: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Startet die CGM-App"; diff --git a/Loop/es.lproj/Localizable.strings b/Loop/es.lproj/Localizable.strings index 1b3fdb51a6..313e2b5464 100644 --- a/Loop/es.lproj/Localizable.strings +++ b/Loop/es.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Informe de Errores"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Última comparación: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Lanza app MCG"; diff --git a/Loop/fr.lproj/Localizable.strings b/Loop/fr.lproj/Localizable.strings index 56d14c322d..cf32d7c90c 100644 --- a/Loop/fr.lproj/Localizable.strings +++ b/Loop/fr.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Editer rapport"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Dernière comparaison: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Lance Application CGM"; diff --git a/Loop/it.lproj/Localizable.strings b/Loop/it.lproj/Localizable.strings index 7075cf1edf..83e6ce0170 100644 --- a/Loop/it.lproj/Localizable.strings +++ b/Loop/it.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Segnalazione"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Ultimo confronto: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Avvia CGM app"; diff --git a/Loop/nb.lproj/Localizable.strings b/Loop/nb.lproj/Localizable.strings index bb9d1069bc..0327aa145f 100644 --- a/Loop/nb.lproj/Localizable.strings +++ b/Loop/nb.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Hendelsesrapport"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Siste sammenligning: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Starter CGM app"; diff --git a/Loop/nl.lproj/Localizable.strings b/Loop/nl.lproj/Localizable.strings index 8fd598f9aa..aa7fe74b94 100644 --- a/Loop/nl.lproj/Localizable.strings +++ b/Loop/nl.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Incidenten rapportage"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Laatste vergelijking: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Start de CGM app op"; diff --git a/Loop/pl.lproj/Localizable.strings b/Loop/pl.lproj/Localizable.strings index 9a6383adea..b77dd05ba1 100644 --- a/Loop/pl.lproj/Localizable.strings +++ b/Loop/pl.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Zgłaszanie błędów"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Ostatnie porównanie: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Uruchamia aplikację CGM"; diff --git a/Loop/ru.lproj/Localizable.strings b/Loop/ru.lproj/Localizable.strings index cc41fc09fb..c23a1eb427 100644 --- a/Loop/ru.lproj/Localizable.strings +++ b/Loop/ru.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "Сообщить об ошибе"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "Недавнее сравнение %1$@ → %2$@ и %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "Запускает приложение непрерывного мониторинга CGM"; diff --git a/Loop/zh-Hans.lproj/Localizable.strings b/Loop/zh-Hans.lproj/Localizable.strings index 9da1ff3b01..0e79f3540a 100644 --- a/Loop/zh-Hans.lproj/Localizable.strings +++ b/Loop/zh-Hans.lproj/Localizable.strings @@ -247,9 +247,6 @@ /* The title text for the issue report cell */ "Issue Report" = "反馈问题"; -/* Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose) */ -"Last comparison: %1$@ → %2$@ vs %3$@" = "的最后一次对比: %1$@ → %2$@ vs %3$@"; - /* Glucose HUD accessibility hint */ "Launches CGM app" = "启动CGM软件"; diff --git a/LoopUI/Extensions/CollectionType.swift b/LoopUI/Extensions/CollectionType.swift index cb0444941c..c6b80f181e 100644 --- a/LoopUI/Extensions/CollectionType.swift +++ b/LoopUI/Extensions/CollectionType.swift @@ -9,7 +9,7 @@ import Foundation -extension BidirectionalCollection where Index: Strideable, Iterator.Element: Comparable, Index.Stride == Int { +extension BidirectionalCollection where Index: Strideable, Element: Comparable, Index.Stride == Int { /** Returns the insertion index of a new value in a sorted collection @@ -20,7 +20,7 @@ extension BidirectionalCollection where Index: Strideable, Iterator.Element: Com - returns: The appropriate insertion index, between `startIndex` and `endIndex` */ - func findInsertionIndex(for value: Iterator.Element) -> Index { + func findInsertionIndex(for value: Element) -> Index { var low = startIndex var high = endIndex @@ -39,7 +39,7 @@ extension BidirectionalCollection where Index: Strideable, Iterator.Element: Com } -extension BidirectionalCollection where Index: Strideable, Iterator.Element: Strideable, Index.Stride == Int { +extension BidirectionalCollection where Index: Strideable, Element: Strideable, Index.Stride == Int { /** Returns the index of the closest element to a specified value in a sorted collection @@ -47,7 +47,7 @@ extension BidirectionalCollection where Index: Strideable, Iterator.Element: Str - returns: The index of the closest element, or nil if the collection is empty */ - func findClosestElementIndex(matching value: Iterator.Element) -> Index? { + func findClosestElementIndex(matching value: Element) -> Index? { let upperBound = findInsertionIndex(for: value) if upperBound == startIndex { From 542219ac1b2c3687a1a38c3397951a08e14f8366 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sat, 29 Sep 2018 00:41:53 -0500 Subject: [PATCH 2/2] Bump LoopKit dev revision --- Cartfile.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 1676b87ad7..fc3216c8a5 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,7 +1,7 @@ github "LoopKit/Amplitude-iOS" "2137d5fd44bf630ed33e1e72d7af6d8f8612f270" github "LoopKit/CGMBLEKit" "97433c2c546b50268bbfe2e1a42cb377c6066e78" github "LoopKit/G4ShareSpy" "47931f1ee70f1081db6eebf3a68bb8f4146e5fca" -github "LoopKit/LoopKit" "8c553f7bd03c2032b146e4a52d58d527a4285c15" +github "LoopKit/LoopKit" "f130260f76d5e58a5984045484331a363e1b7080" github "LoopKit/dexcom-share-client-swift" "f0125aed8960586a9473dca02d17b3e5969558ac" github "i-schuetz/SwiftCharts" "0.6.2" github "ps2/rileylink_ios" "49a43189c1606875d27133589c6d6b92fbd3a0a1"