// // XPCConvertible.swift // kordophone2 // // Created by James Magahern on 8/24/25. // import Foundation import XPC protocol XPCConvertible { static func fromXPC(_ value: xpc_object_t) -> Self? } extension String: XPCConvertible { static func fromXPC(_ value: xpc_object_t) -> String? { guard xpc_get_type(value) == XPC_TYPE_STRING, let cstr = xpc_string_get_string_ptr(value) else { return nil } return String(cString: cstr) } } extension Int: XPCConvertible { static func fromXPC(_ value: xpc_object_t) -> Int? { switch xpc_get_type(value) { case XPC_TYPE_INT64: return Int(xpc_int64_get_value(value)) case XPC_TYPE_UINT64: return Int(xpc_uint64_get_value(value)) case XPC_TYPE_STRING: if let cstr = xpc_string_get_string_ptr(value) { return Int(String(cString: cstr)) } default: return nil } return nil } } extension Date: XPCConvertible { static func fromXPC(_ value: xpc_object_t) -> Date? { // Accept seconds since epoch as int/uint or string if let seconds: Int = Int.fromXPC(value) { return Date(timeIntervalSince1970: TimeInterval(seconds)) } return nil } } extension Array: XPCConvertible where Element: XPCConvertible { static func fromXPC(_ value: xpc_object_t) -> [Element]? { guard xpc_get_type(value) == XPC_TYPE_ARRAY else { return nil } var result: [Element] = [] xpc_array_apply(value) { _, item in if let element = Element.fromXPC(item) { result.append(element) } return true } return result } } extension xpc_object_t { func getObject(_ key: String) -> xpc_object_t? { var raw: xpc_object_t? key.withCString { cKey in raw = xpc_dictionary_get_value(self, cKey) } return raw } func get(_ key: String) -> T? { var raw: xpc_object_t? key.withCString { cKey in raw = xpc_dictionary_get_value(self, cKey) } guard let value = raw else { return nil } return T.fromXPC(value) } subscript(key: String) -> T? { return get(key) } var isDictionary: Bool { xpc_get_type(self) == XPC_TYPE_DICTIONARY } var isArray: Bool { xpc_get_type(self) == XPC_TYPE_ARRAY } } // MARK: - Dictionary wrapper struct XPCDictionary { let raw: xpc_object_t init?(_ value: xpc_object_t) { guard xpc_get_type(value) == XPC_TYPE_DICTIONARY else { return nil } self.raw = value } func object(_ key: String) -> xpc_object_t? { var rawValue: xpc_object_t? key.withCString { cKey in rawValue = xpc_dictionary_get_value(raw, cKey) } return rawValue } func get(_ key: String) -> T? { guard let value = object(key) else { return nil } return T.fromXPC(value) } subscript(_ key: String) -> T? { return get(key) } } extension XPCDictionary { static func wrap(_ value: xpc_object_t) -> XPCDictionary? { return XPCDictionary(value) } }