feat(ios): plugin Capacitor CoachLiveBridge (Phase 3) — relais WCSession -> /live
CAPPlugin + CAPBridgedPlugin (jsName CoachLiveBridge). Active WCSession au load(),
reçoit didReceiveMessage (temps réel) + didReceiveUserInfo (lot bufferisé) et
relaie au JS via notifyListeners("liveSample"). Enregistré dans
MainViewController.capacitorDidLoad(). Méthode getStatus pour debug JS. Build OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
01468471ECA28EA66AFE2D6C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EB6C76F68679844EC72178F /* Foundation.framework */; };
|
||||
21B242F8E61FB07C476C477C /* CoachLiveBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DF3346C49A690C3C3DF2F3 /* CoachLiveBridge.swift */; };
|
||||
290D0F17978B7EF3DDB3A792 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A06CDFF5B4F7852B6329862 /* ContentView.swift */; };
|
||||
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
|
||||
3E6740C521EF14892687BCF7 /* CoachWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 2517374227A4072A4D6AFC15 /* CoachWatch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
@@ -71,6 +72,7 @@
|
||||
8AC9C9B9971F55027D227A06 /* WorkoutManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WorkoutManager.swift; sourceTree = "<group>"; };
|
||||
958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
A420239FC6D065C8A599B61C /* ConnectivityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivityManager.swift; sourceTree = "<group>"; };
|
||||
A9DF3346C49A690C3C3DF2F3 /* CoachLiveBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CoachLiveBridge.swift; sourceTree = "<group>"; };
|
||||
C0AC4A07811751FB78AA0002 /* CoachAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachAuth.swift; sourceTree = "<group>"; };
|
||||
D09B589FA572B2C14E8CD835 /* CoachWatch.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = CoachWatch.entitlements; sourceTree = "<group>"; };
|
||||
D1BD5B1990CF3F2B9C5D0002 /* CoachWorkoutKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachWorkoutKit.swift; sourceTree = "<group>"; };
|
||||
@@ -144,6 +146,7 @@
|
||||
504EC3131FED79650016851F /* Info.plist */,
|
||||
2FAD9762203C412B000D30F8 /* config.xml */,
|
||||
50B271D01FEDC1A000F3C39B /* public */,
|
||||
A9DF3346C49A690C3C3DF2F3 /* CoachLiveBridge.swift */,
|
||||
);
|
||||
path = App;
|
||||
sourceTree = "<group>";
|
||||
@@ -284,6 +287,7 @@
|
||||
F1ABCDEF0123456789AB0001 /* CoachHealthRoute.swift in Sources */,
|
||||
88F28EB12FBB330E00E8306E /* CoachWorkoutObserver.swift in Sources */,
|
||||
E2CE6C2AA1DF4F3CAD6E0001 /* MainViewController.swift in Sources */,
|
||||
21B242F8E61FB07C476C477C /* CoachLiveBridge.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
80
ios/App/App/CoachLiveBridge.swift
Normal file
80
ios/App/App/CoachLiveBridge.swift
Normal file
@@ -0,0 +1,80 @@
|
||||
// CoachLiveBridge.swift
|
||||
// Plugin Capacitor (côté iPhone) : reçoit les samples de séance poussés par
|
||||
// l'app watchOS CoachWatch via WatchConnectivity et les relaie au JS via
|
||||
// l'event "liveSample" (consommé par la page web /live).
|
||||
//
|
||||
// Enregistrement : MainViewController.capacitorDidLoad() ->
|
||||
// bridge?.registerPluginInstance(CoachLiveBridgePlugin())
|
||||
//
|
||||
// Usage JS :
|
||||
// const CoachLiveBridge = registerPlugin('CoachLiveBridge');
|
||||
// CoachLiveBridge.addListener('liveSample', (s) => { ... });
|
||||
// // s.heartRate, s.activeEnergyKcal, s.distanceMeters, s.elapsedSec, s.ts
|
||||
|
||||
import Foundation
|
||||
import Capacitor
|
||||
import WatchConnectivity
|
||||
|
||||
@objc(CoachLiveBridgePlugin)
|
||||
public class CoachLiveBridgePlugin: CAPPlugin, CAPBridgedPlugin {
|
||||
public let identifier = "CoachLiveBridgePlugin"
|
||||
public let jsName = "CoachLiveBridge"
|
||||
public let pluginMethods: [CAPPluginMethod] = [
|
||||
CAPPluginMethod(name: "getStatus", returnType: CAPPluginReturnPromise)
|
||||
]
|
||||
|
||||
override public func load() {
|
||||
super.load()
|
||||
guard WCSession.isSupported() else {
|
||||
CAPLog.print("⚡️ CoachLiveBridge: WCSession non supporté sur cet appareil")
|
||||
return
|
||||
}
|
||||
let session = WCSession.default
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
|
||||
@objc func getStatus(_ call: CAPPluginCall) {
|
||||
let session = WCSession.default
|
||||
call.resolve([
|
||||
"supported": WCSession.isSupported(),
|
||||
"activated": session.activationState == .activated,
|
||||
"paired": session.isPaired,
|
||||
"watchAppInstalled": session.isWatchAppInstalled,
|
||||
"reachable": session.isReachable
|
||||
])
|
||||
}
|
||||
|
||||
private func emit(_ sample: [String: Any]) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.notifyListeners("liveSample", data: sample)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CoachLiveBridgePlugin: WCSessionDelegate {
|
||||
public func session(_ session: WCSession,
|
||||
activationDidCompleteWith activationState: WCSessionActivationState,
|
||||
error: Error?) {}
|
||||
|
||||
public func sessionDidBecomeInactive(_ session: WCSession) {}
|
||||
|
||||
public func sessionDidDeactivate(_ session: WCSession) {
|
||||
// Réactiver pour les sessions futures (ex. changement d'Apple Watch).
|
||||
WCSession.default.activate()
|
||||
}
|
||||
|
||||
// Temps réel : un sample envoyé via sendMessage.
|
||||
public func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
|
||||
emit(message)
|
||||
}
|
||||
|
||||
// Livraison différée : lot de samples envoyé via transferUserInfo.
|
||||
public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
|
||||
if let samples = userInfo["samples"] as? [[String: Any]] {
|
||||
for sample in samples { emit(sample) }
|
||||
} else {
|
||||
emit(userInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,10 @@ class MainViewController: CAPBridgeViewController {
|
||||
// Plugin natif observer HealthKit — détecte fin de séance Watch et
|
||||
// push notif locale "🎉 Séance terminée" (cf. CoachWorkoutObserver.swift).
|
||||
bridge?.registerPluginInstance(CoachWorkoutObserverPlugin())
|
||||
// Plugin natif live workout — reçoit les samples temps réel de l'app
|
||||
// watchOS CoachWatch via WatchConnectivity, relaie au JS (event
|
||||
// "liveSample") pour la page /live (cf. CoachLiveBridge.swift).
|
||||
bridge?.registerPluginInstance(CoachLiveBridgePlugin())
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
Reference in New Issue
Block a user