NightScoutViewController.swift 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // LoopFollow
  2. // NightScoutViewController.swift
  3. import Combine
  4. import UIKit
  5. import WebKit
  6. class NightscoutViewController: UIViewController {
  7. var webView: WKWebView!
  8. private var cancellables = Set<AnyCancellable>()
  9. override func viewDidLoad() {
  10. super.viewDidLoad()
  11. view.backgroundColor = .systemBackground
  12. overrideUserInterfaceStyle = Storage.shared.appearanceMode.value.userInterfaceStyle
  13. // Create WKWebView programmatically
  14. let webConfiguration = WKWebViewConfiguration()
  15. webConfiguration.mediaTypesRequiringUserActionForPlayback = []
  16. webView = WKWebView(frame: .zero, configuration: webConfiguration)
  17. webView.translatesAutoresizingMaskIntoConstraints = false
  18. view.addSubview(webView)
  19. let safeArea = view.safeAreaLayoutGuide
  20. NSLayoutConstraint.activate([
  21. webView.topAnchor.constraint(equalTo: safeArea.topAnchor),
  22. webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  23. webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  24. webView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor),
  25. ])
  26. // Listen for appearance setting changes
  27. Storage.shared.appearanceMode.$value
  28. .receive(on: DispatchQueue.main)
  29. .sink { [weak self] mode in
  30. self?.overrideUserInterfaceStyle = mode.userInterfaceStyle
  31. }
  32. .store(in: &cancellables)
  33. var url = Storage.shared.url.value
  34. let token = Storage.shared.token.value
  35. if token != "" {
  36. url = url + "?token=" + token
  37. }
  38. guard let myUrl = URL(string: url) else { return }
  39. let webpagePreferences = WKWebpagePreferences()
  40. webpagePreferences.allowsContentJavaScript = true
  41. webView.configuration.defaultWebpagePreferences = webpagePreferences
  42. webView.navigationDelegate = self
  43. webView.uiDelegate = self
  44. webView.load(URLRequest(url: myUrl))
  45. let refreshControl = UIRefreshControl()
  46. refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
  47. webView.scrollView.addSubview(refreshControl)
  48. }
  49. @objc func reloadWebView(_ sender: UIRefreshControl) {
  50. clearWebCache()
  51. webView.reload()
  52. sender.endRefreshing()
  53. }
  54. func clearWebCache() {
  55. let dataStore = WKWebsiteDataStore.default()
  56. let cacheTypes = Set([WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache])
  57. let date = Date(timeIntervalSince1970: 0)
  58. dataStore.removeData(ofTypes: cacheTypes, modifiedSince: date) {
  59. print("Web cache cleared.")
  60. }
  61. }
  62. // this handles target=_blank links by opening them in the same view
  63. func webView(webView: WKWebView!, createWebViewWithConfiguration _: WKWebViewConfiguration!, forNavigationAction navigationAction: WKNavigationAction!, windowFeatures _: WKWindowFeatures!) -> WKWebView! {
  64. if let frame = navigationAction.targetFrame,
  65. frame.isMainFrame
  66. {
  67. return nil
  68. }
  69. // for _blank target or non-mainFrame target
  70. webView.load(navigationAction.request)
  71. return nil
  72. }
  73. }
  74. // MARK: - WKUIDelegate implementation
  75. extension NightscoutViewController: WKNavigationDelegate, WKUIDelegate {
  76. func webView(_: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame _: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
  77. let alertCtrl = UIAlertController(title: nil, message: message, preferredStyle: .alert)
  78. alertCtrl.addAction(UIAlertAction(title: String(localized: "OK"), style: .default) { _ in
  79. completionHandler(true)
  80. })
  81. alertCtrl.addAction(UIAlertAction(title: String(localized: "Cancel"), style: .cancel) { _ in
  82. completionHandler(false)
  83. })
  84. present(alertCtrl, animated: true)
  85. }
  86. func webView(_: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
  87. guard let _ = navigationAction.request.url else {
  88. decisionHandler(.cancel)
  89. return
  90. }
  91. decisionHandler(.allow)
  92. }
  93. func webView(_: WKWebView, shouldStartLoadWith request: URLRequest) -> Bool {
  94. guard let url = request.url else {
  95. return false
  96. }
  97. LogManager.shared.log(category: .nightscout, message: "Web shouldStart: \(LogRedactor.url(url.absoluteString))", isDebug: true)
  98. return true
  99. }
  100. func webView(_ webView: WKWebView, createWebViewWith _: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures _: WKWindowFeatures) -> WKWebView? {
  101. if navigationAction.targetFrame == nil {
  102. webView.load(navigationAction.request)
  103. }
  104. return nil
  105. }
  106. func webView(_ webView: WKWebView, didFinish _: WKNavigation!) {
  107. let javascript = """
  108. var meta = document.querySelector('meta[name="viewport"]');
  109. if (meta) {
  110. meta.setAttribute('content', 'width=device-width, initial-scale=0.9, maximum-scale=5.0, user-scalable=yes');
  111. }
  112. """
  113. webView.evaluateJavaScript(javascript)
  114. }
  115. }