add user-defined TIR back but in a better way

to avoid adding extra menu lines, use an enum to track the TIR type chosen and allow the user to just chose directly, or cycle through by double-tapping the statistics view
This commit is contained in:
Paul Plant 2023-12-24 15:00:26 +01:00
parent d96f2e2e66
commit bd6ce1c184
18 changed files with 265 additions and 85 deletions

View File

@ -37,6 +37,7 @@
47ADD2E127FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47ADD2E027FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift */; };
47B60F3726F389E2003198D3 /* LandscapeChartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B60F3626F389E2003198D3 /* LandscapeChartViewController.swift */; };
47B7FC722B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B7FC712B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift */; };
47CF18B22B37689A00FA6160 /* TimeInRangeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CF18B12B37689A00FA6160 /* TimeInRangeType.swift */; };
47D2DB3B2B14F6D000C8EE6B /* ScreenLockDimmingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D2DB3A2B14F6D000C8EE6B /* ScreenLockDimmingType.swift */; };
47D9BC952A78498500AB85B2 /* BgReadingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D9BC942A78498500AB85B2 /* BgReadingsDetailView.swift */; };
47DB06C22A6FC02200267BE3 /* SettingsViewDataSourceSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47DB06C12A6FC02200267BE3 /* SettingsViewDataSourceSettingsViewModel.swift */; };
@ -742,6 +743,7 @@
47ADD2E027FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift; sourceTree = "<group>"; };
47B60F3626F389E2003198D3 /* LandscapeChartViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandscapeChartViewController.swift; sourceTree = "<group>"; };
47B7FC712B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowerBackgroundKeepAliveType.swift; sourceTree = "<group>"; };
47CF18B12B37689A00FA6160 /* TimeInRangeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInRangeType.swift; sourceTree = "<group>"; };
47D2DB3A2B14F6D000C8EE6B /* ScreenLockDimmingType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenLockDimmingType.swift; sourceTree = "<group>"; };
47D9BC942A78498500AB85B2 /* BgReadingsDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BgReadingsDetailView.swift; sourceTree = "<group>"; };
47DB06C02A6FB3CC00267BE3 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/BgReadings.strings; sourceTree = "<group>"; };
@ -1670,6 +1672,7 @@
isa = PBXGroup;
children = (
47FB28072636B04200042FFB /* StatisticsManager.swift */,
47CF18B12B37689A00FA6160 /* TimeInRangeType.swift */,
);
path = Statistics;
sourceTree = "<group>";
@ -3862,6 +3865,7 @@
F8F9722D23A5915900C3F17D /* M5StackBluetoothTransmitter.swift in Sources */,
F808D2C8240323CA0084B5DB /* BubbleBluetoothPeripheralViewModel.swift in Sources */,
F8E6C79024CEC22A007C1199 /* TextsSnooze.swift in Sources */,
47CF18B22B37689A00FA6160 /* TimeInRangeType.swift in Sources */,
F8F9722C23A5915900C3F17D /* GlucoseData.swift in Sources */,
F8A389E7232ECE7E0010F405 /* SettingsViewUtilities.swift in Sources */,
F8A2BC3125DB0D6D001D1E78 /* BluetoothPeripheralManager+M5StackBluetoothTransmitterDelegate.swift in Sources */,

View File

@ -125,6 +125,8 @@ extension UserDefaults {
case daysToUseStatistics = "daysToUseStatistics"
/// use IFCC way to show A1C?
case useIFCCA1C = "useIFCCA1C"
/// which type of TIR calculation is selected?
case timeInRangeType = "timeInRangeType"
/// no longer used, but will leave it here to prevent compiler coredata warnings
case useStandardStatisticsRange = "useStandardStatisticsRange"
/// use the newer TITR of 70-140mg/dL to calculate the statistics? If false, we will use the conventional TIR of 70-180mg/dL
@ -1171,25 +1173,15 @@ extension UserDefaults {
}
}
/// no longer used, but will leave it here to prevent compiler coredata warnings
@objc dynamic var useStandardStatisticsRange: Bool {
// default value for bool in userdefaults is false, by default we want the statistics view to calculate using the older-style, standardised TIR values (false)
/// holds the enum integer of the time in range calculation type
/// it will default to 0 which is standard
var timeInRangeType: TimeInRangeType {
get {
return bool(forKey: Key.useTITRStatisticsRange.rawValue)
let timeInRangeTypeAsInt = integer(forKey: Key.timeInRangeType.rawValue)
return TimeInRangeType(rawValue: timeInRangeTypeAsInt) ?? .standardRange
}
set {
set(newValue, forKey: Key.useTITRStatisticsRange.rawValue)
}
}
/// use the newer TITR range of 70-140mg/dL to calculate the statistics? If false, we will use the conventional TIR range of 70-180mg/dL
@objc dynamic var useTITRStatisticsRange: Bool {
// default value for bool in userdefaults is false, by default we want the statistics view to calculate using the older-style, standardised TIR values (false)
get {
return bool(forKey: Key.useTITRStatisticsRange.rawValue)
}
set {
set(newValue, forKey: Key.useTITRStatisticsRange.rawValue)
set(newValue.rawValue, forKey: Key.timeInRangeType.rawValue)
}
}

View File

@ -121,7 +121,7 @@ public final class StatisticsManager {
}
}
/*
// let's set up the which values will be used to calculate TIR. It can be either the standardised "Time in Range" values or the newer "Time in Tight Range" values.
let useTITR: Bool = UserDefaults.standard.useTITRStatisticsRange
@ -132,6 +132,9 @@ public final class StatisticsManager {
lowLimitForTIR = useTITR ? ConstantsStatistics.standardisedLowValueForTITRInMmol : ConstantsStatistics.standardisedLowValueForTIRInMmol
highLimitForTIR = useTITR ? ConstantsStatistics.standardisedHighValueForTITRInMmol : ConstantsStatistics.standardisedHighValueForTIRInMmol
}
*/
lowLimitForTIR = UserDefaults.standard.timeInRangeType.lowerLimit
highLimitForTIR = UserDefaults.standard.timeInRangeType.higherLimit
// make sure that there exist elements in the glucoseValue array before trying to process statistics calculations or we could get a fatal divide by zero error/crash
if glucoseValues.count > 0 {

View File

@ -0,0 +1,81 @@
//
// TimeInRangeType.swift
// xdrip
//
// Created by Paul Plant on 23/12/23.
// Copyright © 2023 Johan Degraeve. All rights reserved.
//
import Foundation
/// types of background keep-alive
public enum TimeInRangeType: Int, CaseIterable {
// when adding to TimeInRangeType, add new cases at the end (ie 3, ...)
// if this is done in the middle then a database migration would be required, because the rawvalue is stored as Int16 in the coredata
// the order of the returned enum can be defined in allCases below
case standardRange = 0
case tightRange = 1
case userDefinedRange = 2
var description: String {
switch self {
case .standardRange:
return Texts_SettingsView.timeInRangeTypeStandardRange
case .tightRange:
return Texts_SettingsView.timeInRangeTypeTightRange
case .userDefinedRange:
return Texts_SettingsView.timeInRangeTypeUserDefinedRange
}
}
var title: String {
switch self {
case .standardRange:
return Texts_Common.inRangeStatistics
case .tightRange:
return Texts_Common.inTightRangeStatistics
case .userDefinedRange:
return Texts_Common.userRangeStatistics
}
}
var lowerLimit: Double {
let isMgDl = UserDefaults.standard.bloodGlucoseUnitIsMgDl
switch self {
case .standardRange:
return isMgDl ? ConstantsStatistics.standardisedLowValueForTIRInMgDl : ConstantsStatistics.standardisedLowValueForTIRInMmol
case .tightRange:
return isMgDl ? ConstantsStatistics.standardisedLowValueForTITRInMgDl : ConstantsStatistics.standardisedLowValueForTITRInMmol
case .userDefinedRange:
return UserDefaults.standard.lowMarkValueInUserChosenUnit
}
}
var higherLimit: Double {
let isMgDl = UserDefaults.standard.bloodGlucoseUnitIsMgDl
switch self {
case .standardRange:
return isMgDl ? ConstantsStatistics.standardisedHighValueForTIRInMgDl : ConstantsStatistics.standardisedHighValueForTIRInMmol
case .tightRange:
return isMgDl ? ConstantsStatistics.standardisedHighValueForTITRInMgDl : ConstantsStatistics.standardisedHighValueForTITRInMmol
case .userDefinedRange:
return UserDefaults.standard.highMarkValueInUserChosenUnit
}
}
func rangeString() -> String {
let isMgDl = UserDefaults.standard.bloodGlucoseUnitIsMgDl
return " (" + self.lowerLimit.bgValuetoString(mgdl: isMgDl) + "-" + self.higherLimit.bgValuetoString(mgdl: isMgDl) + ")"
}
}

View File

@ -30,7 +30,6 @@
"settingsviews_showStatistics" = "Show Statistics";
"settingsviews_daysToUseStatisticsTitle" = "Days to Calculate";
"settingsviews_daysToUseStatisticsMessage" = "Maximum days we should try to use to calculate the statistics?\n\n(Enter 0 to calculate today since midnight)";
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";
"settingsviews_sectiontitletransmitter" = "Transmitter";
"settingsviews_transmittertype" = "Transmitter Type";

View File

@ -42,6 +42,7 @@
"common_statistics_low" = "Low";
"common_statistics_inRange" = "In Range";
"common_statistics_inTightRange" = "Tight Range";
"common_statistics_userRange" = "User Range";
"common_statistics_high" = "High";
"common_statistics_average" = "Average";
"common_statistics_a1c" = "HbA1c";

View File

@ -60,7 +60,10 @@
"settingsviews_showStatistics" = "Show Statistics";
"settingsviews_daysToUseStatisticsTitle" = "Days to Calculate";
"settingsviews_daysToUseStatisticsMessage" = "Maximum days we should try to use to calculate the statistics?\n\n(Enter 0 to calculate today since midnight)";
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
"settingsviews_labelTimeInRangeType" = "Time In Range Type";
"settingsviews_timeInRangeTypeStandardRange" = "Standard Range";
"settingsviews_timeInRangeTypeTightRange" = "Tight Range";
"settingsviews_timeInRangeTypeUserDefinedRange" = "User Range";
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";
"settingsviews_sectiontitletransmitter" = "Transmitter";
"settingsviews_transmittertype" = "Transmitter Type";

View File

@ -42,6 +42,7 @@
"common_statistics_low" = "Baja";
"common_statistics_inRange" = "En Rango";
"common_statistics_inTightRange" = "Estrecho";
"common_statistics_userRange" = "Usuario";
"common_statistics_high" = "Alta";
"common_statistics_average" = "Promedio";
"common_statistics_a1c" = "HbA1c";

View File

@ -55,7 +55,10 @@
"settingsviews_showStatistics" = "Mostrar Estadísticas";
"settingsviews_daysToUseStatisticsTitle" = "Días a Calcular";
"settingsviews_daysToUseStatisticsMessage" = "Máximos días a intentar usar para calcular las estadísticas?\n\n(Entrar 0 para calcular el día de hoy desde las 00:00hrs)";
"settingsviews_useTITRStatisticsRange" = "Usar Tiempo en Rango Estrecho";
"settingsviews_labelTimeInRangeType" = "Tipo de Tiempo en Rango";
"settingsviews_timeInRangeTypeStandardRange" = "Rango Estándar";
"settingsviews_timeInRangeTypeTightRange" = "Rango Estrecho";
"settingsviews_timeInRangeTypeUserDefinedRange" = "Rango Usuario";
"settingsviews_useIFCCA1C" = "Ver HbA1c en mmols/mol";
"settingsviews_sectiontitletransmitter" = "Transmisor";
"settingsviews_transmittertype" = "Tipo de Transmisor";

View File

@ -307,9 +307,6 @@
/// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -307,9 +307,6 @@
/// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -307,9 +307,6 @@
/// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -270,9 +270,6 @@
/// transmitter settings, this is for the button, when clicked then user will be requested to give transmitter id. The only difference with settingsviews_transmitterid is that ':' is not added
"settingsviews_transmitterid_text_for_button" = "Transmitter ID";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// dexcom share settings, where user can select if readings should be uploaded to dexcom share yes or no
"settingsviews_uploadReadingstoDexcomShare" = "Upload to Dexcom Share";

View File

@ -231,9 +231,6 @@
/// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -190,6 +190,10 @@ class Texts_Common {
return NSLocalizedString("common_statistics_inTightRange", tableName: filename, bundle: Bundle.main, value: "Tight Range", comment: "the words in tight range")
}()
static let userRangeStatistics = {
return NSLocalizedString("common_statistics_userRange", tableName: filename, bundle: Bundle.main, value: "User Range", comment: "the words in user range")
}()
static let highStatistics = {
return NSLocalizedString("common_statistics_high", tableName: filename, bundle: Bundle.main, value: "High", comment: "the word high")
}()

View File

@ -272,8 +272,20 @@ class Texts_SettingsView {
return NSLocalizedString("settingsviews_daysToUseStatisticsMessage", tableName: filename, bundle: Bundle.main, value: "How many days should we use to calculate the statistics? (Enter 0 to calculate today since midnight)", comment: "statistics settings, how many days to use for calculations")
}()
static let labelUseTITRStatisticsRange: String = {
return NSLocalizedString("settingsviews_useTITRStatisticsRange", tableName: filename, bundle: Bundle.main, value: "Use Time in Tight Range", comment: "statistics settings, prefer time in tight range")
static let labelTimeInRangeType: String = {
return NSLocalizedString("settingsviews_labelTimeInRangeType", tableName: filename, bundle: Bundle.main, value: "Time In Range Type", comment: "statistics settings, the type of time in range selected")
}()
static let timeInRangeTypeStandardRange: String = {
return NSLocalizedString("settingsviews_timeInRangeTypeStandardRange", tableName: filename, bundle: Bundle.main, value: "Standard Range", comment: "statistics settings, prefer standard time in range")
}()
static let timeInRangeTypeTightRange: String = {
return NSLocalizedString("settingsviews_timeInRangeTypeTightRange", tableName: filename, bundle: Bundle.main, value: "Tight Range", comment: "statistics settings, prefer time in tight range")
}()
static let timeInRangeTypeUserDefinedRange: String = {
return NSLocalizedString("settingsviews_timeInRangeTypeUserDefinedRange", tableName: filename, bundle: Bundle.main, value: "User Range", comment: "statistics settings, prefer user-defined range")
}()
static let labelUseIFFCA1C: String = {

View File

@ -375,47 +375,61 @@ final class RootViewController: UIViewController, ObservableObject {
@IBOutlet var miniChartDoubleTapGestureRecognizer: UITapGestureRecognizer!
/// can be used as a shortcut to switch between TIR and TITR calculation methods. The user will be notified of the change via UI transitions to show what has changed in the calculation limits
/// can be used as a shortcut to switch between different TIR calculation methods. The user will be notified of the change via UI transitions to show what has changed in the calculation limits
@IBAction func statisticsViewDoubleTapGestureRecognizer(_ sender: UITapGestureRecognizer) {
// the userdefault will be changed due to the double tap
let useTITR = !UserDefaults.standard.useTITRStatisticsRange
let previousTimeInRangeType = UserDefaults.standard.timeInRangeType
UserDefaults.standard.useTITRStatisticsRange = useTITR
var newTimeInRangeType: TimeInRangeType = previousTimeInRangeType
// if we're at the last index in the enum, then go to the first one
// otherwise, just set to the next index
if previousTimeInRangeType == TimeInRangeType.allCases.last {
newTimeInRangeType = .standardRange
} else {
newTimeInRangeType = TimeInRangeType(rawValue: previousTimeInRangeType.rawValue + 1) ?? .tightRange
}
// write the new index back to userdefaults (this will also trigger the observers to update the UI)
UserDefaults.standard.timeInRangeType = newTimeInRangeType
updateStatistics(animate: false)
let normalTitleColor: UIColor = lowTitleLabelOutlet.textColor
let normalLimitValueColor: UIColor = lowLabelOutlet.textColor
inRangeTitleLabelOutlet.textColor = ConstantsStatistics.highlightColorTitles
if ConstantsStatistics.standardisedLowValueForTIRInMgDl != ConstantsStatistics.standardisedLowValueForTITRInMgDl {
if previousTimeInRangeType.lowerLimit != newTimeInRangeType.lowerLimit {
self.lowLabelOutlet.textColor = ConstantsStatistics.labelLowColor
}
if ConstantsStatistics.standardisedHighValueForTIRInMgDl != ConstantsStatistics.standardisedHighValueForTITRInMgDl {
if previousTimeInRangeType.higherLimit != newTimeInRangeType.higherLimit {
self.highLabelOutlet.textColor = ConstantsStatistics.labelHighColor
}
// wait a short while and then fade the labels back out
// even if the label colours weren't changed, it's easier to just fade them all every time.
// even if some of the label colours weren't changed, it's easier to just fade them all every time.
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
UIView.transition(with: self.inRangeTitleLabelOutlet, duration: 2, options: .transitionCrossDissolve, animations: {
UIView.transition(with: self.inRangeTitleLabelOutlet, duration: 1, options: .transitionCrossDissolve, animations: {
self.inRangeTitleLabelOutlet.textColor = normalTitleColor
})
UIView.transition(with: self.lowLabelOutlet, duration: 2, options: .transitionCrossDissolve, animations: {
self.lowLabelOutlet.textColor = normalLimitValueColor
UIView.transition(with: self.lowLabelOutlet, duration: 1, options: .transitionCrossDissolve, animations: {
self.lowLabelOutlet.textColor = .lightGray
})
UIView.transition(with: self.highLabelOutlet, duration: 2, options: .transitionCrossDissolve, animations: {
self.highLabelOutlet.textColor = normalLimitValueColor
UIView.transition(with: self.highLabelOutlet, duration: 1, options: .transitionCrossDissolve, animations: {
self.highLabelOutlet.textColor = .lightGray
})
}
@ -2609,7 +2623,7 @@ final class RootViewController: UIViewController, ObservableObject {
// set the title labels to their correct localization
self.lowTitleLabelOutlet.text = Texts_Common.lowStatistics
self.inRangeTitleLabelOutlet.text = UserDefaults.standard.useTITRStatisticsRange ? Texts_Common.inTightRangeStatistics : Texts_Common.inRangeStatistics
self.inRangeTitleLabelOutlet.text = UserDefaults.standard.timeInRangeType.title
self.highTitleLabelOutlet.text = Texts_Common.highStatistics
self.averageTitleLabelOutlet.text = Texts_Common.averageStatistics
self.a1cTitleLabelOutlet.text = Texts_Common.a1cStatistics

View File

@ -15,8 +15,8 @@ fileprivate enum Setting:Int, CaseIterable {
//show the statistics on the home screen?
case showStatistics = 0
//should we use the standard range for TIR, or the newer Time in Tighter Range values?
case useTITRStatisticsRange = 1
//which TIR type should be used?
case timeInRangeType = 1
//urgent low value
case useIFCCA1C = 2
@ -24,7 +24,15 @@ fileprivate enum Setting:Int, CaseIterable {
}
/// conforms to SettingsViewModelProtocol for all general settings in the first sections screen
struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
class SettingsViewStatisticsSettingsViewModel: NSObject, SettingsViewModelProtocol {
override init() {
super.init()
addObservers()
}
func uiView(index: Int) -> UIView? {
@ -35,8 +43,8 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
case .showStatistics:
return UISwitch(isOn: UserDefaults.standard.showStatistics, action: {(isOn:Bool) in UserDefaults.standard.showStatistics = isOn})
case .useTITRStatisticsRange :
return UISwitch(isOn: UserDefaults.standard.useTITRStatisticsRange, action: {(isOn:Bool) in UserDefaults.standard.useTITRStatisticsRange = isOn})
case .timeInRangeType:
return nil
case .useIFCCA1C :
return UISwitch(isOn: UserDefaults.standard.useIFCCA1C, action: {(isOn:Bool) in UserDefaults.standard.useIFCCA1C = isOn})
@ -45,12 +53,21 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
}
func completeSettingsViewRefreshNeeded(index: Int) -> Bool {
// changing follower to master or master to follower requires changing ui for nightscout settings and transmitter type settings
if (index == Setting.timeInRangeType.rawValue) {return true}
return false
}
var sectionReloadClosure: (() -> Void)?
func storeRowReloadClosure(rowReloadClosure: ((Int) -> Void)) {}
func storeSectionReloadClosure(sectionReloadClosure: @escaping (() -> Void)) {
self.sectionReloadClosure = sectionReloadClosure
}
func storeUIViewController(uIViewController: UIViewController) {}
func storeMessageHandler(messageHandler: ((String, String) -> Void)) {
@ -65,34 +82,59 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
switch setting {
case .showStatistics:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.showStatistics {
UserDefaults.standard.showStatistics = false
} else {
UserDefaults.standard.showStatistics = true
}
})
case .timeInRangeType:
// data to be displayed in list from which user needs to pick a screen dimming type
var data = [String]()
var selectedRow: Int?
var index = 0
let currentTimeInRangeType = UserDefaults.standard.timeInRangeType
// get all data source types and add the description to data. Search for the type that matches the ScreenLockDimmingType that is currently stored in userdefaults.
for timeInRangeType in TimeInRangeType.allCases {
case .showStatistics:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.showStatistics {
UserDefaults.standard.showStatistics = false
} else {
UserDefaults.standard.showStatistics = true
}
})
data.append(timeInRangeType.description + timeInRangeType.rangeString())
if timeInRangeType == currentTimeInRangeType {
selectedRow = index
}
index += 1
}
return SettingsSelectedRowAction.selectFromList(title: Texts_SettingsView.labelTimeInRangeType, data: data, selectedRow: selectedRow, actionTitle: nil, cancelTitle: nil, actionHandler: {(index:Int) in
if index != selectedRow {
case .useTITRStatisticsRange:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.useTITRStatisticsRange {
UserDefaults.standard.useTITRStatisticsRange = false
} else {
UserDefaults.standard.useTITRStatisticsRange = true
}
})
case .useIFCCA1C:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.useIFCCA1C {
UserDefaults.standard.useIFCCA1C = false
} else {
UserDefaults.standard.useIFCCA1C = true
}
})
UserDefaults.standard.timeInRangeType = TimeInRangeType(rawValue: index) ?? .standardRange
}
}, cancelHandler: nil, didSelectRowHandler: nil)
case .useIFCCA1C:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.useIFCCA1C {
UserDefaults.standard.useIFCCA1C = false
} else {
UserDefaults.standard.useIFCCA1C = true
}
})
}
}
@ -118,8 +160,8 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
case .showStatistics:
return Texts_SettingsView.labelShowStatistics
case .useTITRStatisticsRange:
return Texts_SettingsView.labelUseTITRStatisticsRange
case .timeInRangeType:
return Texts_SettingsView.labelTimeInRangeType
case .useIFCCA1C:
return Texts_SettingsView.labelUseIFFCA1C
@ -132,9 +174,12 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
switch setting {
case .showStatistics, .useTITRStatisticsRange, .useIFCCA1C:
case .showStatistics, .useIFCCA1C:
return UITableViewCell.AccessoryType.none
case .timeInRangeType:
return UITableViewCell.AccessoryType.disclosureIndicator
}
}
@ -143,9 +188,42 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
switch setting {
case .showStatistics, .useTITRStatisticsRange, .useIFCCA1C:
case .showStatistics, .useIFCCA1C:
return nil
case .timeInRangeType:
return UserDefaults.standard.timeInRangeType.description
}
}
// MARK: - observe functions
private func addObservers() {
// Listen for changes in the timeInRangeType to trigger the UI to be updated
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.timeInRangeType.rawValue, options: .new, context: nil)
}
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath,
let keyPathEnum = UserDefaults.Key(rawValue: keyPath)
else { return }
switch keyPathEnum {
case UserDefaults.Key.timeInRangeType:
// we have to run this in the main thread to avoid access errors
DispatchQueue.main.async {
self.sectionReloadClosure?()
}
default:
break
}
}