import Foundation import WatchConnectivity extension Notification.Name { static let fuelBoardWatchRefreshRequested = Notification.Name("FuelBoardWatchRefreshRequested") static let fuelBoardShowStationDetail = Notification.Name("FuelBoardShowStationDetail") } @MainActor final class WatchSyncManager: NSObject, WCSessionDelegate { static let shared = WatchSyncManager() private var pendingSnapshotPush = false private override init() { super.init() } func activate() { guard WCSession.isSupported() else { return } pendingSnapshotPush = true let session = WCSession.default if session.delegate !== self { session.delegate = self } session.activate() } func pushSnapshot() { guard WCSession.isSupported() else { return } let session = WCSession.default guard session.activationState == .activated else { pendingSnapshotPush = true return } do { try session.updateApplicationContext(snapshotContext()) pendingSnapshotPush = false } catch { print("WATCH-SYNC push failed: \(error.localizedDescription)") } } private func snapshotContext() -> [String: Any] { var context: [String: Any] = [:] if let favourites = try? JSONEncoder().encode(FuelStore.loadFavourites()) { context[FuelStore.favouritesKey] = favourites } // Do NOT send the full station dump to watchOS via WatchConnectivity. // The UK dataset is megabytes large and exceeds application-context // payload limits, which prevents any snapshot from arriving. The // favourites payload already carries station snapshots + prices, so the // watch can render favourites without the full list. context[FuelStore.fuelKey] = FuelStore.loadSelectedFuel().rawValue context[FuelStore.distanceUnitKey] = FuelStore.loadDistanceUnit().rawValue context[FuelStore.priceDisplayStyleKey] = FuelStore.loadPriceDisplayStyle().rawValue if let lastRefresh = FuelStore.loadLastRefresh() { context[FuelStore.lastRefreshKey] = String(lastRefresh.timeIntervalSince1970) } if let handled = FuelStore.loadWatchRefreshHandled() { context[FuelStore.watchRefreshHandledKey] = String(handled.timeIntervalSince1970) } if let location = FuelStore.loadLocationWithDate() { context[FuelStore.locationKey] = "\(location.coordinate.lat),\(location.coordinate.lng),\(location.date.timeIntervalSince1970)" } return context } nonisolated func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: (any Error)?) { if let error { print("WATCH-SYNC activation failed: \(error.localizedDescription)") return } Task { @MainActor in if activationState == .activated, self.pendingSnapshotPush { self.pushSnapshot() } } } nonisolated func sessionDidBecomeInactive(_ session: WCSession) {} nonisolated func sessionDidDeactivate(_ session: WCSession) { Task { @MainActor in WCSession.default.activate() } } nonisolated func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { handleIncomingMessage(userInfo) } nonisolated func session(_ session: WCSession, didReceiveMessage message: [String : Any]) { handleIncomingMessage(message) } private nonisolated func handleIncomingMessage(_ payload: [String: Any]) { if payload["fuelboard.snapshotRequest"] as? String != nil { Task { @MainActor in self.pushSnapshot() } } guard let raw = payload[FuelStore.watchRefreshRequestKey] as? String, let timestamp = TimeInterval(raw) else { return } let date = Date(timeIntervalSince1970: timestamp) Task { @MainActor in FuelStore.requestWatchRefresh(date) NotificationCenter.default.post(name: .fuelBoardWatchRefreshRequested, object: nil) } } }