OverridePresetsIntentRequest.swift 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import CoreData
  2. import Foundation
  3. @available(iOS 16.0, *) final class OverridePresetsIntentRequest: BaseIntentsRequest {
  4. enum overridePresetsError: Error {
  5. case noTempOverrideFound
  6. case noDurationDefined
  7. case noActiveOverride
  8. }
  9. func fetchAndProcessOverrides() async -> [OverridePreset] {
  10. // Fetch all Override Presets via OverrideStorage
  11. let allOverridePresetsIDs = await overrideStorage.fetchForOverridePresets()
  12. // Since we are fetching on a different background Thread we need to unpack the NSManagedObjectID on the correct Thread first
  13. return await coredataContext.perform {
  14. do {
  15. let overrideObjects = try allOverridePresetsIDs.compactMap { id in
  16. try self.coredataContext.existingObject(with: id) as? OverrideStored
  17. }
  18. return overrideObjects.map { object in
  19. guard let id = object.id,
  20. let name = object.name else { return OverridePreset(id: UUID().uuidString, name: "") }
  21. return OverridePreset(id: id, name: name)
  22. }
  23. } catch {
  24. debugPrint(
  25. "\(#file) \(#function) \(DebuggingIdentifiers.failed) error while fetching/ processing the overrides Array: \(error.localizedDescription)"
  26. )
  27. return [OverridePreset(id: UUID().uuidString, name: "")]
  28. }
  29. }
  30. }
  31. func fetchIDs(_ uuid: [OverridePreset.ID]) async -> [OverridePreset] {
  32. await coredataContext.perform {
  33. let fetchRequest: NSFetchRequest<OverrideStored> = OverrideStored.fetchRequest()
  34. fetchRequest.predicate = NSPredicate(format: "id IN %@", uuid)
  35. do {
  36. let result = try self.coredataContext.fetch(fetchRequest)
  37. if result.isEmpty {
  38. debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) No OverrideStored found for ids: \(uuid)")
  39. return [OverridePreset(id: UUID().uuidString, name: "")]
  40. }
  41. return result.map { overrideStored in
  42. OverridePreset(id: overrideStored.id ?? UUID().uuidString, name: overrideStored.name ?? "")
  43. }
  44. } catch let error as NSError {
  45. debugPrint(
  46. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch Override: \(error.localizedDescription)"
  47. )
  48. return [OverridePreset(id: UUID().uuidString, name: "")]
  49. }
  50. }
  51. }
  52. private func fetchOverrideID(_ preset: OverridePreset) async -> NSManagedObjectID? {
  53. let fetchRequest: NSFetchRequest<OverrideStored> = OverrideStored.fetchRequest()
  54. fetchRequest.predicate = NSPredicate(format: "id == %@", preset.id)
  55. fetchRequest.fetchLimit = 1
  56. return await coredataContext.perform {
  57. do {
  58. return try self.coredataContext.fetch(fetchRequest).first?.objectID
  59. } catch {
  60. debugPrint(
  61. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch Override: \(error.localizedDescription)"
  62. )
  63. return nil
  64. }
  65. }
  66. }
  67. @MainActor func enactOverride(_ preset: OverridePreset) async -> Bool {
  68. do {
  69. guard let overrideID = await fetchOverrideID(preset),
  70. let overrideObject = try viewContext.existingObject(with: overrideID) as? OverrideStored else { return false }
  71. overrideObject.enabled = true
  72. overrideObject.date = Date()
  73. overrideObject.isUploadedToNS = false
  74. // Disable previous Overrides
  75. /// do not create a OverrideRunEntry because we only want that if we cancel a running Override, not when enacting a Preset
  76. await disableAllActiveOverrides(except: overrideID, createOverrideRunEntry: true)
  77. if viewContext.hasChanges {
  78. try viewContext.save()
  79. // Update State variables in OverrideView
  80. Foundation.NotificationCenter.default.post(name: .didUpdateOverrideConfiguration, object: nil)
  81. return true
  82. }
  83. } catch let error as NSError {
  84. debugPrint(
  85. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to enact Override: \(error.localizedDescription)"
  86. )
  87. }
  88. return false
  89. }
  90. func cancelOverride() async {
  91. await disableAllActiveOverrides(createOverrideRunEntry: true)
  92. }
  93. @MainActor func disableAllActiveOverrides(
  94. except overrideID: NSManagedObjectID? = nil,
  95. createOverrideRunEntry _: Bool
  96. ) async {
  97. // Get ALL NSManagedObject IDs of ALL active Overrides to cancel every single Override
  98. let ids = await overrideStorage.loadLatestOverrideConfigurations(fetchLimit: 0) // 0 = no fetch limit
  99. await viewContext.perform {
  100. do {
  101. // Fetch the existing OverrideStored objects from the context
  102. let results = try ids.compactMap { id in
  103. try self.viewContext.existingObject(with: id) as? OverrideStored
  104. }
  105. // If there are no results, return early
  106. guard !results.isEmpty else { return }
  107. // Check if we also need to create a corresponding OverrideRunStored entry, i.e. when the User uses the Cancel Button in Override View
  108. // Auggie - commented out this if statment, we always need to do this for overrides
  109. // if createOverrideRunEntry {
  110. // Use the first override to create a new OverrideRunStored entry
  111. if let canceledOverride = results.first {
  112. let newOverrideRunStored = OverrideRunStored(context: self.viewContext)
  113. newOverrideRunStored.id = UUID()
  114. newOverrideRunStored.name = canceledOverride.name
  115. newOverrideRunStored.startDate = canceledOverride.date ?? .distantPast
  116. newOverrideRunStored.endDate = Date()
  117. newOverrideRunStored
  118. .target = NSDecimalNumber(
  119. decimal: self.overrideStorage
  120. .calculateTarget(override: canceledOverride)
  121. )
  122. newOverrideRunStored.override = canceledOverride
  123. newOverrideRunStored.isUploadedToNS = false
  124. }
  125. // }
  126. // Disable all override except the one with overrideID
  127. for overrideToCancel in results {
  128. if overrideToCancel.objectID != overrideID {
  129. overrideToCancel.enabled = false
  130. overrideToCancel.isUploadedToNS = false
  131. }
  132. }
  133. // Save the context if there are changes
  134. if self.viewContext.hasChanges {
  135. try self.viewContext.save()
  136. // Update State variables in OverrideView
  137. Foundation.NotificationCenter.default.post(name: .didUpdateOverrideConfiguration, object: nil)
  138. }
  139. } catch {
  140. debugPrint(
  141. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to disable active Overrides with error: \(error.localizedDescription)"
  142. )
  143. }
  144. }
  145. }
  146. }