|
|
@@ -1,5 +1,6 @@
|
|
|
import CoreData
|
|
|
import Foundation
|
|
|
+import UIKit
|
|
|
|
|
|
@available(iOS 16.0, *) final class TempPresetsIntentRequest: BaseIntentsRequest {
|
|
|
enum TempPresetsError: Error {
|
|
|
@@ -7,95 +8,228 @@ import Foundation
|
|
|
case noDurationDefined
|
|
|
}
|
|
|
|
|
|
- private func convert(tt: [TempTarget]) -> [tempPreset] {
|
|
|
- tt.map { tempPreset.convert($0) }
|
|
|
- }
|
|
|
+ func fetchAndProcessTempTargets() async -> [TempPreset] {
|
|
|
+ // Fetch all Temp Target Presets via TempTargetStorage
|
|
|
+ let allTempTargetPresetsIDs = await tempTargetsStorage.fetchForTempTargetPresets()
|
|
|
|
|
|
- func fetchAll() -> [tempPreset] {
|
|
|
- convert(tt: tempTargetsStorage.presets())
|
|
|
- }
|
|
|
+ // Perform the fetch and process on the Core Data context's thread
|
|
|
+ return await coredataContext.perform {
|
|
|
+ // Fetch existing TempTargetStored objects based on their NSManagedObjectIDs
|
|
|
+ let tempTargetObjects: [TempTargetStored] = allTempTargetPresetsIDs.compactMap { id in
|
|
|
+ guard let object = try? self.coredataContext.existingObject(with: id) as? TempTargetStored else {
|
|
|
+ debugPrint("\(#file) \(#function) Failed to fetch object for ID: \(id)")
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return object
|
|
|
+ }
|
|
|
|
|
|
- func fetchIDs(_ uuid: [tempPreset.ID]) -> [tempPreset] {
|
|
|
- let UUIDTempTarget = tempTargetsStorage.presets().filter { uuid.contains(UUID(uuidString: $0.id)!) }
|
|
|
- return convert(tt: UUIDTempTarget)
|
|
|
+ // Map fetched TempTargetStored objects to TempPreset
|
|
|
+ return tempTargetObjects.compactMap { object in
|
|
|
+ guard let id = object.id,
|
|
|
+ let name = object.name,
|
|
|
+ let target = object.target?.decimalValue,
|
|
|
+ let duration = object.duration?.decimalValue
|
|
|
+ else {
|
|
|
+ debugPrint("\(#file) \(#function) Missing data for TempTargetStored object.")
|
|
|
+ return TempPreset(id: UUID(), name: "", duration: 0)
|
|
|
+ }
|
|
|
+ return TempPreset(id: id, name: name, targetTop: target, duration: duration)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- func fetchOne(_ uuid: tempPreset.ID) -> tempPreset? {
|
|
|
- let UUIDTempTarget = tempTargetsStorage.presets().filter { UUID(uuidString: $0.id) == uuid }
|
|
|
- guard let OneTempTarget = UUIDTempTarget.first else { return nil }
|
|
|
- return tempPreset.convert(OneTempTarget)
|
|
|
+ func fetchIDs(_ uuid: [TempPreset.ID]) async -> [TempPreset] {
|
|
|
+ await coredataContext.perform {
|
|
|
+ let fetchRequest: NSFetchRequest<TempTargetStored> = TempTargetStored.fetchRequest()
|
|
|
+ fetchRequest.predicate = NSPredicate(format: "id IN %@", uuid)
|
|
|
+
|
|
|
+ do {
|
|
|
+ let result = try self.coredataContext.fetch(fetchRequest)
|
|
|
+
|
|
|
+ if result.isEmpty {
|
|
|
+ debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) No TempTargetStored found for ids: \(uuid)")
|
|
|
+ return [TempPreset(id: UUID(), name: "", duration: 0)]
|
|
|
+ }
|
|
|
+
|
|
|
+ return result.map { tempTargetStored in
|
|
|
+ TempPreset(
|
|
|
+ id: tempTargetStored.id ?? UUID(),
|
|
|
+ name: tempTargetStored.name ?? "",
|
|
|
+ duration: tempTargetStored.duration as? Decimal ?? 0
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } catch let error as NSError {
|
|
|
+ debugPrint(
|
|
|
+ "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch TempTarget: \(error.localizedDescription)"
|
|
|
+ )
|
|
|
+ return [TempPreset(id: UUID(), name: "", duration: 0)]
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- func findTempTarget(_ tempPreset: tempPreset) throws -> TempTarget {
|
|
|
- let tempTargetFound = tempTargetsStorage.presets().filter { $0.id == tempPreset.id.uuidString }
|
|
|
- guard let tempOneTarget = tempTargetFound.first else { throw TempPresetsError.noTempTargetFound }
|
|
|
- return tempOneTarget
|
|
|
+ private func fetchTempTargetID(_ preset: TempPreset) async -> NSManagedObjectID? {
|
|
|
+ let fetchRequest: NSFetchRequest<TempTargetStored> = TempTargetStored.fetchRequest()
|
|
|
+ fetchRequest.predicate = NSPredicate(format: "id == %@", preset.id.uuidString)
|
|
|
+ fetchRequest.fetchLimit = 1
|
|
|
+
|
|
|
+ return await coredataContext.perform {
|
|
|
+ do {
|
|
|
+ return try self.coredataContext.fetch(fetchRequest).first?.objectID
|
|
|
+ } catch {
|
|
|
+ debugPrint(
|
|
|
+ "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch Override: \(error.localizedDescription)"
|
|
|
+ )
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // TODO: - probably broken for now...
|
|
|
+ @MainActor func enactTempTarget(_ preset: TempPreset) async -> Bool {
|
|
|
+ // Start background task
|
|
|
+ var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
|
|
|
+ backgroundTaskID = await UIApplication.shared.beginBackgroundTask(withName: "Override Upload") {
|
|
|
+ guard backgroundTaskID != .invalid else { return }
|
|
|
+ Task {
|
|
|
+ // End background task when the time is about to expire
|
|
|
+ await UIApplication.shared.endBackgroundTask(backgroundTaskID)
|
|
|
+ }
|
|
|
+ backgroundTaskID = .invalid
|
|
|
+ }
|
|
|
+
|
|
|
+ // Defer block to end background task when function exits
|
|
|
+ defer {
|
|
|
+ if backgroundTaskID != .invalid {
|
|
|
+ Task {
|
|
|
+ await UIApplication.shared.endBackgroundTask(backgroundTaskID)
|
|
|
+ }
|
|
|
+ backgroundTaskID = .invalid
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ do {
|
|
|
+ // Get NSManagedObjectID of Preset
|
|
|
+ guard let tempTargetID = await fetchTempTargetID(preset),
|
|
|
+ let tempTargetObject = try viewContext.existingObject(with: tempTargetID) as? TempTargetStored
|
|
|
+ else { return false }
|
|
|
|
|
|
- func enactTempTarget(_ presetTarget: TempTarget) async throws -> TempTarget {
|
|
|
- var tempTarget = presetTarget
|
|
|
- tempTarget.createdAt = Date()
|
|
|
- await storage.storeTempTarget(tempTarget: tempTarget)
|
|
|
+ // Enable TempTarget
|
|
|
+ tempTargetObject.enabled = true
|
|
|
+ tempTargetObject.date = Date()
|
|
|
+ tempTargetObject.isUploadedToNS = false
|
|
|
|
|
|
- coredataContext.performAndWait {
|
|
|
- var tempTargetsArray = [TempTargetStored]()
|
|
|
- let requestTempTargets = TempTargetStored.fetchRequest() as NSFetchRequest<TempTargetStored>
|
|
|
- let sortTT = NSSortDescriptor(key: "date", ascending: false)
|
|
|
- requestTempTargets.sortDescriptors = [sortTT]
|
|
|
- if coredataContext.hasChanges {
|
|
|
- try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
|
|
|
+ // Disable previous overrides if necessary, without starting a background task
|
|
|
+ await disableAllActiveTempTargets(
|
|
|
+ except: tempTargetID,
|
|
|
+ createTempTargetRunEntry: true,
|
|
|
+ shouldStartBackgroundTask: false
|
|
|
+ )
|
|
|
+
|
|
|
+ if viewContext.hasChanges {
|
|
|
+ try viewContext.save()
|
|
|
+
|
|
|
+ // Update State variables in TempTargetView
|
|
|
+ Foundation.NotificationCenter.default.post(name: .willUpdateTempTargetConfiguration, object: nil)
|
|
|
+
|
|
|
+ // Await the notification
|
|
|
+ print("Waiting for notification...")
|
|
|
+ await awaitNotification(.didUpdateTempTargetConfiguration)
|
|
|
+ print("Notification received, continuing...")
|
|
|
+
|
|
|
+ return true
|
|
|
}
|
|
|
+ } catch {
|
|
|
+ // Handle error and ensure background task is ended
|
|
|
+ debugPrint("Failed to enact TempTarget: \(error.localizedDescription)")
|
|
|
+ }
|
|
|
+
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ func cancelTempTarget() async {
|
|
|
+ await disableAllActiveTempTargets(createTempTargetRunEntry: true, shouldStartBackgroundTask: true)
|
|
|
+ }
|
|
|
|
|
|
- let whichID = tempTargetsArray.first(where: { $0.id == UUID(uuidString: tempTarget.id) })
|
|
|
-
|
|
|
- if whichID != nil {
|
|
|
- let saveToCoreData = TempTargetStored(context: self.coredataContext)
|
|
|
- saveToCoreData.enabled = true
|
|
|
- saveToCoreData.date = Date()
|
|
|
- saveToCoreData.target = whichID?.target ?? 160
|
|
|
- saveToCoreData.date = Date()
|
|
|
- saveToCoreData.duration = whichID?.duration ?? 0
|
|
|
-
|
|
|
- do {
|
|
|
- guard coredataContext.hasChanges else { return }
|
|
|
- try self.coredataContext.save()
|
|
|
- } catch {
|
|
|
- print(error.localizedDescription)
|
|
|
+ @MainActor func disableAllActiveTempTargets(
|
|
|
+ except tempTargetID: NSManagedObjectID? = nil,
|
|
|
+ createTempTargetRunEntry: Bool,
|
|
|
+ shouldStartBackgroundTask: Bool = true
|
|
|
+ ) async {
|
|
|
+ var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
|
|
|
+
|
|
|
+ if shouldStartBackgroundTask {
|
|
|
+ // Start background task
|
|
|
+ backgroundTaskID = await UIApplication.shared.beginBackgroundTask(withName: "TempTarget Cancel") {
|
|
|
+ guard backgroundTaskID != .invalid else { return }
|
|
|
+ Task {
|
|
|
+ // End background task when the time is about to expire
|
|
|
+ await UIApplication.shared.endBackgroundTask(backgroundTaskID)
|
|
|
}
|
|
|
- } else {
|
|
|
- let saveToCoreData = TempTargetStored(context: self.coredataContext)
|
|
|
- saveToCoreData.enabled = false
|
|
|
- saveToCoreData.date = Date()
|
|
|
- do {
|
|
|
- guard coredataContext.hasChanges else { return }
|
|
|
- try self.coredataContext.save()
|
|
|
- } catch {
|
|
|
- print(error.localizedDescription)
|
|
|
+ backgroundTaskID = .invalid
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Defer block to end background task when function exits, only if it was started
|
|
|
+ defer {
|
|
|
+ if shouldStartBackgroundTask, backgroundTaskID != .invalid {
|
|
|
+ Task {
|
|
|
+ await UIApplication.shared.endBackgroundTask(backgroundTaskID)
|
|
|
}
|
|
|
+ backgroundTaskID = .invalid
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return tempTarget
|
|
|
- }
|
|
|
+ // Get NSManagedObjectID of all active temp Targets
|
|
|
+ let ids = await tempTargetsStorage.loadLatestTempTargetConfigurations(fetchLimit: 0)
|
|
|
+
|
|
|
+ do {
|
|
|
+ // Fetch existing OverrideStored objects
|
|
|
+ let results = try ids.compactMap { id in
|
|
|
+ try self.viewContext.existingObject(with: id) as? TempTargetStored
|
|
|
+ }
|
|
|
+
|
|
|
+ // Return early if no results
|
|
|
+ guard !results.isEmpty else { return }
|
|
|
+
|
|
|
+ // Create TempTargetRunStored entry if needed
|
|
|
+ if createTempTargetRunEntry {
|
|
|
+ // Use the first temp target to create a new TempTargetRunStored entry
|
|
|
+ if let canceledTempTarget = results.first {
|
|
|
+ let newTempTargetRunStored = TempTargetRunStored(context: viewContext)
|
|
|
+ newTempTargetRunStored.id = UUID()
|
|
|
+ newTempTargetRunStored.name = canceledTempTarget.name
|
|
|
+ newTempTargetRunStored.startDate = canceledTempTarget.date ?? .distantPast
|
|
|
+ newTempTargetRunStored.endDate = Date()
|
|
|
+ newTempTargetRunStored
|
|
|
+ .target = canceledTempTarget.target ?? 0
|
|
|
+ newTempTargetRunStored.tempTarget = canceledTempTarget
|
|
|
+ newTempTargetRunStored.isUploadedToNS = false
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- func cancelTempTarget() async throws {
|
|
|
- await storage.storeTempTarget(tempTarget: TempTarget.cancel(at: Date()))
|
|
|
- try coredataContext.performAndWait {
|
|
|
- let saveToCoreData = TempTargetStored(context: self.coredataContext)
|
|
|
- saveToCoreData.enabled = false
|
|
|
- saveToCoreData.date = Date()
|
|
|
- if coredataContext.hasChanges {
|
|
|
- try self.coredataContext.save()
|
|
|
+ // Disable all override except the one with overrideID
|
|
|
+ for tempTargetToCancel in results {
|
|
|
+ if tempTargetToCancel.objectID != tempTargetID {
|
|
|
+ tempTargetToCancel.enabled = false
|
|
|
+ tempTargetToCancel.isUploadedToNS = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- let setHBT = TempTargetStored(context: self.coredataContext)
|
|
|
- setHBT.enabled = false
|
|
|
- setHBT.date = Date()
|
|
|
- if coredataContext.hasChanges {
|
|
|
- try self.coredataContext.save()
|
|
|
+ if viewContext.hasChanges {
|
|
|
+ try viewContext.save()
|
|
|
+
|
|
|
+ // Update State variables in OverrideView
|
|
|
+ Foundation.NotificationCenter.default.post(name: .willUpdateTempTargetConfiguration, object: nil)
|
|
|
}
|
|
|
+
|
|
|
+ // Await the notification
|
|
|
+ print("Waiting for notification...")
|
|
|
+ await awaitNotification(.didUpdateTempTargetConfiguration)
|
|
|
+ print("Notification received, continuing...")
|
|
|
+
|
|
|
+ } catch {
|
|
|
+ debugPrint(
|
|
|
+ "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to disable active Temp Targets with error: \(error.localizedDescription)"
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
}
|