87 lines
3.5 KiB
Swift
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
|