WatchConnectivityService.swift 5.4 KB
import Foundation
import UIKit
import WatchConnectivity

/// iPhone-side WatchConnectivity wrapper.
/// This keeps Flutter-facing WearEngine APIs mapped to Apple Watch behavior.
final class WatchConnectivityService: NSObject {
  static let shared = WatchConnectivityService()

  private(set) var isPaired = false
  private(set) var isReachable = false
  private(set) var isWatchAppInstalled = false

  private override init() {
    super.init()
  }

  @discardableResult
  func activate() -> Bool {
    guard WCSession.isSupported() else { return false }
    let session = WCSession.default
    session.delegate = self
    if session.activationState == .notActivated {
      session.activate()
    }
    refreshState()
    return true
  }

  func refreshState() {
    guard WCSession.isSupported() else {
      isPaired = false
      isReachable = false
      isWatchAppInstalled = false
      return
    }
    let session = WCSession.default
    isPaired = session.isPaired
    isReachable = session.isReachable
    isWatchAppInstalled = session.isWatchAppInstalled
  }

  func hasAvailableDevices() -> Bool {
    activate()
    refreshState()
    return isPaired && isWatchAppInstalled
  }

  func currentDeviceInfo() -> WearDeviceInfo? {
    activate()
    refreshState()
    guard isPaired else { return nil }
    return WearDeviceInfo(
      deviceId: nil,
      deviceName: "Apple Watch",
      isConnected: isReachable && isWatchAppInstalled
    )
  }

    func transferLoginInfo(userInfo: UserInfoModel?){
        guard activate() else { return }
        let params = loginPayload(userInfo: userInfo)
        do{
            try WCSession.default.updateApplicationContext(params)
            print("Send Watch updateApplicationContext: \(params)")
        }catch{
            print("Error Send Watch updateApplicationContext: \(params)")
        }
    }

    func sendLoginInfoMessage(userInfo: UserInfoModel?) -> Bool {
        guard let userInfo else {
            return sendCommandMessage(AppGroupMessageKey.logout)
        }

        guard let json = userInfo.jsonString else {
            return false
        }
        return sendCommandMessage(AppGroupMessageKey.login, json: json)
    }

    private func loginPayload(userInfo: UserInfoModel?) -> [String: Any] {
        guard var params = userInfo?.toJson(), !params.isEmpty else {
            return ["command": AppGroupMessageKey.logout]
        }
        params["command"] = AppGroupMessageKey.login
        return params
    }

  func sendTextMessage(_ message: String) -> Bool {
    activate()
    guard WCSession.default.isReachable else { return false }
    WCSession.default.sendMessage(["message": message], replyHandler: nil) { error in
      print("Watch message failed: \(error.localizedDescription)")
    }
    return true
  }

    func sendCommandMessage(_ message: String, json: String? = nil) -> Bool {
      activate()
      guard WCSession.default.isReachable else { return false }
        var params: [String: Any] = [
            "command": message
        ]
        params["json"] = json

      WCSession.default.sendMessage(params, replyHandler: nil) { error in
        print("Watch message send failed: \(error.localizedDescription)")
      }
      return true
    }

    func sendWatchThemeChangedMessage(json: String) -> Bool{
        sendCommandMessage(AppGroupMessageKey.reloadTheme, json: json)
    }
}

extension WatchConnectivityService: WCSessionDelegate {
  func session(
    _ session: WCSession,
    activationDidCompleteWith activationState: WCSessionActivationState,
    error: Error?
  ) {
    refreshState()
    if let error {
      print("WatchConnectivity activation failed: \(error.localizedDescription)")
    }else{
        transferLoginInfo(userInfo: AppShared.shared.myUserInfo)
    }
  }

  func sessionDidBecomeInactive(_ session: WCSession) {
    refreshState()
  }

  func sessionDidDeactivate(_ session: WCSession) {
    refreshState()
    session.activate()
  }

  func sessionReachabilityDidChange(_ session: WCSession) {
    refreshState()
    if session.isReachable {
      _ = sendLoginInfoMessage(userInfo: AppShared.shared.myUserInfo)
    }
  }

  func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
    if message["command"] as? String == AppGroupMessageKey.watchDeviceToken,
       let token = message["token"] as? String {
      AppGroupConstants.defaults?.set(token, forKey: AppGroupConstants.Key.watchDeviceToken)
      Task {
        await AppShared.shared.reportDeviceInfo()
      }
    }
  }

  func session(
    _ session: WCSession,
    didReceiveMessage message: [String: Any],
    replyHandler: @escaping ([String: Any]) -> Void
  ) {
    if message["command"] as? String == AppGroupMessageKey.requestLoginState {
      let userInfo = AppShared.shared.myUserInfo
      var reply = loginPayload(userInfo: userInfo)
      if let userInfo, let json = userInfo.jsonString {
        reply["json"] = json
      }
      replyHandler(reply)
    } else if message["command"] as? String == AppGroupMessageKey.watchDeviceToken,
              let token = message["token"] as? String {
      AppGroupConstants.defaults?.set(token, forKey: AppGroupConstants.Key.watchDeviceToken)
      Task {
        await AppShared.shared.reportDeviceInfo()
      }
      replyHandler(["success": true])
    } else if message["command"] as? String == AppGroupMessageKey.statusPulseRefresh {
      replyHandler(["success": true])
    } else {
      replyHandler(["success": true])
    }
  }
}