Files
wristivo/Adapters/WatchBridge/Sources/Watch/WatchWCSessionDelegate.swift
T

87 lines
3.5 KiB
Swift

#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