105 lines
3.8 KiB
Swift
105 lines
3.8 KiB
Swift
import Foundation
|
|
import WatchConnectivity
|
|
|
|
class PhoneConnector: NSObject, ObservableObject, WCSessionDelegate {
|
|
static let shared = PhoneConnector()
|
|
var workoutManager: WorkoutManager? {
|
|
didSet {
|
|
if let pending = pendingWorkouts {
|
|
pending.forEach { workoutManager?.addWorkoutFromPhone($0) }
|
|
pendingWorkouts = nil
|
|
}
|
|
}
|
|
}
|
|
private var pendingWorkouts: [StoredWorkout]?
|
|
|
|
private override init() {
|
|
super.init()
|
|
}
|
|
|
|
// Send completed workout data to phone
|
|
static func sendCompletedWorkout(_ laps: [[String: Any]], duration: TimeInterval, workoutId: String) {
|
|
let message: [String: Any] = [
|
|
"type": "workoutCompleted",
|
|
"workoutId": workoutId,
|
|
"laps": laps,
|
|
"duration": duration,
|
|
]
|
|
|
|
if WCSession.default.isReachable {
|
|
WCSession.default.sendMessage(message, replyHandler: nil) { error in
|
|
print("[PhoneConnector] sendMessage failed, falling back to transferUserInfo: \(error)")
|
|
WCSession.default.transferUserInfo(message)
|
|
}
|
|
} else {
|
|
WCSession.default.transferUserInfo(message)
|
|
}
|
|
}
|
|
|
|
// MARK: - WCSessionDelegate
|
|
|
|
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
|
|
print("[PhoneConnector] Activated: state=\(activationState.rawValue), isReachable=\(session.isReachable)")
|
|
}
|
|
|
|
func sessionReachabilityDidChange(_ session: WCSession) {
|
|
print("[PhoneConnector] Reachability changed: isReachable=\(session.isReachable)")
|
|
}
|
|
|
|
func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
|
|
print("[PhoneConnector] didReceiveMessage (no reply)")
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.handleMessage(message)
|
|
}
|
|
}
|
|
|
|
func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) {
|
|
print("[PhoneConnector] didReceiveMessage (with reply)")
|
|
replyHandler(["status": "received"])
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.handleMessage(message)
|
|
}
|
|
}
|
|
|
|
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any]) {
|
|
print("[PhoneConnector] didReceiveUserInfo")
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.handleMessage(userInfo)
|
|
}
|
|
}
|
|
|
|
private func handleMessage(_ message: [String: Any]) {
|
|
guard let type = message["type"] as? String else {
|
|
print("[PhoneConnector] No type in message: \(message.keys)")
|
|
return
|
|
}
|
|
|
|
print("[PhoneConnector] Received message type: \(type)")
|
|
|
|
switch type {
|
|
case "workoutPlan":
|
|
guard let data = try? JSONSerialization.data(withJSONObject: message) else {
|
|
print("[PhoneConnector] Failed to serialize message")
|
|
return
|
|
}
|
|
print("[PhoneConnector] JSON data: \(String(data: data, encoding: .utf8) ?? "nil")")
|
|
|
|
do {
|
|
let workout = try JSONDecoder().decode(StoredWorkout.self, from: data)
|
|
print("[PhoneConnector] Decoded workout: \(workout.name ?? "nil"), sets: \(workout.sets.count)")
|
|
if let manager = workoutManager {
|
|
manager.addWorkoutFromPhone(workout)
|
|
} else {
|
|
print("[PhoneConnector] workoutManager not set, queuing workout")
|
|
if pendingWorkouts == nil { pendingWorkouts = [] }
|
|
pendingWorkouts?.append(workout)
|
|
}
|
|
} catch {
|
|
print("[PhoneConnector] Decode error: \(error)")
|
|
}
|
|
default:
|
|
print("[PhoneConnector] Unknown type: \(type)")
|
|
}
|
|
}
|
|
}
|