feat(watchos): ConnectivityManager (WCSession) — envoi des samples vers l'iPhone
sendMessage si iPhone joignable, sinon buffer + transferUserInfo (livraison différée). Délégué WCSession hors-main, hop vers main pour @Published isReachable, buffer sérialisé sur file dédiée. Build watchsimulator OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
76
ios/App/CoachWatch/ConnectivityManager.swift
Normal file
76
ios/App/CoachWatch/ConnectivityManager.swift
Normal file
@@ -0,0 +1,76 @@
|
||||
import Foundation
|
||||
import WatchConnectivity
|
||||
|
||||
/// 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
|
||||
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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user