diff --git a/Loop Status Extension/HKUnit.swift b/Loop Status Extension/HKUnit.swift index 726614c457..ffa2e62951 100644 --- a/Loop Status Extension/HKUnit.swift +++ b/Loop Status Extension/HKUnit.swift @@ -8,10 +8,11 @@ import HealthKit +// Code in this extension is duplicated from: +// https://github.com/LoopKit/LoopKit/blob/master/LoopKit/HKUnit.swift +// to avoid pulling in the LoopKit extension since it's not extension-API safe. public extension HKUnit { // A formatting helper for determining the preferred decimal style for a given unit - // This is similar to the LoopKit HKUnit extension, but copied here so that we can - // avoid a dependency on LoopKit from the Loop Status Extension. var preferredMinimumFractionDigits: Int { if self.unitString == "mg/dL" { return 0 @@ -19,4 +20,18 @@ public extension HKUnit { return 1 } } + + static func millimolesPerLiterUnit() -> HKUnit { + return HKUnit.moleUnit(with: .milli, molarMass: HKUnitMolarMassBloodGlucose).unitDivided(by: HKUnit.liter()) + } + + // A glucose-centric presentation helper for the localized unit string + var glucoseUnitDisplayString: String { + if self == HKUnit.millimolesPerLiterUnit() { + return NSLocalizedString("mmol/L", comment: "The unit display string for millimoles of glucose per liter") + } else { + return String(describing: self) + } + } + } diff --git a/Loop Status Extension/Info.plist b/Loop Status Extension/Info.plist index 457555e233..ab9e66d865 100644 --- a/Loop Status Extension/Info.plist +++ b/Loop Status Extension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.1.2 + 1.2.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) MainAppBundleIdentifier diff --git a/Loop Status Extension/StatusExtensionContext.swift b/Loop Status Extension/StatusExtensionContext.swift index bbe312e46a..d733fae7f8 100644 --- a/Loop Status Extension/StatusExtensionContext.swift +++ b/Loop Status Extension/StatusExtensionContext.swift @@ -40,11 +40,11 @@ struct GlucoseContext { let sensor: SensorDisplayable? } -final class StatusExtensionContext: NSObject, RawRepresentable { +final class StatusExtensionContext: RawRepresentable { typealias RawValue = [String: Any] private let version = 1 - var preferredUnitDisplayString: String? + var preferredUnitString: String? var latestGlucose: GlucoseContext? var reservoir: ReservoirContext? var loop: LoopContext? @@ -52,15 +52,12 @@ final class StatusExtensionContext: NSObject, RawRepresentable { var batteryPercentage: Double? var eventualGlucose: Double? - override init() { - super.init() - } + init() { } required init?(rawValue: RawValue) { - super.init() let raw = rawValue - if let preferredString = raw["preferredUnitDisplayString"] as? String, + if let preferredString = raw["preferredUnitString"] as? String, let latestValue = raw["latestGlucose_value"] as? Double, let startDate = raw["latestGlucose_startDate"] as? Date { @@ -81,7 +78,7 @@ final class StatusExtensionContext: NSObject, RawRepresentable { isLocal: local) } - preferredUnitDisplayString = preferredString + preferredUnitString = preferredString latestGlucose = GlucoseContext( quantity: latestValue, startDate: startDate, @@ -115,10 +112,10 @@ final class StatusExtensionContext: NSObject, RawRepresentable { "version": version ] - raw["preferredUnitDisplayString"] = preferredUnitDisplayString + raw["preferredUnitString"] = preferredUnitString - if let glucose = latestGlucose, - preferredUnitDisplayString != nil { + if preferredUnitString != nil, + let glucose = latestGlucose { raw["latestGlucose_value"] = glucose.quantity raw["latestGlucose_startDate"] = glucose.startDate } @@ -161,3 +158,10 @@ final class StatusExtensionContext: NSObject, RawRepresentable { return raw } } + + +extension StatusExtensionContext: CustomDebugStringConvertible { + var debugDescription: String { + return String(reflecting: rawValue) + } +} diff --git a/Loop Status Extension/StatusViewController.swift b/Loop Status Extension/StatusViewController.swift index f8a7505b62..0de511aa87 100644 --- a/Loop Status Extension/StatusViewController.swift +++ b/Loop Status Extension/StatusViewController.swift @@ -43,7 +43,7 @@ class StatusViewController: UIViewController, NCWidgetProviding { // the wrong units and that could be very harmful. So unless there's a preferred // unit, assume that none of the rest of the data is reliable. guard - let preferredUnitDisplayString = context.preferredUnitDisplayString + let preferredUnitString = context.preferredUnitString else { completionHandler(NCUpdateResult.failed) return @@ -52,7 +52,7 @@ class StatusViewController: UIViewController, NCWidgetProviding { if let glucose = context.latestGlucose { glucoseHUD.set(glucoseQuantity: glucose.quantity, at: glucose.startDate, - unitDisplayString: preferredUnitDisplayString, + unitString: preferredUnitString, from: glucose.sensor) } @@ -74,14 +74,16 @@ class StatusViewController: UIViewController, NCWidgetProviding { loopCompletionHUD.lastLoopCompleted = loop.lastCompleted } - if let eventualGlucose = context.eventualGlucose { - let quantity = HKQuantity(unit: HKUnit(from: preferredUnitDisplayString), - doubleValue: eventualGlucose.rounded()) + let preferredUnit = HKUnit(from: preferredUnitString) + let formatter = NumberFormatter.glucoseFormatter(for: preferredUnit) + if let eventualGlucose = context.eventualGlucose, + let eventualGlucoseNumberString = formatter.string(from: NSNumber(value: eventualGlucose)) { subtitleLabel.text = String( format: NSLocalizedString( - "Eventually %@", - comment: "The subtitle format describing eventual glucose. (1: localized glucose value description)"), - String(describing: quantity)) + "Eventually %1$@ %2$@", + comment: "The subtitle format describing eventual glucose. (1: localized glucose value description) (2: localized glucose units description)"), + eventualGlucoseNumberString, + preferredUnit.glucoseUnitDisplayString) subtitleLabel.alpha = 1 } else { subtitleLabel.alpha = 0 diff --git a/Loop.xcconfig b/Loop.xcconfig index 3b48714f3c..d092e89a38 100644 --- a/Loop.xcconfig +++ b/Loop.xcconfig @@ -8,4 +8,4 @@ // Change this on first setup to your own unique organization identifier in // reverse-domain name syntax. -MAIN_APP_BUNDLE_IDENTIFIER = com.loudnate +MAIN_APP_BUNDLE_IDENTIFIER = com.loopkit diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 11ac9d2c75..e8525bd86f 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -1387,8 +1387,7 @@ 4F70C1E31DE8DCA7006380B7 /* Base */, ); name = MainInterface.storyboard; - path = "Loop Status Extension"; - sourceTree = ""; + sourceTree = ""; }; C1C73EF91DE3D0230022FC89 /* InfoPlist.strings */ = { isa = PBXVariantGroup; diff --git a/Loop/Managers/DeviceDataManager.swift b/Loop/Managers/DeviceDataManager.swift index 1db7f0c5d6..495ef0333e 100644 --- a/Loop/Managers/DeviceDataManager.swift +++ b/Loop/Managers/DeviceDataManager.swift @@ -975,11 +975,11 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto // MARK: - WatchKit - private(set) var watchManager: WatchDataManager! + fileprivate var watchManager: WatchDataManager! // MARK: - Status Extension - private(set) var statusExtensionManager: StatusExtensionDataManager! + fileprivate var statusExtensionManager: StatusExtensionDataManager! // MARK: - Initialization @@ -1075,7 +1075,11 @@ extension DeviceDataManager: CustomDebugStringConvertible { "workoutModeEnabled: \(workoutModeEnabled)", "maximumBasalRatePerHour: \(maximumBasalRatePerHour)", "maximumBolus: \(maximumBolus)", - String(reflecting: rileyLinkManager) + String(reflecting: rileyLinkManager), + String(reflecting: statusExtensionManager!), + "", + "## NSUserDefaults", + String(reflecting: UserDefaults.standard.dictionaryRepresentation()) ].joined(separator: "\n") } } diff --git a/Loop/Managers/StatusExtensionDataManager.swift b/Loop/Managers/StatusExtensionDataManager.swift index e0aef3324e..3bd89be2d5 100644 --- a/Loop/Managers/StatusExtensionDataManager.swift +++ b/Loop/Managers/StatusExtensionDataManager.swift @@ -20,13 +20,17 @@ final class StatusExtensionDataManager { NotificationCenter.default.addObserver(self, selector: #selector(update(_:)), name: .LoopDataUpdated, object: deviceDataManager.loopManager) } + fileprivate var defaults: UserDefaults? { + return UserDefaults(suiteName: Bundle.main.appGroupSuiteName) + } + @objc private func update(_ notification: Notification) { self.dataManager.glucoseStore?.preferredUnit() { (unit, error) in if error == nil, let unit = unit { self.createContext(unit) { (context) in if let context = context { - UserDefaults(suiteName: Bundle.main.appGroupSuiteName)?.statusExtensionContext = context + self.defaults?.statusExtensionContext = context } } } @@ -57,10 +61,11 @@ final class StatusExtensionDataManager { context.batteryPercentage = 0.25 context.reservoir = ReservoirContext(startDate: Date(), unitVolume: 42.5, capacity: 200) context.netBasal = NetBasalContext(rate: 2.1, percentage: 0.6, startDate: Date() - TimeInterval(250)) - context.eventualGlucose = 119.123 + context.eventualGlucose = HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: 119.123) + .doubleValue(for: preferredUnit) #endif - context.preferredUnitDisplayString = preferredUnit.glucoseUnitDisplayString + context.preferredUnitString = preferredUnit.unitString context.loop = LoopContext( dosingEnabled: dataManager.loopManager.dosingEnabled, lastCompleted: lastLoopCompleted) @@ -98,3 +103,13 @@ final class StatusExtensionDataManager { } } } + + +extension StatusExtensionDataManager: CustomDebugStringConvertible { + var debugDescription: String { + return [ + "## StatusExtensionDataManager", + "statusExtensionContext: \(String(reflecting: defaults?.statusExtensionContext))" + ].joined(separator: "\n") + } +} diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 7bdca3bc6b..ec995ffb83 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -269,7 +269,7 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize if let glucose = self.dataManager.glucoseStore?.latestGlucose { self.glucoseHUD.set(glucoseQuantity: glucose.quantity.doubleValue(for: self.charts.glucoseUnit), at: glucose.startDate, - unitDisplayString: self.charts.glucoseUnit.glucoseUnitDisplayString, + unitString: self.charts.glucoseUnit.unitString, from: self.dataManager.sensorInfo) } diff --git a/Loop/Views/GlucoseHUDView.swift b/Loop/Views/GlucoseHUDView.swift index a5d1247959..43910dbe60 100644 --- a/Loop/Views/GlucoseHUDView.swift +++ b/Loop/Views/GlucoseHUDView.swift @@ -64,19 +64,20 @@ final class GlucoseHUDView: HUDView { } } - func set(glucoseQuantity: Double, at glucoseStartDate: Date, unitDisplayString: String, from sensor: SensorDisplayable?) { + func set(glucoseQuantity: Double, at glucoseStartDate: Date, unitString: String, from sensor: SensorDisplayable?) { var accessibilityStrings = [String]() let time = timeFormatter.string(from: glucoseStartDate) caption?.text = time + let unit = HKUnit(from: unitString) - let numberFormatter = NumberFormatter.glucoseFormatter(for: HKUnit(from: unitDisplayString)) + let numberFormatter = NumberFormatter.glucoseFormatter(for: unit) if let valueString = numberFormatter.string(from: NSNumber(value: glucoseQuantity)) { glucoseLabel.text = valueString accessibilityStrings.append(String(format: NSLocalizedString("%1$@ at %2$@", comment: "Accessbility format value describing glucose: (1: glucose number)(2: glucose time)"), valueString, time)) } - var unitStrings = [unitDisplayString] + var unitStrings = [unit.glucoseUnitDisplayString] if let trend = sensor?.trendType { unitStrings.append(trend.symbol)