Skip to content

Commit eb0f136

Browse files
author
Darin Krauss
authored
[LOOP-28] Upload loop settings data to Tidepool backend (#476)
- https://tidepool.atlassian.net/browse/LOOP-28 - Capture latest notification settings when app becomes active - Update StoredSettings if latest notification settings have changed - Updates to StoredSettings
1 parent 38a022c commit eb0f136

File tree

9 files changed

+118
-12
lines changed

9 files changed

+118
-12
lines changed

Loop/Extensions/SettingsStore+SimulatedCoreData.swift

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
import Foundation
10+
import HealthKit
1011
import LoopKit
1112

1213
// MARK: - Simulated Core Data
@@ -110,6 +111,39 @@ fileprivate extension StoredSettings {
110111
RepeatingScheduleValue(startTime: .hours(18), value: 8.0),
111112
RepeatingScheduleValue(startTime: .hours(21), value: 10.0)],
112113
timeZone: timeZone)
114+
let notificationSettings = NotificationSettings(authorizationStatus: .authorized,
115+
soundSetting: .enabled,
116+
badgeSetting: .enabled,
117+
alertSetting: .enabled,
118+
notificationCenterSetting: .enabled,
119+
lockScreenSetting: .enabled,
120+
carPlaySetting: .enabled,
121+
alertStyle: .banner,
122+
showPreviewsSetting: .always,
123+
criticalAlertSetting: .enabled,
124+
providesAppNotificationSettings: true,
125+
announcementSetting: .enabled)
126+
let controllerDevice = StoredSettings.ControllerDevice(name: "Controller Name",
127+
systemName: "Controller System Name",
128+
systemVersion: "Controller System Version",
129+
model: "Controller Model",
130+
modelIdentifier: "Controller Model Identifier")
131+
let cgmDevice = HKDevice(name: "CGM Name",
132+
manufacturer: "CGM Manufacturer",
133+
model: "CGM Model",
134+
hardwareVersion: "CGM Hardware Version",
135+
firmwareVersion: "CGM Firmware Version",
136+
softwareVersion: "CGM Software Version",
137+
localIdentifier: "CGM Local Identifier",
138+
udiDeviceIdentifier: "CGM UDI Device Identifier")
139+
let pumpDevice = HKDevice(name: "Pump Name",
140+
manufacturer: "Pump Manufacturer",
141+
model: "Pump Model",
142+
hardwareVersion: "Pump Hardware Version",
143+
firmwareVersion: "Pump Firmware Version",
144+
softwareVersion: "Pump Software Version",
145+
localIdentifier: "Pump Local Identifier",
146+
udiDeviceIdentifier: "Pump UDI Device Identifier")
113147
return StoredSettings(date: date,
114148
dosingEnabled: true,
115149
glucoseTargetRangeSchedule: glucoseTargetRangeSchedule,
@@ -122,11 +156,15 @@ fileprivate extension StoredSettings {
122156
maximumBolus: 10.0,
123157
suspendThreshold: GlucoseThreshold(unit: .milligramsPerDeciliter, value: 75.0),
124158
deviceToken: UUID().uuidString,
159+
insulinType: .humalog,
125160
defaultRapidActingModel: StoredInsulinModel(ExponentialInsulinModelPreset.rapidActingAdult),
126161
basalRateSchedule: basalRateSchedule,
127162
insulinSensitivitySchedule: insulinSensitivitySchedule,
128163
carbRatioSchedule: carbRatioSchedule,
129-
bloodGlucoseUnit: .milligramsPerDeciliter,
130-
syncIdentifier: UUID().uuidString)
164+
notificationSettings: notificationSettings,
165+
controllerDevice: controllerDevice,
166+
cgmDevice: cgmDevice,
167+
pumpDevice: pumpDevice,
168+
bloodGlucoseUnit: .milligramsPerDeciliter)
131169
}
132170
}

Loop/Extensions/UIDevice+Loop.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,21 @@ extension UIDevice {
1313

1414
// https://stackoverflow.com/questions/26028918/how-to-determine-the-current-iphone-device-model
1515
var modelIdentifier: String {
16-
var info = utsname()
17-
uname(&info)
18-
let modelCode = withUnsafePointer(to: &info.machine) { $0.withMemoryRebound(to: CChar.self, capacity: 1) { String(validatingUTF8: $0) } }
19-
return modelCode ?? "unknown"
16+
#if IOS_SIMULATOR
17+
return "\(model)Simulator"
18+
#else
19+
var info = utsname()
20+
uname(&info)
21+
return withUnsafePointer(to: &info.machine) { $0.withMemoryRebound(to: CChar.self, capacity: 1) { String(validatingUTF8: $0) } } ?? "unknown"
22+
#endif
23+
}
24+
25+
public var controllerDevice: StoredSettings.ControllerDevice {
26+
return StoredSettings.ControllerDevice(name: name,
27+
systemName: systemName,
28+
systemVersion: systemVersion,
29+
model: model,
30+
modelIdentifier: modelIdentifier)
2031
}
2132

2233
public var deviceSettings: StoredDosingDecision.DeviceSettings {

Loop/Managers/DeviceDataManager.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,10 @@ extension DeviceDataManager {
706706
return pumpManager?.status
707707
}
708708

709+
var cgmManagerStatus: CGMManagerStatus? {
710+
return cgmManager?.cgmManagerStatus
711+
}
712+
709713
func glucoseDisplay(for glucose: GlucoseSampleValue?) -> GlucoseDisplayable? {
710714
guard let glucose = glucose else {
711715
return cgmManager?.glucoseDisplay
@@ -749,6 +753,7 @@ extension DeviceDataManager {
749753

750754
func didBecomeActive() {
751755
updatePumpManagerBLEHeartbeatPreference()
756+
loopManager.didBecomeActive()
752757
}
753758

754759
func updatePumpManagerBLEHeartbeatPreference() {
@@ -810,6 +815,7 @@ extension DeviceDataManager: CGMManagerDelegate {
810815
}
811816
self.cgmManager = nil
812817
self.displayGlucoseUnitObservers.cleanupDeallocatedElements()
818+
self.loopManager.storeSettings()
813819
}
814820
}
815821

@@ -857,6 +863,7 @@ extension DeviceDataManager: CGMManagerOnboardingDelegate {
857863

858864
DispatchQueue.main.async {
859865
self.refreshDeviceData()
866+
self.loopManager.storeSettings()
860867
}
861868
}
862869
}
@@ -1000,6 +1007,7 @@ extension DeviceDataManager: PumpManagerDelegate {
10001007
DispatchQueue.main.async {
10011008
self.pumpManager = nil
10021009
self.deliveryUncertaintyAlertManager = nil
1010+
self.loopManager.storeSettings()
10031011
}
10041012
}
10051013

@@ -1070,6 +1078,7 @@ extension DeviceDataManager: PumpManagerOnboardingDelegate {
10701078

10711079
DispatchQueue.main.async {
10721080
self.refreshDeviceData()
1081+
self.loopManager.storeSettings()
10731082
}
10741083
}
10751084
}
@@ -1390,8 +1399,8 @@ extension DeviceDataManager: SupportInfoProvider {
13901399
return pumpManager?.status
13911400
}
13921401

1393-
public var cgmDevice: HKDevice? {
1394-
return cgmManager?.device
1402+
public var cgmStatus: CGMManagerStatus? {
1403+
return cgmManager?.cgmManagerStatus
13951404
}
13961405

13971406
public func generateIssueReport(completion: @escaping (String) -> Void) {

Loop/Managers/LoopDataManager.swift

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,11 @@ extension LoopDataManager {
811811
self.dosingDecisionStore.storeDosingDecision(dosingDecision) {}
812812
}
813813

814-
func storeSettings() {
814+
func storeSettings(notificationSettings: NotificationSettings? = nil,
815+
controllerDevice: StoredSettings.ControllerDevice? = nil,
816+
cgmDevice: HKDevice? = nil,
817+
pumpDevice: HKDevice? = nil)
818+
{
815819
guard let appGroup = UserDefaults.appGroup, let loopSettings = appGroup.loopSettings else {
816820
return
817821
}
@@ -828,12 +832,17 @@ extension LoopDataManager {
828832
maximumBolus: loopSettings.maximumBolus,
829833
suspendThreshold: loopSettings.suspendThreshold,
830834
deviceToken: loopSettings.deviceToken?.hexadecimalString,
835+
insulinType: delegate?.pumpManagerStatus?.insulinType,
831836
defaultRapidActingModel: appGroup.defaultRapidActingModel.map(StoredInsulinModel.init),
832837
basalRateSchedule: appGroup.basalRateSchedule,
833838
insulinSensitivitySchedule: appGroup.insulinSensitivitySchedule,
834839
carbRatioSchedule: appGroup.carbRatioSchedule,
840+
notificationSettings: notificationSettings ?? settingsStore.latestSettings?.notificationSettings,
841+
controllerDevice: controllerDevice ?? UIDevice.current.controllerDevice,
842+
cgmDevice: cgmDevice ?? delegate?.cgmManagerStatus?.device,
843+
pumpDevice: pumpDevice ?? delegate?.pumpManagerStatus?.device,
835844
bloodGlucoseUnit: loopSettings.glucoseUnit)
836-
self.settingsStore.storeSettings(settings) {}
845+
settingsStore.storeSettings(settings) {}
837846
}
838847

839848
// Actions
@@ -848,6 +857,8 @@ extension LoopDataManager {
848857
self.logger.default("Loop running")
849858
NotificationCenter.default.post(name: .LoopRunning, object: self)
850859

860+
self.storeUpdatedSettings()
861+
851862
self.lastLoopError = nil
852863
let startDate = self.now()
853864

@@ -1910,6 +1921,37 @@ extension LoopDataManager {
19101921
}
19111922
}
19121923

1924+
extension LoopDataManager {
1925+
func didBecomeActive () {
1926+
storeUpdatedSettings()
1927+
}
1928+
1929+
func storeUpdatedSettings() {
1930+
UNUserNotificationCenter.current().getNotificationSettings() { notificationSettings in
1931+
self.dataAccessQueue.async {
1932+
guard let latestSettings = self.settingsStore.latestSettings else {
1933+
return
1934+
}
1935+
1936+
let notificationSettings = NotificationSettings(notificationSettings)
1937+
let controllerDevice = UIDevice.current.controllerDevice
1938+
let cgmDevice = self.delegate?.cgmManagerStatus?.device
1939+
let pumpDevice = self.delegate?.pumpManagerStatus?.device
1940+
1941+
if notificationSettings != latestSettings.notificationSettings ||
1942+
controllerDevice != latestSettings.controllerDevice ||
1943+
cgmDevice != latestSettings.cgmDevice ||
1944+
pumpDevice != latestSettings.pumpDevice
1945+
{
1946+
self.storeSettings(notificationSettings: notificationSettings,
1947+
controllerDevice: controllerDevice,
1948+
cgmDevice: cgmDevice,
1949+
pumpDevice: pumpDevice)
1950+
}
1951+
}
1952+
}
1953+
}
1954+
}
19131955

19141956
extension LoopDataManager {
19151957
/// Generates a diagnostic report about the current state
@@ -2045,6 +2087,9 @@ protocol LoopDataManagerDelegate: AnyObject {
20452087

20462088
/// The pump manager status, if one exists.
20472089
var pumpManagerStatus: PumpManagerStatus? { get }
2090+
2091+
/// The cgm manager status, if one exists.
2092+
var cgmManagerStatus: CGMManagerStatus? { get }
20482093
}
20492094

20502095
private extension TemporaryScheduleOverride {

Loop/Managers/Store Protocols/SettingsStoreProtocol.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import LoopKit
1010

1111
protocol SettingsStoreProtocol: AnyObject {
12+
var latestSettings: StoredSettings? { get }
1213
func storeSettings(_ settings: StoredSettings, completion: @escaping () -> Void)
1314
}
1415

Loop/View Models/SettingsViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ extension SettingsViewModel {
156156
return nil
157157
}
158158

159-
var cgmDevice: HKDevice? {
159+
var cgmStatus: CGMManagerStatus? {
160160
return nil
161161
}
162162

Loop/Views/SupportScreenView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct SupportScreenView_Previews: PreviewProvider {
7070

7171
var pumpStatus: PumpManagerStatus? = nil
7272

73-
var cgmDevice: HKDevice? = nil
73+
var cgmStatus: CGMManagerStatus? = nil
7474

7575
func generateIssueReport(completion: (String) -> Void) {
7676
completion("Mock Issue Report")

LoopTests/Managers/LoopDataManagerTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ class LoopDataManagerDosingTests: XCTestCase {
299299
func loopDataManager(_ manager: LoopDataManager, roundBasalRate unitsPerHour: Double) -> Double { unitsPerHour }
300300
func loopDataManager(_ manager: LoopDataManager, roundBolusVolume units: Double) -> Double { units }
301301
var pumpManagerStatus: PumpManagerStatus?
302+
var cgmManagerStatus: CGMManagerStatus?
302303
}
303304

304305
func waitOnDataQueue(timeout: TimeInterval = 1.0) {

LoopTests/Mock Stores/MockSettingsStore.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import LoopKit
1010
@testable import Loop
1111

1212
class MockSettingsStore: SettingsStoreProtocol {
13+
var latestSettings: StoredSettings? { nil }
1314
func storeSettings(_ settings: StoredSettings, completion: @escaping () -> Void) {
1415
completion()
1516
}

0 commit comments

Comments
 (0)