import Foundation import WatchConnectivity import os private let connLog = Logger(subsystem: "ch.hypnotruck.coach.watchkitapp", category: "connectivity") /// Envoie les samples de séance vers l'iPhone. /// - `sendMessage` quand l'iPhone est joignable (temps réel) /// - sinon buffer + `transferUserInfo` (livraison différée garantie) final class ConnectivityManager: NSObject, ObservableObject { static let shared = ConnectivityManager() @Published private(set) var isReachable = false private let queue = DispatchQueue(label: "ch.hypnotruck.coach.connectivity") private var buffer: [[String: Any]] = [] private override init() { super.init() guard WCSession.isSupported() else { return } let session = WCSession.default session.delegate = self session.activate() } func sendSample(_ payload: [String: Any]) { queue.async { let session = WCSession.default let hr = (payload["heartRate"] as? Double) ?? -1 connLog.info("sendSample activated=\(session.activationState == .activated, privacy: .public) reachable=\(session.isReachable, privacy: .public) hr=\(hr, privacy: .public)") guard session.activationState == .activated else { self.buffer.append(payload) return } if session.isReachable { session.sendMessage(payload, replyHandler: nil) { [weak self] _ in // échec d'envoi temps réel -> bascule en livraison différée self?.queue.async { self?.buffer.append(payload) self?.flushLocked() } } } else { self.buffer.append(payload) self.flushLocked() } } } /// À appeler sur `queue` uniquement. private func flushLocked() { guard !buffer.isEmpty else { return } let session = WCSession.default guard session.activationState == .activated else { return } session.transferUserInfo(["samples": buffer]) buffer.removeAll() } private func updateReachable(_ value: Bool) { DispatchQueue.main.async { [weak self] in self?.isReachable = value } } } extension ConnectivityManager: WCSessionDelegate { func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { updateReachable(session.isReachable) if activationState == .activated { queue.async { [weak self] in self?.flushLocked() } } } func sessionReachabilityDidChange(_ session: WCSession) { let reachable = session.isReachable updateReachable(reachable) if reachable { queue.async { [weak self] in self?.flushLocked() } } } }