FreeAPSApp.swift 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. if #available(iOS 16.2, *) {
  44. _ = resolver.resolve(LiveActivityBridge.self)!
  45. }
  46. }
  47. init() {
  48. debug(
  49. .default,
  50. "iAPS Started: v\(Bundle.main.releaseVersionNumber ?? "")(\(Bundle.main.buildVersionNumber ?? "")) [buildDate: \(Bundle.main.buildDate)] [buildExpires: \(Bundle.main.profileExpiration)]"
  51. )
  52. loadServices()
  53. // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
  54. cleanupOldData()
  55. }
  56. var body: some Scene {
  57. WindowGroup {
  58. Main.RootView(resolver: resolver)
  59. .environment(\.managedObjectContext, coreDataStack.persistentContainer.viewContext)
  60. .environmentObject(Icons())
  61. .onOpenURL(perform: handleURL)
  62. }
  63. .onChange(of: scenePhase) { newScenePhase in
  64. debug(.default, "APPLICATION PHASE: \(newScenePhase)")
  65. /// If the App goes to the background we should ensure that all the changes are saved from the viewContext to the Persistent Container
  66. if newScenePhase == .background {
  67. coreDataStack.save()
  68. }
  69. }
  70. .backgroundTask(.appRefresh("com.openiaps.cleanup")) {
  71. await scheduleDatabaseCleaning()
  72. await cleanupOldData()
  73. }
  74. }
  75. func scheduleDatabaseCleaning() {
  76. let request = BGAppRefreshTaskRequest(identifier: "com.openiaps.cleanup")
  77. request.earliestBeginDate = .now.addingTimeInterval(7 * 24 * 60 * 60) // 7 days
  78. do {
  79. try BGTaskScheduler.shared.submit(request)
  80. debugPrint("Task scheduled successfully")
  81. } catch {
  82. debugPrint("Failed to schedule tasks")
  83. }
  84. }
  85. private func cleanupOldData() {
  86. Task {
  87. await coreDataStack.cleanupPersistentHistoryTokens(before: Date.oneWeekAgo)
  88. try await purgeOldNSManagedObjects()
  89. }
  90. }
  91. private func purgeOldNSManagedObjects() async throws {
  92. try await coreDataStack.batchDeleteOlderThan(GlucoseStored.self, dateKey: "date", days: 90)
  93. try await coreDataStack.batchDeleteOlderThan(PumpEventStored.self, dateKey: "timestamp", days: 90)
  94. try await coreDataStack.batchDeleteOlderThan(
  95. parentType: PumpEventStored.self,
  96. childType: BolusStored.self,
  97. dateKey: "timestamp",
  98. days: 90,
  99. relationshipKey: "pumpEvent"
  100. )
  101. try await coreDataStack.batchDeleteOlderThan(
  102. parentType: PumpEventStored.self,
  103. childType: TempBasalStored.self,
  104. dateKey: "timestamp",
  105. days: 90,
  106. relationshipKey: "pumpEvent"
  107. )
  108. try await coreDataStack.batchDeleteOlderThan(OrefDetermination.self, dateKey: "deliverAt", days: 90)
  109. try await coreDataStack.batchDeleteOlderThan(OpenAPS_Battery.self, dateKey: "date", days: 90)
  110. try await coreDataStack.batchDeleteOlderThan(CarbEntryStored.self, dateKey: "date", days: 90)
  111. try await coreDataStack.batchDeleteOlderThan(Forecast.self, dateKey: "date", days: 2)
  112. try await coreDataStack.batchDeleteOlderThan(
  113. parentType: Forecast.self,
  114. childType: ForecastValue.self,
  115. dateKey: "date",
  116. days: 2,
  117. relationshipKey: "forecast"
  118. )
  119. try await coreDataStack.batchDeleteOlderThan(OverrideStored.self, dateKey: "date", days: 3)
  120. try await coreDataStack.batchDeleteOlderThan(OverrideRunStored.self, dateKey: "startDate", days: 3)
  121. // TODO: - Purge Data of other (future) entities as well
  122. }
  123. private func handleURL(_ url: URL) {
  124. let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
  125. switch components?.host {
  126. case "device-select-resp":
  127. resolver.resolve(NotificationCenter.self)!.post(name: .openFromGarminConnect, object: url)
  128. default: break
  129. }
  130. }
  131. }