FreeAPSApp.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import ActivityKit
  2. import BackgroundTasks
  3. import CoreData
  4. import Foundation
  5. import SwiftUI
  6. import Swinject
  7. @main struct FreeAPSApp: App {
  8. @Environment(\.scenePhase) var scenePhase
  9. @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
  10. let coreDataStack = CoreDataStack.shared
  11. // Dependencies Assembler
  12. // contain all dependencies Assemblies
  13. // TODO: Remove static key after update "Use Dependencies" logic
  14. private static var assembler = Assembler([
  15. StorageAssembly(),
  16. ServiceAssembly(),
  17. APSAssembly(),
  18. NetworkAssembly(),
  19. UIAssembly(),
  20. SecurityAssembly()
  21. ], parent: nil, defaultObjectScope: .container)
  22. var resolver: Resolver {
  23. FreeAPSApp.assembler.resolver
  24. }
  25. // Temp static var
  26. // Use to backward compatibility with old Dependencies logic on Logger
  27. // TODO: Remove var after update "Use Dependencies" logic in Logger
  28. static var resolver: Resolver {
  29. FreeAPSApp.assembler.resolver
  30. }
  31. private func loadServices() {
  32. resolver.resolve(AppearanceManager.self)!.setupGlobalAppearance()
  33. _ = resolver.resolve(DeviceDataManager.self)!
  34. _ = resolver.resolve(APSManager.self)!
  35. _ = resolver.resolve(FetchGlucoseManager.self)!
  36. _ = resolver.resolve(FetchTreatmentsManager.self)!
  37. _ = resolver.resolve(FetchAnnouncementsManager.self)!
  38. _ = resolver.resolve(CalendarManager.self)!
  39. _ = resolver.resolve(UserNotificationsManager.self)!
  40. _ = resolver.resolve(WatchManager.self)!
  41. _ = resolver.resolve(HealthKitManager.self)!
  42. _ = resolver.resolve(BluetoothStateManager.self)!
  43. _ = resolver.resolve(PluginManager.self)!
  44. if #available(iOS 16.2, *) {
  45. _ = resolver.resolve(LiveActivityBridge.self)!
  46. }
  47. }
  48. init() {
  49. debug(
  50. .default,
  51. "Trio Started: v\(Bundle.main.releaseVersionNumber ?? "")(\(Bundle.main.buildVersionNumber ?? "")) [buildDate: \(BuildDetails.default.buildDate())] [buildExpires: \(BuildDetails.default.calculateExpirationDate())]"
  52. )
  53. loadServices()
  54. // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
  55. cleanupOldData()
  56. }
  57. var body: some Scene {
  58. WindowGroup {
  59. Main.RootView(resolver: resolver)
  60. .environment(\.managedObjectContext, coreDataStack.persistentContainer.viewContext)
  61. .environmentObject(Icons())
  62. .onOpenURL(perform: handleURL)
  63. }
  64. .onChange(of: scenePhase) { newScenePhase in
  65. debug(.default, "APPLICATION PHASE: \(newScenePhase)")
  66. /// If the App goes to the background we should ensure that all the changes are saved from the viewContext to the Persistent Container
  67. if newScenePhase == .background {
  68. coreDataStack.save()
  69. }
  70. }
  71. .backgroundTask(.appRefresh("com.openiaps.cleanup")) {
  72. await scheduleDatabaseCleaning()
  73. await cleanupOldData()
  74. }
  75. }
  76. func scheduleDatabaseCleaning() {
  77. let request = BGAppRefreshTaskRequest(identifier: "com.openiaps.cleanup")
  78. request.earliestBeginDate = .now.addingTimeInterval(7 * 24 * 60 * 60) // 7 days
  79. do {
  80. try BGTaskScheduler.shared.submit(request)
  81. debugPrint("Task scheduled successfully")
  82. } catch {
  83. debugPrint("Failed to schedule tasks")
  84. }
  85. }
  86. private func cleanupOldData() {
  87. Task {
  88. async let cleanupTokens: () = coreDataStack.cleanupPersistentHistoryTokens(before: Date.oneWeekAgo)
  89. async let purgeData: () = purgeOldNSManagedObjects()
  90. await cleanupTokens
  91. try await purgeData
  92. }
  93. }
  94. private func purgeOldNSManagedObjects() async throws {
  95. async let glucoseDeletion: () = coreDataStack.batchDeleteOlderThan(GlucoseStored.self, dateKey: "date", days: 90)
  96. async let pumpEventDeletion: () = coreDataStack.batchDeleteOlderThan(PumpEventStored.self, dateKey: "timestamp", days: 90)
  97. async let bolusDeletion: () = coreDataStack.batchDeleteOlderThan(
  98. parentType: PumpEventStored.self,
  99. childType: BolusStored.self,
  100. dateKey: "timestamp",
  101. days: 90,
  102. relationshipKey: "pumpEvent"
  103. )
  104. async let tempBasalDeletion: () = coreDataStack.batchDeleteOlderThan(
  105. parentType: PumpEventStored.self,
  106. childType: TempBasalStored.self,
  107. dateKey: "timestamp",
  108. days: 90,
  109. relationshipKey: "pumpEvent"
  110. )
  111. async let determinationDeletion: () = coreDataStack
  112. .batchDeleteOlderThan(OrefDetermination.self, dateKey: "deliverAt", days: 90)
  113. async let batteryDeletion: () = coreDataStack.batchDeleteOlderThan(OpenAPS_Battery.self, dateKey: "date", days: 90)
  114. async let carbEntryDeletion: () = coreDataStack.batchDeleteOlderThan(CarbEntryStored.self, dateKey: "date", days: 90)
  115. async let forecastDeletion: () = coreDataStack.batchDeleteOlderThan(Forecast.self, dateKey: "date", days: 2)
  116. async let forecastValueDeletion: () = coreDataStack.batchDeleteOlderThan(
  117. parentType: Forecast.self,
  118. childType: ForecastValue.self,
  119. dateKey: "date",
  120. days: 2,
  121. relationshipKey: "forecast"
  122. )
  123. async let overrideDeletion: () = coreDataStack
  124. .batchDeleteOlderThan(OverrideStored.self, dateKey: "date", days: 3, isPresetKey: "isPreset")
  125. async let overrideRunDeletion: () = coreDataStack
  126. .batchDeleteOlderThan(OverrideRunStored.self, dateKey: "startDate", days: 3)
  127. // Await each task to ensure they are all completed
  128. try await glucoseDeletion
  129. try await pumpEventDeletion
  130. try await bolusDeletion
  131. try await tempBasalDeletion
  132. try await determinationDeletion
  133. try await batteryDeletion
  134. try await carbEntryDeletion
  135. try await forecastDeletion
  136. try await forecastValueDeletion
  137. try await overrideDeletion
  138. try await overrideRunDeletion
  139. }
  140. private func handleURL(_ url: URL) {
  141. let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
  142. switch components?.host {
  143. case "device-select-resp":
  144. resolver.resolve(NotificationCenter.self)!.post(name: .openFromGarminConnect, object: url)
  145. default: break
  146. }
  147. }
  148. }