Browse Source

Attempt to use a background Task to finish the Update problem to NS when enacting an Override via Shortcut

polscm32 1 year ago
parent
commit
b63aaba606

+ 1 - 1
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -392,7 +392,7 @@ extension Home {
                 guard viewContext.hasChanges else { return }
                 try viewContext.save()
 
-                Foundation.NotificationCenter.default.post(name: .didUpdateOverrideConfiguration, object: nil)
+                Foundation.NotificationCenter.default.post(name: .willUpdateOverrideConfiguration, object: nil)
             } catch {
                 debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to cancel Profile")
             }

+ 9 - 10
FreeAPS/Sources/Modules/OverrideConfig/OverrideStateModel.swift

@@ -1,3 +1,4 @@
+import Combine
 import CoreData
 import Observation
 import SwiftUI
@@ -57,6 +58,8 @@ extension OverrideConfig {
             return "Please enter a valid target between" + " \(target)."
         }
 
+        private var cancellables = Set<AnyCancellable>()
+
         override func subscribe() {
             setupNotification()
             units = settingsManager.settings.units
@@ -97,16 +100,12 @@ extension OverrideConfig {
 extension OverrideConfig.StateModel {
     // Custom Notification to update View when an Override has been cancelled via Home View
     func setupNotification() {
-        Foundation.NotificationCenter.default.addObserver(
-            self,
-            selector: #selector(handleOverrideConfigurationUpdate),
-            name: .didUpdateOverrideConfiguration,
-            object: nil
-        )
-    }
-
-    @objc private func handleOverrideConfigurationUpdate() {
-        updateLatestOverrideConfiguration()
+        Foundation.NotificationCenter.default.publisher(for: .willUpdateOverrideConfiguration)
+            .sink { [weak self] _ in
+                guard let self = self else { return }
+                self.updateLatestOverrideConfiguration()
+            }
+            .store(in: &cancellables)
     }
 
     // MARK: - Enact Overrides

+ 11 - 12
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -142,18 +142,17 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     }
 
     func setupNotification() {
-        Foundation.NotificationCenter.default.addObserver(
-            self,
-            selector: #selector(handleOverrideConfigurationUpdate),
-            name: .didUpdateOverrideConfiguration,
-            object: nil
-        )
-    }
-
-    @objc private func handleOverrideConfigurationUpdate() {
-        Task.detached {
-            await self.uploadOverrides()
-        }
+        Foundation.NotificationCenter.default.publisher(for: .willUpdateOverrideConfiguration)
+            .sink { [weak self] _ in
+                guard let self = self else { return }
+                Task {
+                    await self.uploadOverrides()
+
+                    // Post a notification indicating that the upload has finished and that we can end the background task in the OverridePresetsIntentRequest
+                    Foundation.NotificationCenter.default.post(name: .didUpdateOverrideConfiguration, object: nil)
+                }
+            }
+            .store(in: &subscriptions)
     }
 
     func sourceInfo() -> [String: Any]? {

+ 35 - 8
FreeAPS/Sources/Shortcuts/Override/OverridePresetsIntentRequest.swift

@@ -1,5 +1,6 @@
 import CoreData
 import Foundation
+import UIKit
 
 @available(iOS 16.0, *) final class OverridePresetsIntentRequest: BaseIntentsRequest {
     enum overridePresetsError: Error {
@@ -77,31 +78,57 @@ import Foundation
     }
 
     @MainActor func enactOverride(_ preset: OverridePreset) async -> Bool {
+        // Start background task to ensure that the task can run in background mode
+        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
+        }
+
         do {
             guard let overrideID = await fetchOverrideID(preset),
-                  let overrideObject = try viewContext.existingObject(with: overrideID) as? OverrideStored else { return false }
+                  let overrideObject = try viewContext.existingObject(with: overrideID) as? OverrideStored
+            else {
+                // Be sure to end background task if error occurs
+                await UIApplication.shared.endBackgroundTask(backgroundTaskID)
+                return false
+            }
 
+            // Enable Override
             overrideObject.enabled = true
             overrideObject.date = Date()
             overrideObject.isUploadedToNS = false
 
-            // Disable previous Overrides
-            /// do not create a OverrideRunEntry because we only want that if we cancel a running Override, not when enacting a Preset
+            // Disable previous overrides if necessary
             await disableAllActiveOverrides(except: overrideID, createOverrideRunEntry: true)
 
             if viewContext.hasChanges {
                 try viewContext.save()
 
                 // Update State variables in OverrideView
-                Foundation.NotificationCenter.default.post(name: .didUpdateOverrideConfiguration, object: nil)
+                Foundation.NotificationCenter.default.post(name: .willUpdateOverrideConfiguration, object: nil)
+
+                // Await the notification
+                print("Waiting for notification...")
+                await awaitNotification(.didUpdateOverrideConfiguration)
+                print("Notification received, continuing...")
 
+                // End background task after everything is done
+                await UIApplication.shared.endBackgroundTask(backgroundTaskID)
                 return true
             }
-        } catch let error as NSError {
-            debugPrint(
-                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to enact Override: \(error.localizedDescription)"
-            )
+        } catch {
+            // Handle error and ensure background task is ended
+            debugPrint("Failed to enact Override: \(error.localizedDescription)")
+            await UIApplication.shared.endBackgroundTask(backgroundTaskID)
         }
+
+        // Make sure background task ends in any case
+        await UIApplication.shared.endBackgroundTask(backgroundTaskID)
         return false
     }
 

+ 19 - 0
Model/Helper/CustomNotification.swift

@@ -1,6 +1,25 @@
+import Combine
 import Foundation
 
 extension Notification.Name {
+    static let willUpdateOverrideConfiguration = Notification.Name("willUpdateOverrideConfiguration")
     static let didUpdateOverrideConfiguration = Notification.Name("didUpdateOverrideConfiguration")
     static let didUpdateCobIob = Notification.Name("didUpdateCobIob")
 }
+
+func awaitNotification(_ name: Notification.Name) async {
+    await withCheckedContinuation { continuation in
+        var cancellable: AnyCancellable?
+
+        // Create a Combine publisher that listens for notifications
+        cancellable = Foundation.NotificationCenter.default
+            .publisher(for: name)
+            .sink { _ in
+                // When the notification is received, resume the awaiting task
+                continuation.resume()
+
+                // Cancel the subscription after the continuation has resumed
+                cancellable?.cancel()
+            }
+    }
+}