فهرست منبع

Exclude Live Activity code from Mac Catalyst builds (#601)

Wrap all Live Activity references behind #if !targetEnvironment(macCatalyst)
compiler directives to fix build errors when targeting Mac Catalyst, since
ActivityKit is not available on that platform.
Jonas Björkert 3 ماه پیش
والد
کامیت
4e3e322c6b

+ 5 - 3
LoopFollow/Application/SceneDelegate.swift

@@ -39,9 +39,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         // scene(_:openURLContexts:) fires after sceneDidBecomeActive when the app
         // foregrounds from background. Post on the next run loop so the view
         // hierarchy (including any presented modals) is fully settled.
-        DispatchQueue.main.async {
-            NotificationCenter.default.post(name: .liveActivityDidForeground, object: nil)
-        }
+        #if !targetEnvironment(macCatalyst)
+            DispatchQueue.main.async {
+                NotificationCenter.default.post(name: .liveActivityDidForeground, object: nil)
+            }
+        #endif
     }
 
     func sceneWillResignActive(_: UIScene) {

+ 2 - 2
LoopFollow/LiveActivity/LiveActivityManager.swift

@@ -780,10 +780,10 @@ final class LiveActivityManager {
     }
 }
 
-#endif
-
 extension Notification.Name {
     /// Posted when the user taps the Live Activity or Dynamic Island.
     /// Observers navigate to the Home or Snoozer tab as appropriate.
     static let liveActivityDidForeground = Notification.Name("liveActivityDidForeground")
 }
+
+#endif

+ 7 - 3
LoopFollow/LiveActivity/StorageCurrentGlucoseStateProvider.swift

@@ -131,8 +131,12 @@ struct StorageCurrentGlucoseStateProvider: CurrentGlucoseStateProviding {
     // MARK: - Renewal
 
     var showRenewalOverlay: Bool {
-        let renewBy = Storage.shared.laRenewBy.value
-        let now = Date().timeIntervalSince1970
-        return renewBy > 0 && now >= renewBy - LiveActivityManager.renewalWarning
+        #if targetEnvironment(macCatalyst)
+            return false
+        #else
+            let renewBy = Storage.shared.laRenewBy.value
+            let now = Date().timeIntervalSince1970
+            return renewBy > 0 && now >= renewBy - LiveActivityManager.renewalWarning
+        #endif
     }
 }

+ 67 - 64
LoopFollow/LiveActivitySettingsView.swift

@@ -1,40 +1,58 @@
 // LoopFollow
 // LiveActivitySettingsView.swift
 
-import SwiftUI
+#if !targetEnvironment(macCatalyst)
 
-struct LiveActivitySettingsView: View {
-    @State private var laEnabled: Bool = Storage.shared.laEnabled.value
-    @State private var restartConfirmed = false
-    @State private var slots: [LiveActivitySlotOption] = LAAppGroupSettings.slots()
-    @State private var smallWidgetSlot: LiveActivitySlotOption = LAAppGroupSettings.smallWidgetSlot()
+    import SwiftUI
 
-    private let slotLabels = ["Top left", "Top right", "Bottom left", "Bottom right"]
+    struct LiveActivitySettingsView: View {
+        @State private var laEnabled: Bool = Storage.shared.laEnabled.value
+        @State private var restartConfirmed = false
+        @State private var slots: [LiveActivitySlotOption] = LAAppGroupSettings.slots()
+        @State private var smallWidgetSlot: LiveActivitySlotOption = LAAppGroupSettings.smallWidgetSlot()
 
-    var body: some View {
-        Form {
-            Section(header: Text("Live Activity")) {
-                Toggle("Enable Live Activity", isOn: $laEnabled)
-            }
+        private let slotLabels = ["Top left", "Top right", "Bottom left", "Bottom right"]
+
+        var body: some View {
+            Form {
+                Section(header: Text("Live Activity")) {
+                    Toggle("Enable Live Activity", isOn: $laEnabled)
+                }
 
-            if laEnabled {
-                Section {
-                    Button(restartConfirmed ? "Live Activity Restarted" : "Restart Live Activity") {
-                        LiveActivityManager.shared.forceRestart()
-                        restartConfirmed = true
-                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
-                            restartConfirmed = false
+                if laEnabled {
+                    Section {
+                        Button(restartConfirmed ? "Live Activity Restarted" : "Restart Live Activity") {
+                            LiveActivityManager.shared.forceRestart()
+                            restartConfirmed = true
+                            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
+                                restartConfirmed = false
+                            }
                         }
+                        .disabled(restartConfirmed)
                     }
-                    .disabled(restartConfirmed)
                 }
-            }
 
-            Section(header: Text("Grid Slots - Live Activity")) {
-                ForEach(0 ..< 4, id: \.self) { index in
-                    Picker(slotLabels[index], selection: Binding(
-                        get: { slots[index] },
-                        set: { selectSlot($0, at: index) }
+                Section(header: Text("Grid Slots - Live Activity")) {
+                    ForEach(0 ..< 4, id: \.self) { index in
+                        Picker(slotLabels[index], selection: Binding(
+                            get: { slots[index] },
+                            set: { selectSlot($0, at: index) }
+                        )) {
+                            ForEach(LiveActivitySlotOption.allCases, id: \.self) { option in
+                                Text(option.displayName).tag(option)
+                            }
+                        }
+                    }
+                }
+
+                Section(header: Text("Grid Slot - CarPlay / Watch")) {
+                    Picker("Right slot", selection: Binding(
+                        get: { smallWidgetSlot },
+                        set: { newValue in
+                            smallWidgetSlot = newValue
+                            LAAppGroupSettings.setSmallWidgetSlot(newValue)
+                            LiveActivityManager.shared.refreshFromCurrentState(reason: "small widget slot changed")
+                        }
                     )) {
                         ForEach(LiveActivitySlotOption.allCases, id: \.self) { option in
                             Text(option.displayName).tag(option)
@@ -42,48 +60,33 @@ struct LiveActivitySettingsView: View {
                     }
                 }
             }
-
-            Section(header: Text("Grid Slot - CarPlay / Watch")) {
-                Picker("Right slot", selection: Binding(
-                    get: { smallWidgetSlot },
-                    set: { newValue in
-                        smallWidgetSlot = newValue
-                        LAAppGroupSettings.setSmallWidgetSlot(newValue)
-                        LiveActivityManager.shared.refreshFromCurrentState(reason: "small widget slot changed")
-                    }
-                )) {
-                    ForEach(LiveActivitySlotOption.allCases, id: \.self) { option in
-                        Text(option.displayName).tag(option)
-                    }
-                }
+            .onReceive(Storage.shared.laEnabled.$value) { newValue in
+                if newValue != laEnabled { laEnabled = newValue }
             }
-        }
-        .onReceive(Storage.shared.laEnabled.$value) { newValue in
-            if newValue != laEnabled { laEnabled = newValue }
-        }
-        .onChange(of: laEnabled) { newValue in
-            Storage.shared.laEnabled.value = newValue
-            if newValue {
-                LiveActivityManager.shared.forceRestart()
-            } else {
-                LiveActivityManager.shared.end(dismissalPolicy: .immediate)
+            .onChange(of: laEnabled) { newValue in
+                Storage.shared.laEnabled.value = newValue
+                if newValue {
+                    LiveActivityManager.shared.forceRestart()
+                } else {
+                    LiveActivityManager.shared.end(dismissalPolicy: .immediate)
+                }
             }
+            .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
+            .navigationTitle("Live Activity")
+            .navigationBarTitleDisplayMode(.inline)
         }
-        .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
-        .navigationTitle("Live Activity")
-        .navigationBarTitleDisplayMode(.inline)
-    }
 
-    /// Selects an option for the given slot index, enforcing uniqueness:
-    /// if the chosen option is already in another slot, that slot is cleared to `.none`.
-    private func selectSlot(_ option: LiveActivitySlotOption, at index: Int) {
-        if option != .none {
-            for i in 0 ..< slots.count where i != index && slots[i] == option {
-                slots[i] = .none
+        /// Selects an option for the given slot index, enforcing uniqueness:
+        /// if the chosen option is already in another slot, that slot is cleared to `.none`.
+        private func selectSlot(_ option: LiveActivitySlotOption, at index: Int) {
+            if option != .none {
+                for i in 0 ..< slots.count where i != index && slots[i] == option {
+                    slots[i] = .none
+                }
             }
+            slots[index] = option
+            LAAppGroupSettings.setSlots(slots)
+            LiveActivityManager.shared.refreshFromCurrentState(reason: "slot config changed")
         }
-        slots[index] = option
-        LAAppGroupSettings.setSlots(slots)
-        LiveActivityManager.shared.refreshFromCurrentState(reason: "slot config changed")
     }
-}
+#endif

+ 13 - 7
LoopFollow/Settings/SettingsMenuView.swift

@@ -71,11 +71,13 @@ struct SettingsMenuView: View {
                         settingsPath.value.append(Sheet.apn)
                     }
 
-                    NavigationRow(title: "Live Activity",
-                                  icon: "dot.radiowaves.left.and.right")
-                    {
-                        settingsPath.value.append(Sheet.liveActivity)
-                    }
+                    #if !targetEnvironment(macCatalyst)
+                        NavigationRow(title: "Live Activity",
+                                      icon: "dot.radiowaves.left.and.right")
+                        {
+                            settingsPath.value.append(Sheet.liveActivity)
+                        }
+                    #endif
 
                     if !nightscoutURL.value.isEmpty {
                         NavigationRow(title: "Remote",
@@ -171,7 +173,9 @@ private enum Sheet: Hashable, Identifiable {
     case infoDisplay
     case alarmSettings
     case apn
-    case liveActivity
+    #if !targetEnvironment(macCatalyst)
+        case liveActivity
+    #endif
     case remote
     case importExport
     case calendar, contact
@@ -192,7 +196,9 @@ private enum Sheet: Hashable, Identifiable {
         case .infoDisplay: InfoDisplaySettingsView(viewModel: .init())
         case .alarmSettings: AlarmSettingsView()
         case .apn: APNSettingsView()
-        case .liveActivity: LiveActivitySettingsView()
+        #if !targetEnvironment(macCatalyst)
+            case .liveActivity: LiveActivitySettingsView()
+        #endif
         case .remote: RemoteSettingsView(viewModel: .init())
         case .importExport: ImportExportSettingsView()
         case .calendar: CalendarSettingsView()

+ 4 - 1
LoopFollow/ViewControllers/MainViewController.swift

@@ -194,7 +194,10 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         // when runMigrationsIfNeeded() is called. This catches migrations deferred by a
         // background BGAppRefreshTask launch in Before-First-Unlock state.
         notificationCenter.addObserver(self, selector: #selector(appDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
-        notificationCenter.addObserver(self, selector: #selector(navigateOnLAForeground), name: .liveActivityDidForeground, object: nil)
+
+        #if !targetEnvironment(macCatalyst)
+            notificationCenter.addObserver(self, selector: #selector(navigateOnLAForeground), name: .liveActivityDidForeground, object: nil)
+        #endif
 
         // Setup the Graph
         if firstGraphLoad {

+ 37 - 35
RestartLiveActivityIntent.swift

@@ -1,45 +1,47 @@
 // LoopFollow
 // RestartLiveActivityIntent.swift
 
-import AppIntents
-import UIKit
-
-@available(iOS 16.4, *)
-struct RestartLiveActivityIntent: AppIntent, ForegroundContinuableIntent {
-    static var title: LocalizedStringResource = "Restart Live Activity"
-    static var description = IntentDescription("Starts or restarts the LoopFollow Live Activity.")
+#if !targetEnvironment(macCatalyst)
+    import AppIntents
+    import UIKit
+
+    @available(iOS 16.4, *)
+    struct RestartLiveActivityIntent: AppIntent, ForegroundContinuableIntent {
+        static var title: LocalizedStringResource = "Restart Live Activity"
+        static var description = IntentDescription("Starts or restarts the LoopFollow Live Activity.")
+
+        func perform() async throws -> some IntentResult & ProvidesDialog {
+            Storage.shared.laEnabled.value = true
+
+            let keyId = Storage.shared.lfKeyId.value
+            let apnsKey = Storage.shared.lfApnsKey.value
+
+            if keyId.isEmpty || apnsKey.isEmpty {
+                if let url = URL(string: "loopfollow://settings/live-activity") {
+                    await MainActor.run { UIApplication.shared.open(url) }
+                }
+                return .result(dialog: "Please enter your APNs credentials in LoopFollow settings to use the Live Activity.")
+            }
 
-    func perform() async throws -> some IntentResult & ProvidesDialog {
-        Storage.shared.laEnabled.value = true
+            if #available(iOS 26.0, *) {
+                try await continueInForeground()
+            }
 
-        let keyId = Storage.shared.lfKeyId.value
-        let apnsKey = Storage.shared.lfApnsKey.value
+            await MainActor.run { LiveActivityManager.shared.forceRestart() }
 
-        if keyId.isEmpty || apnsKey.isEmpty {
-            if let url = URL(string: "loopfollow://settings/live-activity") {
-                await MainActor.run { UIApplication.shared.open(url) }
-            }
-            return .result(dialog: "Please enter your APNs credentials in LoopFollow settings to use the Live Activity.")
+            return .result(dialog: "Live Activity restarted.")
         }
+    }
 
-        if #available(iOS 26.0, *) {
-            try await continueInForeground()
+    @available(iOS 16.4, *)
+    struct LoopFollowAppShortcuts: AppShortcutsProvider {
+        static var appShortcuts: [AppShortcut] {
+            AppShortcut(
+                intent: RestartLiveActivityIntent(),
+                phrases: ["Restart Live Activity in \(.applicationName)"],
+                shortTitle: "Restart Live Activity",
+                systemImageName: "dot.radiowaves.left.and.right"
+            )
         }
-
-        await MainActor.run { LiveActivityManager.shared.forceRestart() }
-
-        return .result(dialog: "Live Activity restarted.")
-    }
-}
-
-@available(iOS 16.4, *)
-struct LoopFollowAppShortcuts: AppShortcutsProvider {
-    static var appShortcuts: [AppShortcut] {
-        AppShortcut(
-            intent: RestartLiveActivityIntent(),
-            phrases: ["Restart Live Activity in \(.applicationName)"],
-            shortTitle: "Restart Live Activity",
-            systemImageName: "dot.radiowaves.left.and.right"
-        )
     }
-}
+#endif