فهرست منبع

Fix memory issues on iOS 15

Ivan Valkou 4 سال پیش
والد
کامیت
d4483a7e5e

+ 1 - 3
FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootViewModel.swift

@@ -5,8 +5,6 @@ extension AuthorizedRoot {
     class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AuthorizedRootProvider {
     class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AuthorizedRootProvider {
         override func subscribe() {}
         override func subscribe() {}
 
 
-        var rootView: some View {
-            router.view(for: .home)
-        }
+        lazy var rootView: some View = { router.view(for: .home) }()
     }
     }
 }
 }

+ 18 - 0
FreeAPS/Sources/Modules/Base/BaseViewModel.swift

@@ -5,8 +5,10 @@ import Swinject
 protocol ViewModel {
 protocol ViewModel {
     func subscribe()
     func subscribe()
     func view(for screen: Screen) -> AnyView
     func view(for screen: Screen) -> AnyView
+    func cachedView(for screen: Screen) -> AnyView
     func showModal(for screen: Screen?)
     func showModal(for screen: Screen?)
     func hideModal()
     func hideModal()
+    func cleanViewCache()
 }
 }
 
 
 class BaseViewModel<Provider>: ViewModel, Injectable where Provider: FreeAPS.Provider {
 class BaseViewModel<Provider>: ViewModel, Injectable where Provider: FreeAPS.Provider {
@@ -15,6 +17,8 @@ class BaseViewModel<Provider>: ViewModel, Injectable where Provider: FreeAPS.Pro
     var lifetime = Lifetime()
     var lifetime = Lifetime()
     @Injected() var router: Router!
     @Injected() var router: Router!
 
 
+    private var viewCache: [Screen: AnyView] = [:]
+
     required init(provider: Provider, resolver: Resolver) {
     required init(provider: Provider, resolver: Resolver) {
         self.provider = provider
         self.provider = provider
         self.resolver = resolver
         self.resolver = resolver
@@ -28,6 +32,20 @@ class BaseViewModel<Provider>: ViewModel, Injectable where Provider: FreeAPS.Pro
         router.view(for: screen)
         router.view(for: screen)
     }
     }
 
 
+    func cachedView(for screen: Screen) -> AnyView {
+        if let view = viewCache[screen] {
+            return view
+        }
+
+        let view = view(for: screen)
+        viewCache[screen] = view
+        return view
+    }
+
+    func cleanViewCache() {
+        viewCache.removeAll()
+    }
+
     func showModal(for screen: Screen?) {
     func showModal(for screen: Screen?) {
         router.mainModalScreen.send(screen)
         router.mainModalScreen.send(screen)
     }
     }

+ 25 - 15
FreeAPS/Sources/Modules/Home/HomeViewModel.swift

@@ -81,9 +81,9 @@ extension Home {
             broadcaster.register(PumpReservoirObserver.self, observer: self)
             broadcaster.register(PumpReservoirObserver.self, observer: self)
 
 
             timer.eventHandler = {
             timer.eventHandler = {
-                DispatchQueue.main.async {
-                    self.timerDate = Date()
-                    self.setupCurrentTempTarget()
+                DispatchQueue.main.async { [weak self] in
+                    self?.timerDate = Date()
+                    self?.setupCurrentTempTarget()
                 }
                 }
             }
             }
             timer.resume()
             timer.resume()
@@ -110,8 +110,8 @@ extension Home {
 
 
             apsManager.lastError
             apsManager.lastError
                 .receive(on: DispatchQueue.main)
                 .receive(on: DispatchQueue.main)
-                .map { error in
-                    self.errorDate = error == nil ? nil : Date()
+                .map { [weak self] error in
+                    self?.errorDate = error == nil ? nil : Date()
                     return error?.localizedDescription
                     return error?.localizedDescription
                 }
                 }
                 .weakAssign(to: \.errorMessage, on: self)
                 .weakAssign(to: \.errorMessage, on: self)
@@ -136,7 +136,8 @@ extension Home {
         }
         }
 
 
         private func setupGlucose() {
         private func setupGlucose() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.glucose = self.provider.filteredGlucose(hours: self.filteredHours)
                 self.glucose = self.provider.filteredGlucose(hours: self.filteredHours)
                 self.recentGlucose = self.glucose.last
                 self.recentGlucose = self.glucose.last
                 if self.glucose.count >= 2 {
                 if self.glucose.count >= 2 {
@@ -148,7 +149,8 @@ extension Home {
         }
         }
 
 
         private func setupBasals() {
         private func setupBasals() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.tempBasals = self.provider.pumpHistory(hours: self.filteredHours).filter {
                 self.tempBasals = self.provider.pumpHistory(hours: self.filteredHours).filter {
                     $0.type == .tempBasal || $0.type == .tempBasalDuration
                     $0.type == .tempBasal || $0.type == .tempBasalDuration
                 }
                 }
@@ -172,7 +174,8 @@ extension Home {
         }
         }
 
 
         private func setupBoluses() {
         private func setupBoluses() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.boluses = self.provider.pumpHistory(hours: self.filteredHours).filter {
                 self.boluses = self.provider.pumpHistory(hours: self.filteredHours).filter {
                     $0.type == .bolus
                     $0.type == .bolus
                 }
                 }
@@ -180,7 +183,8 @@ extension Home {
         }
         }
 
 
         private func setupSuspensions() {
         private func setupSuspensions() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.suspensions = self.provider.pumpHistory(hours: self.filteredHours).filter {
                 self.suspensions = self.provider.pumpHistory(hours: self.filteredHours).filter {
                     $0.type == .pumpSuspend || $0.type == .pumpResume
                     $0.type == .pumpSuspend || $0.type == .pumpResume
                 }
                 }
@@ -193,26 +197,30 @@ extension Home {
         }
         }
 
 
         private func setupPumpSettings() {
         private func setupPumpSettings() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.maxBasal = self.provider.pumpSettings().maxBasal
                 self.maxBasal = self.provider.pumpSettings().maxBasal
             }
             }
         }
         }
 
 
         private func setupBasalProfile() {
         private func setupBasalProfile() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.autotunedBasalProfile = self.provider.autotunedBasalProfile()
                 self.autotunedBasalProfile = self.provider.autotunedBasalProfile()
                 self.basalProfile = self.provider.basalProfile()
                 self.basalProfile = self.provider.basalProfile()
             }
             }
         }
         }
 
 
         private func setupTempTargets() {
         private func setupTempTargets() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.tempTargets = self.provider.tempTargets(hours: self.filteredHours)
                 self.tempTargets = self.provider.tempTargets(hours: self.filteredHours)
             }
             }
         }
         }
 
 
         private func setupCarbs() {
         private func setupCarbs() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.carbs = self.provider.carbs(hours: self.filteredHours)
                 self.carbs = self.provider.carbs(hours: self.filteredHours)
             }
             }
         }
         }
@@ -241,13 +249,15 @@ extension Home {
         }
         }
 
 
         private func setupReservoir() {
         private func setupReservoir() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.reservoir = self.provider.pumpReservoir()
                 self.reservoir = self.provider.pumpReservoir()
             }
             }
         }
         }
 
 
         private func setupBattery() {
         private func setupBattery() {
-            DispatchQueue.main.async {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
                 self.battery = self.provider.pumpBattery()
                 self.battery = self.provider.pumpBattery()
             }
             }
         }
         }

+ 5 - 1
FreeAPS/Sources/Modules/Main/View/MainRootView.swift

@@ -4,8 +4,12 @@ extension Main {
     struct RootView: BaseView {
     struct RootView: BaseView {
         @EnvironmentObject var viewModel: ViewModel<Provider>
         @EnvironmentObject var viewModel: ViewModel<Provider>
 
 
+        @ViewBuilder func presentedView() -> some View {
+            viewModel.cachedView(for: viewModel.scene.screen)
+        }
+
         var body: some View {
         var body: some View {
-            viewModel.view(for: viewModel.scene.screen)
+            presentedView()
                 .sheet(isPresented: $viewModel.isModalPresented) {
                 .sheet(isPresented: $viewModel.isModalPresented) {
                     NavigationView { self.viewModel.modal!.view }
                     NavigationView { self.viewModel.modal!.view }
                         .navigationViewStyle(StackNavigationViewStyle())
                         .navigationViewStyle(StackNavigationViewStyle())

+ 1 - 1
FreeAPS/Sources/Router/Screen.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 import SwiftUI
 import Swinject
 import Swinject
 
 
-enum Screen: Identifiable {
+enum Screen: Identifiable, Hashable {
     case loading
     case loading
     case home
     case home
     case settings
     case settings