diff --git a/Loop/Base.lproj/Main.storyboard b/Loop/Base.lproj/Main.storyboard
index 9519d4a64d..d7f3763134 100644
--- a/Loop/Base.lproj/Main.storyboard
+++ b/Loop/Base.lproj/Main.storyboard
@@ -343,6 +343,16 @@
+
@@ -350,10 +360,12 @@
+
+
@@ -362,6 +374,7 @@
+
diff --git a/Loop/Managers/DeviceDataManager.swift b/Loop/Managers/DeviceDataManager.swift
index 7732b94ede..089138f43f 100644
--- a/Loop/Managers/DeviceDataManager.swift
+++ b/Loop/Managers/DeviceDataManager.swift
@@ -62,7 +62,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
}
var sensorInfo: SensorDisplayable? {
- return latestGlucoseG5 ?? latestGlucoseG4 ?? latestPumpStatusFromMySentry
+ return latestGlucoseG5 ?? latestGlucoseG4 ?? latestGlucoseFromShare ?? latestPumpStatusFromMySentry
}
// MARK: - RileyLink
@@ -492,6 +492,8 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
private var latestGlucoseG5: xDripG5.Glucose?
+ private var latestGlucoseFromShare: ShareGlucose?
+
/**
Attempts to backfill glucose data from the share servers if a G5 connection hasn't been established.
@@ -519,6 +521,8 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
return
}
+ self.latestGlucoseFromShare = glucose.first
+
// Ignore glucose values that are up to a minute newer than our previous value, to account for possible time shifting in Share data
let newGlucose = glucose.filterDateRange(glucoseStore.latestGlucose?.startDate.dateByAddingTimeInterval(NSTimeInterval(minutes: 1)), nil).map {
return (quantity: $0.quantity, date: $0.startDate, isDisplayOnly: false)
@@ -560,7 +564,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
let includeAfter = glucoseStore.latestGlucose?.startDate.dateByAddingTimeInterval(NSTimeInterval(minutes: 1))
let validGlucose = glucoseHistory.flatMap({
- $0.isValid ? $0 : nil
+ $0.isStateValid ? $0 : nil
}).filterDateRange(includeAfter, nil).map({
(quantity: $0.quantity, date: $0.startDate, isDisplayOnly: $0.isDisplayOnly)
})
diff --git a/Loop/Models/Glucose.swift b/Loop/Models/Glucose.swift
index c0c2db1544..72aec6e4e8 100644
--- a/Loop/Models/Glucose.swift
+++ b/Loop/Models/Glucose.swift
@@ -11,6 +11,10 @@ import xDripG5
extension Glucose: SensorDisplayable {
+ var isStateValid: Bool {
+ return state == .OK && status == .OK
+ }
+
var stateDescription: String {
let status: String
switch self.status {
diff --git a/Loop/Models/GlucoseG4.swift b/Loop/Models/GlucoseG4.swift
index 3510a43eb7..e87623fc51 100644
--- a/Loop/Models/GlucoseG4.swift
+++ b/Loop/Models/GlucoseG4.swift
@@ -12,13 +12,6 @@ import HealthKit
import LoopKit
-extension GlucoseG4 {
- var isValid: Bool {
- return glucose >= 20
- }
-}
-
-
extension GlucoseG4: GlucoseValue {
public var quantity: HKQuantity {
return HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: Double(glucose))
@@ -31,11 +24,15 @@ extension GlucoseG4: GlucoseValue {
extension GlucoseG4: SensorDisplayable {
+ var isStateValid: Bool {
+ return glucose >= 20
+ }
+
var stateDescription: String {
- if isValid {
- return "✓"
+ if isStateValid {
+ return NSLocalizedString("OK", comment: "Sensor state description for the valid state")
} else {
- return String(format: "%02x", glucose)
+ return NSLocalizedString("Needs Attention", comment: "Sensor state description for the non-valid state")
}
}
diff --git a/Loop/Models/MySentryPumpStatusMessageBody.swift b/Loop/Models/MySentryPumpStatusMessageBody.swift
index 631995cfba..f1535c6816 100644
--- a/Loop/Models/MySentryPumpStatusMessageBody.swift
+++ b/Loop/Models/MySentryPumpStatusMessageBody.swift
@@ -11,14 +11,12 @@ import MinimedKit
extension MySentryPumpStatusMessageBody: SensorDisplayable {
- var stateDescription: String {
+ var isStateValid: Bool {
switch glucose {
- case .Active:
- return "✓"
- case .Off:
- return ""
+ case .Active, .Off:
+ return true
default:
- return String(glucose)
+ return false
}
}
diff --git a/Loop/Models/SensorDisplayable.swift b/Loop/Models/SensorDisplayable.swift
index bdcb516a51..7cf7723110 100644
--- a/Loop/Models/SensorDisplayable.swift
+++ b/Loop/Models/SensorDisplayable.swift
@@ -6,11 +6,27 @@
// Copyright © 2016 Nathan Racklyeft. All rights reserved.
//
+import Foundation
+
protocol SensorDisplayable {
- // Describes the state of the sensor in the current localization
+ /// Returns whether the current state is valid
+ var isStateValid: Bool { get }
+
+ /// Describes the state of the sensor in the current localization
var stateDescription: String { get }
/// Enumerates the trend of the sensor values
var trendType: GlucoseTrend? { get }
}
+
+
+extension SensorDisplayable {
+ var stateDescription: String {
+ if isStateValid {
+ return NSLocalizedString("OK", comment: "Sensor state description for the valid state")
+ } else {
+ return NSLocalizedString("Needs Attention", comment: "Sensor state description for the non-valid state")
+ }
+ }
+}
diff --git a/Loop/Models/ShareGlucose+GlucoseKit.swift b/Loop/Models/ShareGlucose+GlucoseKit.swift
index ef93b5694d..8699c1e9a6 100644
--- a/Loop/Models/ShareGlucose+GlucoseKit.swift
+++ b/Loop/Models/ShareGlucose+GlucoseKit.swift
@@ -21,3 +21,14 @@ extension ShareGlucose: GlucoseValue {
return HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: Double(glucose))
}
}
+
+
+extension ShareGlucose: SensorDisplayable {
+ var isStateValid: Bool {
+ return glucose >= 20
+ }
+
+ var trendType: GlucoseTrend? {
+ return GlucoseTrend(rawValue: Int(trend))
+ }
+}
diff --git a/Loop/View Controllers/PredictionTableViewController.swift b/Loop/View Controllers/PredictionTableViewController.swift
index 59c5c0836a..a6577dfc8d 100644
--- a/Loop/View Controllers/PredictionTableViewController.swift
+++ b/Loop/View Controllers/PredictionTableViewController.swift
@@ -8,6 +8,8 @@
import UIKit
import HealthKit
+import LoopKit
+
class PredictionTableViewController: UITableViewController, IdentifiableClass {
@@ -85,12 +87,6 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
var dataManager: DeviceDataManager!
- private var active = true {
- didSet {
- reloadData()
- }
- }
-
private lazy var charts: StatusChartsManager = {
let charts = StatusChartsManager()
@@ -102,6 +98,14 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
return charts
}()
+ private var retrospectivePredictedGlucose: [GlucoseValue]?
+
+ private var active = true {
+ didSet {
+ reloadData()
+ }
+ }
+
private var needsRefresh = true
private var visible = false {
@@ -149,11 +153,12 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
}
dispatch_group_enter(reloadGroup)
- dataManager.loopManager.getLoopStatus { (predictedGlucose, _, _, _, _, _, error) in
+ dataManager.loopManager.getLoopStatus { (predictedGlucose, retrospectivePredictedGlucose, _, _, _, _, error) in
if error != nil {
self.needsRefresh = true
}
+ self.retrospectivePredictedGlucose = retrospectivePredictedGlucose
self.charts.predictedGlucoseValues = predictedGlucose ?? []
dispatch_group_leave(reloadGroup)
@@ -180,7 +185,7 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
self.charts.prerender()
self.tableView.reloadSections(NSIndexSet(indexesInRange: NSMakeRange(Section.charts.rawValue, 1)),
- withRowAnimation: animated ? .Fade : .None
+ withRowAnimation: .None
)
self.reloading = false
@@ -242,11 +247,29 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
let (input, selected) = selectedInputs[indexPath.row]
cell.titleLabel?.text = input.localizedTitle
- cell.subtitleLabel?.text = input.localizedDescription(forGlucoseUnit: charts.glucoseUnit)
cell.accessoryType = selected ? .Checkmark : .None
-
cell.enabled = input != .retrospection || dataManager.loopManager.retrospectiveCorrectionEnabled
+ var subtitleText = input.localizedDescription(forGlucoseUnit: charts.glucoseUnit)
+
+ if input == .retrospection,
+ let startGlucose = retrospectivePredictedGlucose?.first,
+ let endGlucose = retrospectivePredictedGlucose?.last,
+ let currentGlucose = self.dataManager.glucoseStore?.latestGlucose
+ {
+ let formatter = NSNumberFormatter.glucoseFormatter(for: charts.glucoseUnit)
+ let values = [startGlucose, endGlucose, currentGlucose].map { formatter.stringFromNumber($0.quantity.doubleValueForUnit(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)"),
+ values[0], values[1], values[2]
+ )
+
+ subtitleText = String(format: "%@\n%@", subtitleText, retro)
+ }
+
+ cell.subtitleLabel?.text = subtitleText
+
cell.contentView.layoutMargins.left = tableView.separatorInset.left
return cell
diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift
index 8d1ec70104..d2e526434a 100644
--- a/Loop/View Controllers/StatusTableViewController.swift
+++ b/Loop/View Controllers/StatusTableViewController.swift
@@ -136,9 +136,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
needsRefresh = false
reloading = true
- tableView.reloadSections(NSIndexSet(indexesInRange: NSMakeRange(Section.Sensor.rawValue, Section.count - Section.Sensor.rawValue)
- ), withRowAnimation: visible ? .Automatic : .None)
-
let calendar = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.minute = 0
@@ -265,9 +262,8 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
private enum Section: Int {
case Charts = 0
case Status
- case Sensor
- static let count = 3
+ static let count = 2
}
// MARK: - Chart Section Data
@@ -367,32 +363,10 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
// MARK: - Pump/Sensor Section Data
- private enum SensorRow: Int {
- case State
-
- static let count = 1
- }
-
private lazy var emptyValueString: String = NSLocalizedString("––",
comment: "The detail value of a numeric cell with no value"
)
- private lazy var dateComponentsFormatter: NSDateComponentsFormatter = {
- let formatter = NSDateComponentsFormatter()
- formatter.unitsStyle = .Short
-
- return formatter
- }()
-
- private lazy var numberFormatter = NSNumberFormatter()
-
- private lazy var dateFormatter: NSDateFormatter = {
- let formatter = NSDateFormatter()
- formatter.dateStyle = .MediumStyle
- formatter.timeStyle = .MediumStyle
- return formatter
- }()
-
private lazy var timeFormatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateStyle = .NoStyle
@@ -413,8 +387,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
return ChartRow.count
case .Status:
return StatusRow.count
- case .Sensor:
- return SensorRow.count
}
}
@@ -483,18 +455,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
}
}
- return cell
- case .Sensor:
- let cell = tableView.dequeueReusableCellWithIdentifier(UITableViewCell.className, forIndexPath: indexPath)
- cell.selectionStyle = .None
-
- switch SensorRow(rawValue: indexPath.row)! {
- case .State:
- cell.textLabel?.text = NSLocalizedString("Sensor State", comment: "The title of the cell containing the current sensor state")
-
- cell.detailTextLabel?.text = dataManager.sensorInfo?.stateDescription ?? emptyValueString
- }
-
return cell
}
}
@@ -510,7 +470,7 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
case .IOB, .Dose, .COB:
return 85
}
- case .Status, .Sensor:
+ case .Status:
return UITableViewAutomaticDimension
}
}
@@ -548,10 +508,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
}
}
}
- case .Sensor:
- if let URL = NSURL(string: "dexcomcgm://") {
- UIApplication.sharedApplication().openURL(URL)
- }
}
}
diff --git a/Loop/Views/GlucoseHUDView.swift b/Loop/Views/GlucoseHUDView.swift
index bcd05094a7..67b50840b1 100644
--- a/Loop/Views/GlucoseHUDView.swift
+++ b/Loop/Views/GlucoseHUDView.swift
@@ -15,27 +15,32 @@ final class GlucoseHUDView: HUDView {
@IBOutlet private var unitLabel: UILabel! {
didSet {
- unitLabel?.text = "–"
- unitLabel?.textColor = .glucoseTintColor
+ unitLabel.text = "–"
+ unitLabel.textColor = .glucoseTintColor
}
}
@IBOutlet private var glucoseLabel: UILabel! {
didSet {
- glucoseLabel?.text = "–"
- glucoseLabel?.textColor = .glucoseTintColor
+ glucoseLabel.text = "–"
+ glucoseLabel.textColor = .glucoseTintColor
+ }
+ }
+
+ @IBOutlet private var alertLabel: UILabel! {
+ didSet {
+ alertLabel.alpha = 0
+ alertLabel.backgroundColor = UIColor.agingColor
+ alertLabel.textColor = UIColor.whiteColor()
+ alertLabel.layer.cornerRadius = 9
+ alertLabel.clipsToBounds = true
}
}
func set(glucoseValue: GlucoseValue, for unit: HKUnit, from sensor: SensorDisplayable?) {
caption?.text = timeFormatter.stringFromDate(glucoseValue.startDate)
- let numberFormatter = NSNumberFormatter()
- numberFormatter.numberStyle = .DecimalStyle
- numberFormatter.minimumFractionDigits = unit.preferredMinimumFractionDigits
- numberFormatter.usesSignificantDigits = true
- numberFormatter.minimumSignificantDigits = 2
- numberFormatter.maximumSignificantDigits = 3
+ let numberFormatter = NSNumberFormatter.glucoseFormatter(for: unit)
glucoseLabel.text = numberFormatter.stringFromNumber(glucoseValue.quantity.doubleValueForUnit(unit))
var unitStrings = [unit.glucoseUnitDisplayString]
@@ -45,6 +50,10 @@ final class GlucoseHUDView: HUDView {
}
unitLabel.text = unitStrings.joinWithSeparator(" ")
+
+ UIView.animateWithDuration(0.25) {
+ self.alertLabel.alpha = sensor?.isStateValid == true ? 0 : 1
+ }
}
private lazy var timeFormatter: NSDateFormatter = {