SettingsViewController.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. //
  2. // SettingsViewController.swift
  3. // LoopFollow
  4. //
  5. // Created by Jon Fawcett on 6/3/20.
  6. // Copyright © 2020 Jon Fawcett. All rights reserved.
  7. //
  8. import UIKit
  9. import Eureka
  10. import EventKit
  11. import EventKitUI
  12. class SettingsViewController: FormViewController {
  13. var appStateController: AppStateController?
  14. var statusLabelRow: LabelRow!
  15. func showHideNSDetails() {
  16. var isHidden = false
  17. var isEnabled = true
  18. var isLoopHidden = false;
  19. if UserDefaultsRepository.url.value == "" || !UserDefaultsRepository.loopUser.value {
  20. isHidden = true
  21. isEnabled = false
  22. }
  23. if let row1 = form.rowBy(tag: "informationDisplaySettings") as? ButtonRow {
  24. row1.hidden = .function(["hide"], {form in
  25. return isHidden
  26. })
  27. row1.evaluateHidden()
  28. }
  29. if UserDefaultsRepository.url.value != "" {
  30. isEnabled = true
  31. }
  32. guard let nightscoutTab = self.tabBarController?.tabBar.items![3] else { return }
  33. nightscoutTab.isEnabled = isEnabled
  34. }
  35. override func viewDidLoad() {
  36. super.viewDidLoad()
  37. if UserDefaultsRepository.forceDarkMode.value {
  38. overrideUserInterfaceStyle = .dark
  39. }
  40. UserDefaultsRepository.showNS.value = false
  41. UserDefaultsRepository.showDex.value = false
  42. var expiration: Date = Date()
  43. if let provision = MobileProvision.read() {
  44. expiration = provision.expirationDate
  45. }
  46. form
  47. +++ Section(header: "Data Settings", footer: "")
  48. <<< SegmentedRow<String>("units") { row in
  49. row.title = "Units"
  50. row.options = ["mg/dL", "mmol/L"]
  51. row.value = UserDefaultsRepository.units.value
  52. }.onChange { row in
  53. guard let value = row.value else { return }
  54. UserDefaultsRepository.units.value = value
  55. }
  56. <<< SwitchRow("showNS"){ row in
  57. row.title = "Show Nightscout Settings"
  58. row.value = UserDefaultsRepository.showNS.value
  59. }.onChange { [weak self] row in
  60. guard let value = row.value else { return }
  61. UserDefaultsRepository.showNS.value = value
  62. }
  63. <<< TextRow() { row in
  64. row.title = "URL"
  65. row.placeholder = "https://mycgm.herokuapp.com"
  66. row.value = UserDefaultsRepository.url.value
  67. row.hidden = "$showNS == false"
  68. }.cellSetup { (cell, row) in
  69. cell.textField.autocorrectionType = .no
  70. cell.textField.autocapitalizationType = .none
  71. }.onChange { row in
  72. guard let value = row.value else {
  73. UserDefaultsRepository.url.value = ""
  74. self.showHideNSDetails()
  75. return }
  76. // check the format of the URL entered by the user and trim away any spaces or "/" at the end
  77. var urlNSInput = value.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression)
  78. if urlNSInput.last == "/" {
  79. urlNSInput = String(urlNSInput.dropLast())
  80. }
  81. UserDefaultsRepository.url.value = urlNSInput.lowercased()
  82. // set the row value back to the correctly formatted URL so that the user immediately sees how it should have been written
  83. row.value = UserDefaultsRepository.url.value
  84. self.showHideNSDetails()
  85. globalVariables.nsVerifiedAlert = 0
  86. // Verify Nightscout URL and token
  87. self.checkNightscoutStatus()
  88. }
  89. <<< TextRow() { row in
  90. row.title = "NS Token"
  91. row.placeholder = "Leave blank if not using tokens"
  92. row.value = UserDefaultsRepository.token.value
  93. row.hidden = "$showNS == false"
  94. }.cellSetup { (cell, row) in
  95. cell.textField.autocorrectionType = .no
  96. cell.textField.autocapitalizationType = .none
  97. cell.textField.textContentType = .password
  98. }.onChange { row in
  99. if row.value == nil {
  100. UserDefaultsRepository.token.value = ""
  101. }
  102. guard let value = row.value else { return }
  103. UserDefaultsRepository.token.value = value
  104. globalVariables.nsVerifiedAlert = 0
  105. // Verify Nightscout URL and token
  106. self.checkNightscoutStatus()
  107. }
  108. <<< LabelRow() { row in
  109. row.title = "NS Status"
  110. row.value = "Checking..."
  111. statusLabelRow = row
  112. row.hidden = "$showNS == false"
  113. }
  114. <<< SwitchRow("loopUser"){ row in
  115. row.title = "Download Loop/iAPS Data"
  116. row.tag = "loopUser"
  117. row.value = UserDefaultsRepository.loopUser.value
  118. row.hidden = "$showNS == false"
  119. }.onChange { [weak self] row in
  120. guard let value = row.value else { return }
  121. UserDefaultsRepository.loopUser.value = value
  122. }
  123. <<< SwitchRow("showDex"){ row in
  124. row.title = "Show Dexcom Settings"
  125. row.value = UserDefaultsRepository.showDex.value
  126. }.onChange { [weak self] row in
  127. guard let value = row.value else { return }
  128. UserDefaultsRepository.showDex.value = value
  129. }
  130. <<< TextRow(){ row in
  131. row.title = "User Name"
  132. row.value = UserDefaultsRepository.shareUserName.value
  133. row.hidden = "$showDex == false"
  134. }.cellSetup { (cell, row) in
  135. cell.textField.autocorrectionType = .no
  136. cell.textField.autocapitalizationType = .none
  137. }.onChange { row in
  138. if row.value == nil {
  139. UserDefaultsRepository.shareUserName.value = ""
  140. }
  141. guard let value = row.value else { return }
  142. UserDefaultsRepository.shareUserName.value = value
  143. globalVariables.dexVerifiedAlert = 0
  144. }
  145. <<< TextRow(){ row in
  146. row.title = "Password"
  147. row.value = UserDefaultsRepository.sharePassword.value
  148. row.hidden = "$showDex == false"
  149. }.cellSetup { (cell, row) in
  150. cell.textField.autocorrectionType = .no
  151. cell.textField.isSecureTextEntry = true
  152. cell.textField.autocapitalizationType = .none
  153. }.onChange { row in
  154. if row.value == nil {
  155. UserDefaultsRepository.sharePassword.value = ""
  156. }
  157. guard let value = row.value else { return }
  158. UserDefaultsRepository.sharePassword.value = value
  159. globalVariables.dexVerifiedAlert = 0
  160. }
  161. <<< SegmentedRow<String>("shareServer") { row in
  162. row.title = "Server"
  163. row.options = ["US", "NON-US"]
  164. row.value = UserDefaultsRepository.shareServer.value
  165. row.hidden = "$showDex == false"
  166. }.onChange { row in
  167. guard let value = row.value else { return }
  168. UserDefaultsRepository.shareServer.value = value
  169. }
  170. +++ Section("App Settings")
  171. <<< ButtonRow() {
  172. $0.title = "General Settings"
  173. $0.presentationMode = .show(
  174. controllerProvider: .callback(builder: {
  175. let controller = GeneralSettingsViewController()
  176. controller.appStateController = self.appStateController
  177. return controller
  178. }
  179. ), onDismiss: nil)
  180. }
  181. <<< ButtonRow("graphSettings") {
  182. $0.title = "Graph Settings"
  183. $0.presentationMode = .show(
  184. controllerProvider: .callback(builder: {
  185. let controller = GraphSettingsViewController()
  186. controller.appStateController = self.appStateController
  187. return controller
  188. }
  189. ), onDismiss: nil)
  190. }
  191. <<< ButtonRow("informationDisplaySettings") {
  192. $0.title = "Information Display Settings"
  193. $0.presentationMode = .show(
  194. controllerProvider: .callback(builder: {
  195. let controller = InfoDisplaySettingsViewController()
  196. controller.appStateController = self.appStateController
  197. return controller
  198. }
  199. ), onDismiss: nil)
  200. }
  201. +++ Section("Integrations")
  202. <<< ButtonRow() {
  203. $0.title = "Apple Watch and Carplay"
  204. $0.presentationMode = .show(
  205. controllerProvider: .callback(builder: {
  206. let controller = WatchSettingsViewController()
  207. controller.appStateController = self.appStateController
  208. return controller
  209. }
  210. ), onDismiss: nil)
  211. }
  212. +++ Section("Advanced Settings")
  213. <<< ButtonRow() {
  214. $0.title = "Advanced Settings"
  215. $0.presentationMode = .show(
  216. controllerProvider: .callback(builder: {
  217. let controller = AdvancedSettingsViewController()
  218. controller.appStateController = self.appStateController
  219. return controller
  220. }
  221. ), onDismiss: nil)
  222. }
  223. +++ Section(header: getAppVersion(), footer: "")
  224. +++ Section(header: "App Expiration", footer: String(expiration.description))
  225. showHideNSDetails()
  226. checkNightscoutStatus()
  227. }
  228. func getAppVersion() -> String {
  229. if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
  230. return "App Version: \(version)"
  231. }
  232. return "Version Unknown"
  233. }
  234. func updateStatusLabel(error: NightscoutUtils.NightscoutError?) {
  235. if let error = error {
  236. switch error {
  237. case .invalidURL:
  238. statusLabelRow.value = "Invalid URL"
  239. case .networkError:
  240. statusLabelRow.value = "Network Error"
  241. case .invalidToken:
  242. statusLabelRow.value = "Invalid Token"
  243. case .tokenRequired:
  244. statusLabelRow.value = "Token Required"
  245. case .siteNotFound:
  246. statusLabelRow.value = "Site Not Found"
  247. case .unknown:
  248. statusLabelRow.value = "Unknown Error"
  249. case .emptyAddress:
  250. statusLabelRow.value = "Address Empty"
  251. }
  252. } else {
  253. statusLabelRow.value = "OK"
  254. NotificationCenter.default.post(name: NSNotification.Name("refresh"), object: nil)
  255. }
  256. statusLabelRow.updateCell()
  257. }
  258. func checkNightscoutStatus() {
  259. NightscoutUtils.verifyURLAndToken(urlUser: UserDefaultsRepository.url.value, token: UserDefaultsRepository.token.value) { error in
  260. DispatchQueue.main.async {
  261. self.updateStatusLabel(error: error)
  262. }
  263. }
  264. }
  265. }