#if os(watchOS) import Foundation import RemoteCore import WatchBridgeCommon import WatchConnectivity @MainActor private final class WatchWCSessionSending: RelaySessionSending { private let session: WCSession init(session: WCSession) { self.session = session } var activationState: RelayActivationState { switch session.activationState { case .notActivated: return .notActivated case .inactive: return .inactive case .activated: return .activated @unknown default: return .inactive } } var isReachable: Bool { session.isReachable } func activate() { session.activate() } func sendMessage(_ message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void, errorHandler: @escaping (Error) -> Void) { session.sendMessage(message, replyHandler: replyHandler, errorHandler: errorHandler) } func updateApplicationContext(_ context: [String: Any]) throws { try session.updateApplicationContext(context) } } /// Separate watchOS delegate. The app remains foreground/active for gesture /// use; this delegate does not add a background mode or deferred command queue. @MainActor public final class WatchWCSessionDelegate: NSObject, WCSessionDelegate { public let bridge: WatchRelayBridge public var onContext: ((RelayApplicationContext) -> Void)? public private(set) var latestContext: RelayApplicationContext? private let session: WCSession public init(session: WCSession = .default) { self.session = session bridge = WatchRelayBridge(session: WatchWCSessionSending(session: session)) super.init() session.delegate = self bridge.activate() } nonisolated public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { Task { @MainActor [weak self] in guard let self else { return } if case .inactive = self.bridge.state { } else { self.bridge.didDeactivate() } self.bridge.activationDidComplete(error: error) self.receiveContext(self.session.receivedApplicationContext) } } private func receiveContext(_ dictionary: [String: Any]) { guard let context = try? RelayApplicationContext(dictionary: dictionary) else { return } latestContext = context onContext?(context) } nonisolated public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) { guard let context = try? RelayApplicationContext(dictionary: applicationContext) else { return } Task { @MainActor [weak self] in self?.latestContext = context self?.onContext?(context) } } nonisolated public func sessionReachabilityDidChange(_ session: WCSession) { Task { @MainActor [weak self] in self?.bridge.reachabilityDidChange() } } nonisolated public func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) { // The watch is the request sender in v1; never execute a phone reply // as a request. Complete unexpected inbound traffic exactly once. RelayReplyOnce(replyHandler).reply( relayErrorReply(RelayFailure(code: .malformedRequest))) } } #endif