Explorar o código

Merge pull request #70 from josep1972/dexcom

Merge All Latest Changes
jonfawcett %!s(int64=6) %!d(string=hai) anos
pai
achega
028873f9d2

+ 28 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -8,7 +8,14 @@
 
 /* Begin PBXBuildFile section */
 		3F1335F351590E573D8E6962 /* Pods_LoopFollow.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */; };
+		DD152D3B24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */; };
 		DD98F54424BCEFEE0007425A /* ShareClientExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */; };
+		DDCF979424C0D380002C9752 /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979324C0D380002C9752 /* UIViewExtension.swift */; };
+		DDCF979624C1443C002C9752 /* GeneralSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */; };
+		DDCF979824C1489C002C9752 /* GraphSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */; };
+		DDCF979A24C14DB4002C9752 /* WatchSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979924C14DB4002C9752 /* WatchSettingsViewController.swift */; };
+		DDCF979C24C14EFB002C9752 /* DebugSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979B24C14EFB002C9752 /* DebugSettingsViewController.swift */; };
+		DDCF979E24C2382A002C9752 /* AppStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979D24C2382A002C9752 /* AppStateController.swift */; };
 		FC16A97A24996673003D6245 /* NightScout.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC16A97924996673003D6245 /* NightScout.swift */; };
 		FC16A97B249966A3003D6245 /* AlarmSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7CE589248ABEA3001F83B8 /* AlarmSound.swift */; };
 		FC16A97D24996747003D6245 /* Alarms.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC16A97C24996747003D6245 /* Alarms.swift */; };
@@ -163,7 +170,14 @@
 /* Begin PBXFileReference section */
 		059B0FA59AABFE72FE13DDDA /* Pods-LoopFollow.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoopFollow.release.xcconfig"; path = "Target Support Files/Pods-LoopFollow/Pods-LoopFollow.release.xcconfig"; sourceTree = "<group>"; };
 		A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LoopFollow.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoDisplaySettingsViewController.swift; sourceTree = "<group>"; };
 		DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareClientExtension.swift; sourceTree = "<group>"; };
+		DDCF979324C0D380002C9752 /* UIViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtension.swift; sourceTree = "<group>"; };
+		DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsViewController.swift; sourceTree = "<group>"; };
+		DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphSettingsViewController.swift; sourceTree = "<group>"; };
+		DDCF979924C14DB4002C9752 /* WatchSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSettingsViewController.swift; sourceTree = "<group>"; };
+		DDCF979B24C14EFB002C9752 /* DebugSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugSettingsViewController.swift; sourceTree = "<group>"; };
+		DDCF979D24C2382A002C9752 /* AppStateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateController.swift; sourceTree = "<group>"; };
 		ECA3EFB4037410B4973BB632 /* Pods-LoopFollow.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoopFollow.debug.xcconfig"; path = "Target Support Files/Pods-LoopFollow/Pods-LoopFollow.debug.xcconfig"; sourceTree = "<group>"; };
 		FC16A97924996673003D6245 /* NightScout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightScout.swift; sourceTree = "<group>"; };
 		FC16A97C24996747003D6245 /* Alarms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alarms.swift; sourceTree = "<group>"; };
@@ -356,6 +370,7 @@
 			isa = PBXGroup;
 			children = (
 				DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */,
+				DDCF979324C0D380002C9752 /* UIViewExtension.swift */,
 			);
 			path = Extensions;
 			sourceTree = "<group>";
@@ -374,6 +389,7 @@
 		FC16A9782499657E003D6245 /* Controllers */ = {
 			isa = PBXGroup;
 			children = (
+				DDCF979D24C2382A002C9752 /* AppStateController.swift */,
 				FC7CE589248ABEA3001F83B8 /* AlarmSound.swift */,
 				FC16A97924996673003D6245 /* NightScout.swift */,
 				FC16A97C24996747003D6245 /* Alarms.swift */,
@@ -589,6 +605,11 @@
 				FCFEECA1248857A600402A7F /* SettingsViewController.swift */,
 				FCC6884F248935D800A0279D /* AlarmViewController.swift */,
 				FC7CE59E248D8D23001F83B8 /* SnoozeViewController.swift */,
+				DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */,
+				DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */,
+				DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */,
+				DDCF979924C14DB4002C9752 /* WatchSettingsViewController.swift */,
+				DDCF979B24C14EFB002C9752 /* DebugSettingsViewController.swift */,
 			);
 			path = ViewControllers;
 			sourceTree = "<group>";
@@ -835,7 +856,9 @@
 				FCC68850248935D800A0279D /* AlarmViewController.swift in Sources */,
 				FC7CE59F248D8D23001F83B8 /* SnoozeViewController.swift in Sources */,
 				FCC6886724898F8000A0279D /* UserDefaultsValue.swift in Sources */,
+				DDCF979E24C2382A002C9752 /* AppStateController.swift in Sources */,
 				FC97881E2485969B00A7906C /* NightScoutViewController.swift in Sources */,
+				DDCF979824C1489C002C9752 /* GraphSettingsViewController.swift in Sources */,
 				FC3AE7B5249E8E0E00AAE1E0 /* LoopFollow.xcdatamodeld in Sources */,
 				FCC6886F2489A53800A0279D /* AppConstants.swift in Sources */,
 				FC16A97A24996673003D6245 /* NightScout.swift in Sources */,
@@ -844,11 +867,15 @@
 				FC16A97B249966A3003D6245 /* AlarmSound.swift in Sources */,
 				FCFEECA2248857A600402A7F /* SettingsViewController.swift in Sources */,
 				FCFEECA02488157B00402A7F /* Chart.swift in Sources */,
+				DDCF979424C0D380002C9752 /* UIViewExtension.swift in Sources */,
 				FCEF87AC24A141A700AE6FA0 /* bgUnits.swift in Sources */,
 				FC1BDD3224A2585C001B652C /* DataStructs.swift in Sources */,
 				FCC6886524898EEE00A0279D /* UserDefaults.swift in Sources */,
 				FC16A97F249969E2003D6245 /* Graphs.swift in Sources */,
+				DD152D3B24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift in Sources */,
+				DDCF979C24C14EFB002C9752 /* DebugSettingsViewController.swift in Sources */,
 				FC97881C2485969B00A7906C /* MainViewController.swift in Sources */,
+				DDCF979624C1443C002C9752 /* GeneralSettingsViewController.swift in Sources */,
 				FCC0FAC224922A22003E610E /* DictionaryKeyPath.swift in Sources */,
 				FC9788182485969B00A7906C /* AppDelegate.swift in Sources */,
 				FCC6886B24898FD800A0279D /* ObservationToken.swift in Sources */,
@@ -859,6 +886,7 @@
 				FC1BDD2B24A22650001B652C /* Stats.swift in Sources */,
 				FC1BDD2D24A23204001B652C /* StatsView.swift in Sources */,
 				FCE537BC249A4D7D00F80BF8 /* carbBolusArrays.swift in Sources */,
+				DDCF979A24C14DB4002C9752 /* WatchSettingsViewController.swift in Sources */,
 				FC16A98124996C07003D6245 /* DateTime.swift in Sources */,
 				FC3CAB022493B6220068A152 /* BackgroundTaskAudio.swift in Sources */,
 			);

+ 8 - 7
LoopFollow/Application/AppDelegate.swift

@@ -16,7 +16,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
     var window: UIWindow?
     let notificationCenter = UNUserNotificationCenter.current()
-
+   
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         // Override point for customization after application launch.
         
@@ -30,12 +30,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         }
         let store = EKEventStore()
         store.requestAccess(to: .event) {(granted, error) in
-        if !granted { return }
-            }
-         
-        return true
-    }
-    
+           if !granted { return }
+        }
+        
+         return true
+      }
+        
+   
     func applicationWillTerminate(_ application: UIApplication) {
         if UserDefaultsRepository.alertAppInactive.value {
             AlarmSound.setSoundFile(str: "Alarm_Buzzer")

+ 32 - 1
LoopFollow/Application/SceneDelegate.swift

@@ -12,12 +12,43 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
 
     var window: UIWindow?
 
-
+    let appStateController = AppStateController()
+    
     func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
         // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
         // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
         // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
         guard let _ = (scene as? UIWindowScene) else { return }
+         
+        // get the tabBar
+        guard let tabBarController = window?.rootViewController as? UITabBarController,
+           let viewControllers = tabBarController.viewControllers
+        else {
+           return
+         }
+                  
+         // set the main controllers' connection to the app sate
+         // other controllers that need to know app state are setup programatically
+         for i in 0..<viewControllers.count {
+            if let vc = viewControllers[i] as? MainViewController {
+               vc.appStateController = appStateController
+            }
+            if let vc = viewControllers[i] as? NightscoutViewController {
+               vc.appStateController = appStateController
+            }
+            if let vc = viewControllers[i] as? SettingsViewController {
+               vc.appStateController = appStateController
+            }
+            if let vc = viewControllers[i] as? AlarmViewController {
+               vc.appStateController = appStateController
+            }
+            if let vc = viewControllers[i] as? SnoozeViewController {
+               vc.appStateController = appStateController
+            }
+            if let vc = viewControllers[i] as? debugViewController {
+               vc.appStateController = appStateController
+            }
+         }
     }
 
     func sceneDidDisconnect(_ scene: UIScene) {

+ 61 - 0
LoopFollow/Controllers/AppStateController.swift

@@ -0,0 +1,61 @@
+//
+//  AppStateController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/17/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+
+// App Sate used used to changes to the app view controllers (Settings, for example)
+// Recommended way of utilizing is when viewVillAppear(..) is called,
+// look in the app state to see if further action must be t
+
+// Setup App States to comminicate between views
+
+// Graph Setup Flags
+enum ChartSettingsChangeEnum: Int {
+  case chartScaleXChanged = 1
+  case showDotsChanged = 2
+  case showLinesChanged = 4
+  case offsetCarbsBolusChanged = 8
+  case hoursToLoadChanged = 16
+  case predictionToLoadChanged = 32
+  case minBasalScaleChanged = 64
+  case minBGScaleChanged = 128
+  case minBGValueChanged = 256
+  case overrideDisplayLocationChanged = 512
+  case lowLineChanged = 1024
+  case highLineChanged = 2048
+}
+
+// General Settings Flags
+enum GeneralSettingsChangeEnum: Int { 
+   case colorBGTextChange = 1
+   case speakBGChange = 2
+   case backgroundRefreshFrequencyChange = 4
+   case backgroundRefreshChange = 8
+   case appBadgeChange = 16
+   case dimScreenWhenIdleChange = 32
+   case forceDarkModeChang = 64
+   case persistentNotificationChange = 128
+   case persistentNotificationLastBGTimeChange = 256
+   case screenlockSwitchStateChange = 512
+}
+
+class AppStateController {
+   
+   // add app states & methods here
+
+   // General Settings States
+   var generalSettingsChanged : Bool = false
+   var generalSettingsChanges : Int = 0
+
+   // Chart Settings State
+   var chartSettingsChanged : Bool = false // settings change has ocurred
+   var chartSettingsChanges: Int = 0      // what settings have changed
+   
+   // Info Data Settings State; no need for flags
+   var infoDataSettingsChanged: Bool = false
+}

+ 23 - 8
LoopFollow/Controllers/Graphs.swift

@@ -10,10 +10,9 @@ import Foundation
 import Charts
 import UIKit
 
-
+let ScaleXMax:Float = 150.0
 extension MainViewController {
     
-
     func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
         if chartView == BGChartFull {
             BGChart.moveViewToX(entry.x)
@@ -27,9 +26,22 @@ extension MainViewController {
             //BGChartFull.highlightValue(x: Double(currentMatrix.tx), y: Double(currentMatrix.ty), dataSetIndex: 0)
         }
     }
+    
+    func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) {
+        print("Chart Scaled: \(BGChart.scaleX), \(BGChart.scaleY)")
+      
+        // dont store huge values
+        var scale: Float = Float(BGChart.scaleX)
+        if(scale > ScaleXMax ) {
+            scale = ScaleXMax
+        }
+        UserDefaultsRepository.chartScaleX.value = Float(scale)
+    }
+    
 
     func createGraph(){
         self.BGChart.clear()
+        
         // Create the BG Graph Data
         let entries = bgData
         var bgChartEntry = [ChartDataEntry]()
@@ -211,12 +223,11 @@ extension MainViewController {
         BGChart.scaleYEnabled = false
         BGChart.drawGridBackgroundEnabled = false
         //BGChart.gridBackgroundColor = NSUIColor.secondarySystemBackground
+      
         
         BGChart.data = data
         BGChart.setExtraOffsets(left: 10, top: 10, right: 10, bottom: 10)
         
-       
-        
     }
     
     func updateBGGraphSettings() {
@@ -255,7 +266,7 @@ extension MainViewController {
         ul.limit = Double(UserDefaultsRepository.highLine.value)
         ul.lineColor = NSUIColor.systemYellow.withAlphaComponent(0.5)
         BGChart.rightAxis.addLimitLine(ul)
-        
+    
         BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
         BGChart.data?.notifyDataChanged()
         BGChart.notifyDataSetChanged()
@@ -293,7 +304,6 @@ extension MainViewController {
         }
         
         
-        
         // Set Colors
         let lineBG = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
 
@@ -315,7 +325,6 @@ extension MainViewController {
         
         BGChart.rightAxis.axisMaximum = Double(maxBG)
         BGChart.setVisibleXRangeMinimum(600)
-        
         BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
         BGChart.data?.notifyDataChanged()
         BGChart.notifyDataSetChanged()
@@ -324,7 +333,13 @@ extension MainViewController {
         BGChartFull.notifyDataSetChanged()
         
         if firstGraphLoad {
-            BGChart.zoom(scaleX: 18, scaleY: 1, x: 1, y: 1)
+            var scaleX = CGFloat(UserDefaultsRepository.chartScaleX.value)
+            print("Scale: \(scaleX)")
+            if( scaleX > CGFloat(ScaleXMax) ) {
+                scaleX = CGFloat(ScaleXMax)
+                UserDefaultsRepository.chartScaleX.value = ScaleXMax
+            }
+            BGChart.zoom(scaleX: scaleX, scaleY: 1, x: 1, y: 1)
             firstGraphLoad = false
         }
         if BGChart.chartXMax > dateTimeUtils.getNowTimeIntervalUTC() {

+ 6 - 0
LoopFollow/Controllers/NightScout.swift

@@ -400,6 +400,7 @@ extension MainViewController {
         
         // Loop
         if let lastLoopRecord = lastDeviceStatus?["loop"] as! [String : AnyObject]? {
+            print("Loop: \(lastLoopRecord)")
             if let lastLoopTime = formatter.date(from: (lastLoopRecord["timestamp"] as! String))?.timeIntervalSince1970  {
                 UserDefaultsRepository.alertLastLoopTime.value = lastLoopTime
                 if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "lastLoopTime: " + String(lastLoopTime)) }
@@ -413,6 +414,7 @@ extension MainViewController {
                         if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Loop: Was Enacted") }
                         wasEnacted = true
                         if let lastTempBasal = enacted["rate"] as? Double {
+
                         }
                     }
                     if let iobdata = lastLoopRecord["iob"] as? [String:AnyObject] {
@@ -445,6 +447,9 @@ extension MainViewController {
                             updatePredictionGraph()
                         }
                     }
+                    if let recBolus = lastLoopRecord["recommendedBolus"] as? Double {
+                        tableData[8].value = String(format:"%.2fU", recBolus)
+                    }
                     if let loopStatus = lastLoopRecord["recommendedTempBasal"] as? [String:AnyObject] {
                         if let tempBasalTime = formatter.date(from: (loopStatus["timestamp"] as! String))?.timeIntervalSince1970 {
                             var lastBGTime = lastLoopTime
@@ -1019,6 +1024,7 @@ extension MainViewController {
         if UserDefaultsRepository.graphBasal.value {
             updateBasalGraph()
         }
+        infoTable.reloadData()
     }
     
     // NS Bolus Web Call

+ 29 - 0
LoopFollow/Extensions/UIViewExtension.swift

@@ -0,0 +1,29 @@
+//
+//  UIViewExtension.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension UIView {
+    enum ViewSide {
+        case Left, Right, Top, Bottom
+    }
+    
+    func addBorder(toSide side: ViewSide, withColor color: CGColor, andThickness thickness: CGFloat) {
+       let border = CALayer()
+       border.backgroundColor = color
+       
+       switch side {
+          case .Left: border.frame = CGRect(x:0, y: 0, width: thickness, height: frame.height); break
+          case .Right: border.frame = CGRect(x: frame.width-thickness, y: 0, width: thickness, height: frame.height); break
+          case .Top: border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness); break
+          case .Bottom: border.frame = CGRect(x: 0, y: frame.height-thickness, width: frame.width, height: thickness); break
+       }
+       layer.addSublayer(border)
+    }
+}

+ 1 - 0
LoopFollow/ViewControllers/AlarmViewController.swift

@@ -11,6 +11,7 @@ import UIKit
 import Eureka
 
 class AlarmViewController: FormViewController {
+    var appStateController: AppStateController?
     
     var soundFiles: [String] = [
         "Alarm_Buzzer",

+ 13 - 0
LoopFollow/ViewControllers/AppStateViewController.swift

@@ -0,0 +1,13 @@
+//
+//  AppStateViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/17/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+
+class AppStateViewController {
+   var appStateController: AppStateController?
+}

+ 113 - 0
LoopFollow/ViewControllers/DebugSettingsViewController.swift

@@ -0,0 +1,113 @@
+//
+//  DebugSettingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import Eureka
+import EventKit
+import EventKitUI
+
+class DebugSettingsViewController: FormViewController {
+
+   var appStateController: AppStateController?
+   
+   override func viewDidLoad()  {
+      super.viewDidLoad()
+      if UserDefaultsRepository.forceDarkMode.value {
+         overrideUserInterfaceStyle = .dark
+      }
+      buildDebugSettings()
+   }
+   private func buildDebugSettings() {
+        form
+            +++ Section("Debug Settings")
+
+        <<< SwitchRow("downloadBasal"){ row in
+            row.title = "Download Basal"
+            row.value = UserDefaultsRepository.downloadBasal.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.downloadBasal.value = value
+            }
+            <<< SwitchRow("graphBasal"){ row in
+            row.title = "Graph Basal"
+            row.value = UserDefaultsRepository.graphBasal.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.graphBasal.value = value
+            }
+            <<< SwitchRow("downloadBolus"){ row in
+                row.title = "Download Bolus"
+                row.value = UserDefaultsRepository.downloadBolus.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.downloadBolus.value = value
+                }
+           <<< SwitchRow("graphBolus"){ row in
+               row.title = "Graph Bolus"
+               row.value = UserDefaultsRepository.graphBolus.value
+           }.onChange { [weak self] row in
+                       guard let value = row.value else { return }
+                       UserDefaultsRepository.graphBolus.value = value
+               }
+            <<< SwitchRow("downloadCarbs"){ row in
+                row.title = "Download Carbs"
+                row.value = UserDefaultsRepository.downloadCarbs.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.downloadCarbs.value = value
+                }
+              <<< SwitchRow("graphCarbs"){ row in
+                  row.title = "Graph Carbs"
+                  row.value = UserDefaultsRepository.graphCarbs.value
+              }.onChange { [weak self] row in
+                          guard let value = row.value else { return }
+                          UserDefaultsRepository.graphCarbs.value = value
+                  }
+            
+        <<< SwitchRow("downloadPrediction"){ row in
+                 row.title = "Download Prediction"
+                 row.value = UserDefaultsRepository.downloadPrediction.value
+             }.onChange { [weak self] row in
+                         guard let value = row.value else { return }
+                         UserDefaultsRepository.downloadPrediction.value = value
+                 }
+        <<< SwitchRow("graphPrediction"){ row in
+            row.title = "Graph Prediction"
+            row.value = UserDefaultsRepository.graphPrediction.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.graphPrediction.value = value
+            }
+            <<< SwitchRow("debugLog"){ row in
+                row.title = "Show Debug Log"
+                row.value = UserDefaultsRepository.debugLog.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.debugLog.value = value
+                }
+        <<< StepperRow("viewRefreshDelay") { row in
+            row.title = "View Refresh Delay"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = 5
+            row.cell.stepper.maximumValue = 30
+            row.value = Double(UserDefaultsRepository.viewRefreshDelay.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return "\(Int(value))"
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.viewRefreshDelay.value = Double(value)
+        }
+        +++ ButtonRow() {
+          $0.title = "DONE"
+        }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+        }
+    }
+}

+ 2 - 0
LoopFollow/ViewControllers/DebugViewController.swift

@@ -14,6 +14,8 @@ class debugViewController: UIViewController {
     @IBOutlet weak var debugLogTextView: UITextView!
     @IBOutlet weak var clearButton: UIButton!
     
+    var appStateController: AppStateController?
+    
     override func viewDidLoad() {
         super.viewDidLoad()
         debugLogTextView.isEditable = false

+ 126 - 0
LoopFollow/ViewControllers/GeneralSettingsViewController.swift

@@ -0,0 +1,126 @@
+//
+//  GeneralSetingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import Eureka
+import EventKit
+import EventKitUI
+
+class GeneralSettingsViewController: FormViewController {
+   
+   var appStateController: AppStateController?
+   
+   override func viewDidLoad()  {
+      super.viewDidLoad()
+      
+      if UserDefaultsRepository.forceDarkMode.value {
+         overrideUserInterfaceStyle = .dark
+      }
+      buildGeneralSettings()
+   }
+   
+   private func buildGeneralSettings() {
+      form
+        +++ Section("General Settings")
+        <<< SwitchRow("colorBGText") { row in
+        row.title = "Color Main BG Text"
+        row.value = UserDefaultsRepository.colorBGText.value
+        }.onChange { [weak self] row in
+            guard let value = row.value else { return }
+            UserDefaultsRepository.colorBGText.value = value
+            // Force main screen update
+            //guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            //mainScreen.setBGTextColor()
+            
+            // set the appstate to indicate settings change and flags
+            if let appState = self!.appStateController {
+              appState.generalSettingsChanged = true
+              appState.generalSettingsChanges |= GeneralSettingsChangeEnum.colorBGTextChange.rawValue
+           }
+        }
+        <<< SwitchRow("forceDarkMode") { row in
+        row.title = "Force Dark Mode (Restart App)"
+        row.value = UserDefaultsRepository.forceDarkMode.value
+        }.onChange { [weak self] row in
+            guard let value = row.value else { return }
+            UserDefaultsRepository.forceDarkMode.value = value
+             
+        }
+        <<< SwitchRow("persistentNotification") { row in
+        row.title = "Persistent Notification"
+        row.value = UserDefaultsRepository.persistentNotification.value
+        }.onChange { [weak self] row in
+            guard let value = row.value else { return }
+                UserDefaultsRepository.persistentNotification.value = value
+        }
+        <<< SwitchRow("screenlockSwitchState") { row in
+            row.title = "Keep Screen Active"
+            row.value = UserDefaultsRepository.screenlockSwitchState.value
+            }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                
+                if value {
+                    UserDefaultsRepository.screenlockSwitchState.value = value
+                }
+            }
+        
+        <<< SwitchRow("backgroundRefresh"){ row in
+            row.title = "Background Refresh"
+            row.tag = "backgroundRefresh"
+            row.value = UserDefaultsRepository.backgroundRefresh.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.backgroundRefresh.value = value
+            }
+        <<< StepperRow("backgroundRefreshFrequency") { row in
+            row.title = "Refresh Minutes"
+            row.tag = "backgroundRefreshFrequency"
+            row.cell.stepper.stepValue = 0.25
+            row.cell.stepper.minimumValue = 0.25
+            row.cell.stepper.maximumValue = 10
+            row.value = Double(UserDefaultsRepository.backgroundRefreshFrequency.value)
+            row.hidden = "$backgroundRefresh == false"
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.backgroundRefreshFrequency.value = value
+        }
+            
+        <<< SwitchRow("appBadge"){ row in
+            row.title = "Display App Badge"
+            row.tag = "appBadge"
+            row.value = UserDefaultsRepository.appBadge.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.appBadge.value = value
+                    // Force main screen update
+                    //guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+                    //mainScreen.nightscoutLoader(forceLoad: true)
+                    
+           // set the appstate to indicate settings change and flags
+           if let appState = self!.appStateController {
+              appState.generalSettingsChanged = true
+              appState.generalSettingsChanges |= GeneralSettingsChangeEnum.appBadgeChange.rawValue
+           }
+           
+        }
+        <<< SwitchRow("speakBG"){ row in
+            row.title = "Speak BG"
+            row.value = UserDefaultsRepository.speakBG.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.speakBG.value = value
+        }
+        
+        +++ ButtonRow() {
+          $0.title = "DONE"
+       }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+       }
+    }
+   
+}

+ 197 - 0
LoopFollow/ViewControllers/GraphSettingsViewController.swift

@@ -0,0 +1,197 @@
+//
+//  GraphSettingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import Eureka
+import EventKit
+import EventKitUI
+
+class GraphSettingsViewController: FormViewController {
+
+   var appStateController: AppStateController?
+   
+   override func viewDidLoad()  {
+      super.viewDidLoad()
+      if UserDefaultsRepository.forceDarkMode.value {
+         overrideUserInterfaceStyle = .dark
+      }
+ 
+      buildGraphSettings()
+   }
+   
+    private func buildGraphSettings() {
+        form
+            +++ Section("Graph Settings")
+            
+        <<< SwitchRow("switchRowDots"){ row in
+            row.title = "Display Dots"
+            row.value = UserDefaultsRepository.showDots.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.showDots.value = value
+                // Force main screen update
+                // guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+                // mainScreen.updateBGGraphSettings()
+                
+                // tell main screen that grap needs updating
+                if let appState = self!.appStateController {
+                   appState.chartSettingsChanged = true
+                   appState.chartSettingsChanges |= ChartSettingsChangeEnum.showDotsChanged.rawValue
+                }
+            }
+        <<< SwitchRow("switchRowLines"){ row in
+            row.title = "Display Lines"
+            row.value = UserDefaultsRepository.showLines.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.showLines.value = value
+            // Force main screen update
+            //guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            //mainScreen.updateBGGraphSettings()
+           
+            if let appState = self!.appStateController {
+               appState.chartSettingsChanged = true
+               appState.chartSettingsChanges |= ChartSettingsChangeEnum.showLinesChanged.rawValue
+             }
+               
+        }
+            <<< SwitchRow("offsetCarbsBolus"){ row in
+                row.title = "Offset Carb/Bolus Dots"
+                row.value = UserDefaultsRepository.offsetCarbsBolus.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.offsetCarbsBolus.value = value
+                        
+            }
+            <<< StepperRow("predictionToLoad") { row in
+                row.title = "Hours of Prediction"
+                row.cell.stepper.stepValue = 0.25
+                row.cell.stepper.minimumValue = 0.0
+                row.cell.stepper.maximumValue = 6.0
+                row.value = Double(UserDefaultsRepository.predictionToLoad.value)
+            }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.predictionToLoad.value = value
+            }
+        <<< StepperRow("minBGScale") { row in
+            row.title = "Min BG Scale"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = Double(UserDefaultsRepository.highLine.value)
+            row.cell.stepper.maximumValue = 400
+            row.value = Double(UserDefaultsRepository.minBGScale.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+                
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.minBGScale.value = Float(value)
+                
+            }
+            <<< StepperRow("minBGValue") { row in
+                row.title = "Min BG Display"
+                row.cell.stepper.stepValue = 1
+                row.cell.stepper.minimumValue = -40
+                row.cell.stepper.maximumValue = 40
+                row.value = Double(UserDefaultsRepository.minBGValue.value)
+                row.displayValueFor = { value in
+                    guard let value = value else { return nil }
+                    return bgUnits.toDisplayUnits(String(value))
+                }
+            }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.minBGValue.value = Float(value)
+                // Force main screen update
+                //guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+                //mainScreen.updateBGGraphSettings()
+              
+                if let appState = self!.appStateController {
+                  appState.chartSettingsChanged = true
+                  appState.chartSettingsChanges |= ChartSettingsChangeEnum.minBGValueChanged.rawValue
+               }
+                
+            }
+        <<< StepperRow("minBasalScale") { row in
+            row.title = "Min Basal Scale"
+            row.cell.stepper.stepValue = 0.5
+            row.cell.stepper.minimumValue = 0.5
+            row.cell.stepper.maximumValue = 20
+            row.value = Double(UserDefaultsRepository.minBasalScale.value)
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.minBasalScale.value = value
+        }
+        <<< StepperRow("lowLine") { row in
+            row.title = "Low BG Display Value"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = 40
+            row.cell.stepper.maximumValue = 120
+            row.value = Double(UserDefaultsRepository.lowLine.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.lowLine.value = Float(value)
+            // Force main screen update
+            //guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            //mainScreen.updateBGGraphSettings()
+            
+            // tell main screen to update
+            if let appState = self!.appStateController {
+               appState.chartSettingsChanged = true
+               appState.chartSettingsChanges |= ChartSettingsChangeEnum.lowLineChanged.rawValue
+             }
+        }
+        <<< StepperRow("highLine") { row in
+            row.title = "High BG Display Value"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = 120
+            row.cell.stepper.maximumValue = 400
+            row.value = Double(UserDefaultsRepository.highLine.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.highLine.value = Float(value)
+            // Force main screen update
+            //guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            //mainScreen.updateBGGraphSettings()
+            
+            // let app state know of the change
+            if let appState = self!.appStateController {
+               appState.chartSettingsChanged = true
+               appState.chartSettingsChanges |= ChartSettingsChangeEnum.highLineChanged.rawValue
+             }
+        }
+        <<< StepperRow("overrideDisplayLocation") { row in
+            row.title = "Override BG Location"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = Double(UserDefaultsRepository.minBGValue.value)
+            row.cell.stepper.maximumValue = Double(UserDefaultsRepository.minBGScale.value)
+            row.value = Double(UserDefaultsRepository.overrideDisplayLocation.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.overrideDisplayLocation.value = Float(value)
+            }
+            
+       +++ ButtonRow() {
+          $0.title = "DONE"
+       }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+       }
+    }
+}

+ 124 - 0
LoopFollow/ViewControllers/InfoDisplaySettingsViewController.swift

@@ -0,0 +1,124 @@
+//
+//  InfoDisplaySettingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import UIKit
+import Eureka
+import EventKit
+import EventKitUI
+
+class InfoDisplaySettingsViewController: FormViewController {
+    var appStateController: AppStateController?
+    
+    var infoNames:   [String] = []
+    var infoSort:    [Int]    = []
+    var infoVisible: [Bool]   = []
+    
+    override func viewDidLoad() {
+        print("Display Load")
+        super.viewDidLoad()
+        if UserDefaultsRepository.forceDarkMode.value {
+            overrideUserInterfaceStyle = .dark
+        }
+         
+        // get the info table
+        let userDefaults = UserDefaults.standard
+
+        // names
+        self.infoNames = userDefaults.stringArray(forKey:InfoNames) ?? [String]()
+        if(self.infoNames.count == 0) {
+            self.infoNames = DefaultInfoNames
+            userDefaults.set(self.infoNames, forKey:InfoNames)
+        }
+        
+        // sort
+        self.infoSort = userDefaults.array(forKey:InfoSort) as? [Int] ?? [Int]()
+        if(self.infoSort.count != self.infoNames.count) {
+            self.infoSort = []
+            for i in 0..<self.infoNames.count {
+                self.infoSort.append(i)
+            }
+            userDefaults.set(self.infoSort, forKey:InfoSort)
+        }
+        // visible
+        self.infoVisible = userDefaults.array(forKey:InfoVisible) as? [Bool] ?? [Bool]()
+        if(self.infoVisible.count != self.infoNames.count) {
+            self.infoVisible = []
+            for _ in 0..<self.infoNames.count {
+                self.infoVisible.append(true)
+            }
+            userDefaults.set(self.infoVisible, forKey:InfoVisible)
+        }
+        createForm()
+    }
+    
+    private func createForm() {
+        form
+        +++ MultivaluedSection(multivaluedOptions: .Reorder, header: "Information Display Settings", footer: "Arrage/Enable Information Desired") {
+        
+           // TODO: add the other display values
+           $0.tag = "InfoDisplay"
+           
+            for i in 0..<self.infoNames.count {
+              $0 <<< TextRow() { row in
+                if(self.infoVisible[self.infoSort[i]]) {
+                    row.title = "\u{2713}\t\(self.infoNames[self.infoSort[i]])"
+                 } else {
+                    row.title = "\u{2001}\t\(self.infoNames[self.infoSort[i]])"
+                 }
+              }.onCellSelection{(cell, row) in
+                let i = row.indexPath!.row
+                self.infoVisible[self.infoSort[i]] = !self.infoVisible[self.infoSort[i]]
+                
+                // save info visible
+                UserDefaults.standard.set(self.infoVisible, forKey:InfoVisible)
+                
+                self.tableView.reloadData()
+                
+                //print("\(row.title)")
+                //print("\(row.indexPath?.row)")
+              }.cellSetup { (cell, row) in
+                 cell.textField.isUserInteractionEnabled = false
+              }.cellUpdate{ (cell, row) in
+                 if(self.infoVisible[self.infoSort[i]]) {
+                    row.title = "\u{2713}\t\(self.infoNames[self.infoSort[i]])"
+                 } else {
+                    row.title = "\u{2001}\t\(self.infoNames[self.infoSort[i]])"
+                 }
+                 self.appStateController!.infoDataSettingsChanged = true
+              }
+           }
+       }
+    
+       +++ ButtonRow() {
+          $0.title = "DONE"
+       }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+       }
+    }
+    
+    override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
+        //let view = tableView
+        let sourceIndex = sourceIndexPath.row
+        let destIndex = destinationIndexPath.row
+        
+        // new sort
+        if(destIndex != sourceIndex ) {
+           self.appStateController!.infoDataSettingsChanged = true
+           
+           let tmpVal = self.infoSort[sourceIndex]
+           self.infoSort.remove(at:sourceIndex)
+           self.infoSort.insert(tmpVal, at:destIndex)
+       
+           // save to defaults
+           UserDefaults.standard.set(self.infoSort, forKey:InfoSort)
+        }
+        
+        //print("Source Row: \(sourceIndexPath.row); Source Section: \(sourceIndexPath.section)")
+        //print("Destination Row: \(destinationIndexPath.row); Destination Section: \(destinationIndexPath.section)")
+    }
+ }

+ 134 - 25
LoopFollow/ViewControllers/MainViewController.swift

@@ -11,6 +11,20 @@ import Charts
 import EventKit
 import ShareClient
 
+let DefaultInfoNames = [
+   "IOB",
+   "COB",
+   "Basal",
+   "Override",
+   "Battery",
+   "Pump",
+   "SAGE",
+   "CAGE",
+   "Rec. Bolus"
+]
+let InfoNames = "infoNames"
+let InfoSort = "infoSort"
+let InfoVisible = "infoVisible"
 
 class MainViewController: UIViewController, UITableViewDataSource, ChartViewDelegate, UNUserNotificationCenterDelegate {
     
@@ -33,13 +47,19 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     @IBOutlet weak var statsEstA1C: UILabel!
     @IBOutlet weak var statsStdDev: UILabel!
     @IBOutlet weak var serverText: UILabel!
-    
-    
-    // Data Table Struct
-    struct infoData {
-        var name: String
-        var value: String
+
+      
+    // Data Table class
+    class infoData {
+        public var name: String
+        public var value: String
+        init(name: String, value: String) {
+            self.name = name
+            self.value = value
+        }
     }
+    
+    var appStateController: AppStateController?
 
     // Variables for BG Charts
     public var numPoints: Int = 13
@@ -78,17 +98,11 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     var calTimer = Timer()
     
     // Info Table Setup
-    // TODO: make this selectable
-    var tableData = [
-        infoData(name: "IOB", value: ""), //0
-        infoData(name: "COB", value: ""), //1
-        infoData(name: "Basal", value: ""), //2
-        infoData(name: "Override", value: ""), //3
-        infoData(name: "Battery", value: ""), //4
-        infoData(name: "Pump", value: ""), //5
-        infoData(name: "SAGE", value: ""), //6
-        infoData(name: "CAGE", value: "") //7
-    ]
+    var infoNames : [String] = []
+    var infoSort : [Int] = []
+    var infoVisible: [Bool] = []
+    var tableData : [infoData] = []
+    var derivedTableData: [infoData] = []
     
     var bgData: [ShareGlucoseData] = []
     var basalProfile: [basalProfileStruct] = []
@@ -123,8 +137,35 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     
     override func viewDidLoad() {
         super.viewDidLoad()
+
         
-        // TODO: look for username and password in the UserDefaultsRepo ?
+        // table view
+        //infoTable.layer.borderColor = UIColor.darkGray.cgColor
+        //infoTable.layer.borderWidth = 1.0
+        //infoTable.layer.cornerRadius = 6
+        infoTable.rowHeight = 24
+        infoTable.dataSource = self
+        infoTable.tableFooterView = UIView(frame: .zero) // get rid of extra rows
+        infoTable.bounces = false
+        infoTable.addBorder(toSide: .Left, withColor: UIColor.darkGray.cgColor, andThickness: 2)
+      
+        // get the info table setup
+        let userDefaults = UserDefaults.standard
+
+        // names
+        self.infoNames = userDefaults.stringArray(forKey:InfoNames) ?? [String]()
+        if(self.infoNames.count == 0) {
+            self.infoNames = DefaultInfoNames
+            userDefaults.set(self.infoNames, forKey:InfoNames)
+        }
+        
+        // initialize the tableData
+        self.tableData = []
+        for i in 0..<self.infoNames.count {
+            self.tableData.append(infoData(name:self.infoNames[i], value:""))
+        }
+        createDerivedData()
+      
         // TODO: need non-us server ?
         let shareUserName = UserDefaultsRepository.shareUserName.value
         let sharePassword = UserDefaultsRepository.sharePassword.value
@@ -163,11 +204,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         let notificationCenter = NotificationCenter.default
             notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
             notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
-        
-        //Bind info data
-        infoTable.rowHeight = 25
-        infoTable.dataSource = self
-        
+         
         // Setup the Graph
         if firstGraphLoad {
             createGraph()
@@ -187,16 +224,88 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         // set screen lock
         UIApplication.shared.isIdleTimerDisabled = UserDefaultsRepository.screenlockSwitchState.value;
         
+        // check the app state
+        // TODO: move to a function ?
+        if let appState = self.appStateController {
+        
+           if appState.chartSettingsChanged {
+              
+              // can look at settings flags to be more fine tuned
+              self.updateBGGraphSettings()
+              
+              // reset the app state
+              appState.chartSettingsChanged = false
+              appState.chartSettingsChanges = 0
+           }
+           if appState.generalSettingsChanged {
+           
+              // settings for appBadge changed
+              if appState.generalSettingsChanges & GeneralSettingsChangeEnum.appBadgeChange.rawValue != 0 {
+                 self.nightscoutLoader(forceLoad: true)
+              }
+              
+              // settings for textcolor changed
+              if appState.generalSettingsChanges & GeneralSettingsChangeEnum.colorBGTextChange.rawValue != 0 {
+                 self.setBGTextColor()
+              }
+              
+              // reset the app state
+              appState.generalSettingsChanged = false
+              appState.generalSettingsChanges = 0
+           }
+           if appState.infoDataSettingsChanged {
+              createDerivedData()
+              self.infoTable.reloadData()
+              
+              // reset
+              appState.infoDataSettingsChanged = false
+           }
+           
+           // add more processing of the app state
+        }
     }
     
+    private func createDerivedData() {
+      // get the info table
+        let userDefaults = UserDefaults.standard
+         
+        // sort
+        self.infoSort = userDefaults.array(forKey:InfoSort) as? [Int] ?? [Int]()
+        if(self.infoSort.count != self.infoNames.count) {
+            self.infoSort = []
+            for i in 0..<self.infoNames.count {
+                self.infoSort.append(i)
+            }
+            userDefaults.set(self.infoSort, forKey:InfoSort)
+        }
+        // visible
+        infoVisible = userDefaults.array(forKey:InfoVisible) as? [Bool] ?? [Bool]()
+        if(self.infoVisible.count != self.infoNames.count) {
+            self.infoVisible = []
+            for _ in 0..<self.infoNames.count {
+                self.infoVisible.append(true)
+            }
+            userDefaults.set(self.infoVisible, forKey:InfoVisible)
+        }
+        
+        self.derivedTableData = []
+        for i in 0..<self.tableData.count {
+            if(self.infoVisible[self.infoSort[i]]) {
+                self.derivedTableData.append(self.tableData[self.infoSort[i]])
+            }
+        }
+   }
+   
     // Info Table Functions
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return tableData.count
+        //return tableData.count
+        return derivedTableData.count
+        
     }
     
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath)
-        let values = tableData[indexPath.row]
+        let values = derivedTableData[indexPath.row]
         cell.textLabel?.text = values.name
         cell.detailTextLabel?.text = values.value
         return cell

+ 1 - 0
LoopFollow/ViewControllers/NightScoutViewController.swift

@@ -15,6 +15,7 @@ class NightscoutViewController: UIViewController {
 
     @IBOutlet weak var webView: WKWebView!
     
+    var appStateController: AppStateController?
     
     override func viewDidLoad() {
         super.viewDidLoad()

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 55 - 369
LoopFollow/ViewControllers/SettingsViewController.swift


+ 1 - 1
LoopFollow/ViewControllers/SnoozeViewController.swift

@@ -11,7 +11,7 @@ import UserNotifications
 
 
 class SnoozeViewController: UIViewController, UNUserNotificationCenterDelegate {
-
+    var appStateController: AppStateController?
     var snoozeTabItem: UITabBarItem = UITabBarItem()
     var mainTabItem: UITabBarItem = UITabBarItem()
     

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 96 - 0
LoopFollow/ViewControllers/WatchSettingsViewController.swift


+ 6 - 4
LoopFollow/repository/UserDefaults.swift

@@ -10,19 +10,22 @@ import Foundation
 import UIKit
 
 class UserDefaultsRepository {
+
+    // DisplayValues total
+    static let infoDataTotal = UserDefaultsValue<Int>(key: "infoDataTotal", default: 0)
     
-    
-    //Nightscout Settings
+    // Nightscout Settings
     static let url = UserDefaultsValue<String>(key: "url", default: "")
     static let token = UserDefaultsValue<String>(key: "token", default: "")
     static let units = UserDefaultsValue<String>(key: "units", default: "mg/dL")
     
-    //Dexcom Share Settings
+    // Dexcom Share Settings
     static let shareUserName = UserDefaultsValue<String>(key: "shareUserName", default: "")
     static let sharePassword = UserDefaultsValue<String>(key: "sharePassword", default: "")
     static let shareServer = UserDefaultsValue<String>(key: "shareServer", default: "US")
     
     // Graph Settings
+    static let chartScaleX = UserDefaultsValue<Float>(key: "chartScaleX", default: 18.0)
     static let showDots = UserDefaultsValue<Bool>(key: "showDots", default: true)
     static let showLines = UserDefaultsValue<Bool>(key: "showLines", default: true)
     static let offsetCarbsBolus = UserDefaultsValue<Bool>(key: "offsetCarbsBolus", default: true)
@@ -92,7 +95,6 @@ class UserDefaultsRepository {
     static let alertCageInsertTime = UserDefaultsValue<TimeInterval>(key: "alertCageInsertTime", default: 0)
     static let alertSageInsertTime = UserDefaultsValue<TimeInterval>(key: "alertSageInsertTime", default: 0)
     
-    
     static let alertSnoozeAllTime = UserDefaultsValue<Date?>(key: "alertSnoozeAllTime", default: nil)
     static let alertSnoozeAllIsSnoozed = UserDefaultsValue<Bool>(key: "alertSnoozeAllIsSnoozed", default: false)