Integrate live phone and watch remote prototype with guarded motion control

This commit is contained in:
2026-09-18 19:11:52 +04:00
parent 3e787543c0
commit 8131f4f873
54 changed files with 4355 additions and 329 deletions
@@ -1,4 +1,5 @@
import Foundation
import CoreFoundation
import RemoteCore
private func relayNSNumberIsBoolean(_ value: NSNumber) -> Bool {
@@ -88,22 +89,24 @@ public struct RelayApplicationContext: Sendable, Equatable {
public let schemaVersion: Int
public let selectedTelevisionID: UUID?
public let displayName: String?
public let isConnectionReady: Bool
public init(selectedTelevisionID: UUID?, displayName: String?) {
public init(selectedTelevisionID: UUID?, displayName: String?, isConnectionReady: Bool = false) {
self.schemaVersion = RelayWire.schemaVersion
self.selectedTelevisionID = selectedTelevisionID
self.displayName = displayName
self.isConnectionReady = isConnectionReady && selectedTelevisionID != nil
}
public func dictionary() throws -> [String: Any] {
var result: [String: Any] = ["schemaVersion": schemaVersion]
var result: [String: Any] = ["schemaVersion": schemaVersion, "isConnectionReady": isConnectionReady]
if let selectedTelevisionID { result["selectedTelevisionID"] = selectedTelevisionID.uuidString }
if let displayName { result["displayName"] = String(displayName.prefix(128)) }
return result
}
public init(dictionary: [String: Any]) throws {
let allowed: Set<String> = ["schemaVersion", "selectedTelevisionID", "displayName"]
let allowed: Set<String> = ["schemaVersion", "selectedTelevisionID", "displayName", "isConnectionReady"]
guard Set(dictionary.keys).isSubset(of: allowed) else { throw RelayContainerError.extraKeys }
guard let version = dictionary["schemaVersion"] as? NSNumber,
relayNSNumberIsInteger(version),
@@ -127,6 +130,12 @@ public struct RelayApplicationContext: Sendable, Equatable {
} else {
self.displayName = nil
}
if let raw = dictionary["isConnectionReady"] {
guard let number = raw as? NSNumber, CFGetTypeID(number) == CFBooleanGetTypeID() else {
throw RelayContainerError.invalidPayload
}
self.isConnectionReady = number.boolValue && selectedTelevisionID != nil
} else { self.isConnectionReady = false }
self.schemaVersion = version.intValue
}
}
@@ -168,12 +177,13 @@ public final class RelayReplyOnce: @unchecked Sendable {
/// a delegate context. This explicit boundary lets the native delegate hop to
/// the main actor without pretending the dictionary itself is Sendable.
public final class RelayInboundMessage: @unchecked Sendable {
public let message: [String: Any]
private let container: RelayContainer?
public var message: [String: Any] { container?.dictionary() ?? [:] }
public let reply: RelayReplyOnce
public init(message: [String: Any],
replyHandler: @escaping ([String: Any]) -> Void) {
self.message = message
self.container = try? RelayContainer(dictionary: message)
self.reply = RelayReplyOnce(replyHandler)
}
}