Skip to content
Merged
2 changes: 1 addition & 1 deletion Sources/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

private func setupLiveActivityReattachment() {
#if canImport(ActivityKit)
#if os(iOS) && !targetEnvironment(macCatalyst)
if #available(iOS 17.2, *) {
// Pre-warm the registry on the main thread before spawning background Tasks.
// This avoids a lazy-init race if a push notification handler accesses it
Expand Down
7 changes: 7 additions & 0 deletions Sources/App/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -513,8 +513,15 @@ This server requires a client certificate (mTLS) but the operation was cancelled
"live_activity.status.enabled" = "Enabled";
"live_activity.status.not_supported" = "Not available on iPad";
"live_activity.status.open_settings" = "Open Settings";
"live_activity.end_all.button" = "End All Activities";
"live_activity.end_all.confirm.title" = "End all Live Activities?";
"live_activity.end_all.confirm.button" = "End All";
"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings › %@ › Live Activities.";
"live_activity.frequent_updates.title" = "Frequent Updates";
"live_activity.privacy.message" = "Live Activity content is visible on your Lock Screen and Dynamic Island without Face ID or Touch ID. Choose what you display carefully.";
"live_activity.subtitle" = "Real-time Home Assistant updates on your Lock Screen and Dynamic Island.";
"live_activity.title" = "Live Activities";

"location_change_notification.app_shortcut.body" = "Location updated via App Shortcut";
"location_change_notification.background_fetch.body" = "Current location delivery triggered via background fetch";
"location_change_notification.beacon_region_enter.body" = "%@ entered via iBeacon";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
import Shared
import SwiftUI
Expand Down Expand Up @@ -685,3 +686,4 @@ private struct ActivitySnapshot: Identifiable {
self.message = activity.content.state.message
}
}
#endif
4 changes: 4 additions & 0 deletions Sources/App/Settings/Settings/SettingsItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,13 @@ enum SettingsItem: String, Hashable, CaseIterable {
case .notifications:
SettingsNotificationsView()
case .liveActivities:
#if os(iOS) && !targetEnvironment(macCatalyst)
if #available(iOS 17.2, *) {
LiveActivitySettingsView()
}
#else
EmptyView()
#endif
case .sensors:
SensorListView()
case .nfc:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
import Shared
import SwiftUI
Expand Down Expand Up @@ -161,3 +162,4 @@ struct HAExpandedBottomView: View {
return .haPrimary
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
import Shared
import SwiftUI
Expand All @@ -21,3 +22,4 @@ struct HALiveActivityConfiguration: Widget {
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
import Shared
import SwiftUI
Expand Down Expand Up @@ -83,3 +84,4 @@ struct HALockScreenView: View {
return .haPrimary
}
}
#endif
6 changes: 6 additions & 0 deletions Sources/Extensions/Widgets/Widgets.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ struct WidgetsBundleLegacy: WidgetBundle {
}

var body: some Widget {
#if os(iOS) && !targetEnvironment(macCatalyst)
if #available(iOSApplicationExtension 17.2, *) {
HALiveActivityConfiguration()
}
#endif
WidgetAssist()
LegacyWidgetActions()
WidgetOpenPage()
Expand All @@ -37,9 +39,11 @@ struct WidgetsBundle17: WidgetBundle {
}

var body: some Widget {
#if os(iOS) && !targetEnvironment(macCatalyst)
if #available(iOSApplicationExtension 17.2, *) {
HALiveActivityConfiguration()
}
#endif
WidgetCommonlyUsedEntities()
WidgetCustom()
WidgetAssist()
Expand All @@ -60,7 +64,9 @@ struct WidgetsBundle18: WidgetBundle {
}

var body: some Widget {
#if os(iOS) && !targetEnvironment(macCatalyst)
HALiveActivityConfiguration()
#endif

// Controls
ControlAssist()
Expand Down
4 changes: 2 additions & 2 deletions Sources/Shared/API/HAAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Version
#if os(iOS)
import Reachability
#endif
#if os(iOS) && canImport(ActivityKit)
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
#endif

Expand Down Expand Up @@ -562,7 +562,7 @@ public class HomeAssistantAPI {
"push_token": pushID,
]

#if os(iOS) && canImport(ActivityKit)
#if os(iOS) && !targetEnvironment(macCatalyst)
if #available(iOS 17.2, *) {
// Advertise Live Activity support so HA can gate the UI and send
// activity push tokens back to the relay server.
Expand Down
2 changes: 1 addition & 1 deletion Sources/Shared/Environment/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public class AppEnvironment {
}

#if os(iOS)
#if canImport(ActivityKit)
#if !targetEnvironment(macCatalyst)
/// Call `_ = Current.liveActivityRegistry` on the main thread at launch (before any
/// background thread can access it) to avoid a lazy-init race between concurrent callers.
public lazy var liveActivityRegistry: LiveActivityRegistryProtocol? = {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Shared/LiveActivity/HALiveActivityAttributes.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if canImport(ActivityKit)
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
import SwiftUI

Expand Down
2 changes: 1 addition & 1 deletion Sources/Shared/LiveActivity/LiveActivityRegistry.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if canImport(ActivityKit)
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
import Foundation

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if canImport(ActivityKit)
#if os(iOS) && !targetEnvironment(macCatalyst)
import ActivityKit
import Foundation
import PromiseKit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class NotificationCommandManager {
register(command: "clear_notification", handler: HandlerClearNotification())
#if os(iOS)
register(command: "update_complications", handler: HandlerUpdateComplications())
#if canImport(ActivityKit)
#if !targetEnvironment(macCatalyst)
if #available(iOS 17.2, *) {
register(command: "live_activity", handler: HandlerStartOrUpdateLiveActivity())
register(command: "end_live_activity", handler: HandlerEndLiveActivity())
Expand All @@ -48,7 +48,7 @@ public class NotificationCommandManager {

// Support data.live_update: true — the same field Android uses for Live Updates.
// A single YAML automation can target both platforms with no platform-specific keys.
#if canImport(ActivityKit)
#if os(iOS) && !targetEnvironment(macCatalyst)
if #available(iOS 17.2, *), hadict["live_update"] as? Bool == true,
let handler = commands["live_activity"] {
return handler.handle(hadict)
Expand Down Expand Up @@ -111,8 +111,8 @@ private struct HandlerClearNotification: NotificationCommandHandler {
// Also end any Live Activity whose tag matches — same YAML works on both iOS and Android.
// Bridged into the returned Promise so the background fetch window stays open until
// the activity is actually dismissed (prevents the OS suspending mid-dismiss).
// ActivityKit is unavailable in the PushProvider extension, so guard accordingly.
#if os(iOS) && canImport(ActivityKit)
// ActivityKit is unavailable in the PushProvider extension and Mac Catalyst, so guard accordingly.
#if os(iOS) && !targetEnvironment(macCatalyst)
if #available(iOS 17.2, *), !Current.isAppExtension, let tag = payload["tag"] as? String {
return Promise<Void> { seal in
Task {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Shared/Resources/Swiftgen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1874,7 +1874,7 @@ public enum L10n {
}
}
public enum FrequentUpdates {
/// Allows Home Assistant to update Live Activities up to once per second. Enable in Settings u203A %@ u203A Live Activities.
/// Allows Home Assistant to update Live Activities up to once per second. Enable in Settings %@ Live Activities.
public static func footer(_ p1: Any) -> String {
return L10n.tr("Localizable", "live_activity.frequent_updates.footer", String(describing: p1))
}
Expand Down
Loading