From 126a4cc55f9aae4c37bc717bbf1545a64ceac48f Mon Sep 17 00:00:00 2001 From: James Magahern Date: Sun, 24 Aug 2025 18:54:50 -0700 Subject: [PATCH] Get swifty --- kordophone2/Models.swift | 25 +++++----- kordophone2/XPC/XPCClient.swift | 74 ++++++++++++++-------------- kordophone2/XPC/XPCConvertible.swift | 48 +++++++++++++++++- 3 files changed, 94 insertions(+), 53 deletions(-) diff --git a/kordophone2/Models.swift b/kordophone2/Models.swift index 9811c88..a145218 100644 --- a/kordophone2/Models.swift +++ b/kordophone2/Models.swift @@ -93,16 +93,13 @@ enum Serialized init?(xpc dict: xpc_object_t) { - guard let g: String = dict["guid"] else { return nil } + guard let d = XPCDictionary(dict), let g: String = d["guid"] else { return nil } - let dn: String? = dict["display_name"] - let lmp: String? = dict["last_message_preview"] - - let names: [String] = dict["participants"] ?? [] - - let unread: Int = dict["unread_count"] ?? 0 - - let dt: Date = dict["date"] ?? Date(timeIntervalSince1970: 0) + let dn: String? = d["display_name"] + let lmp: String? = d["last_message_preview"] + let names: [String] = d["participants"] ?? [] + let unread: Int = d["unread_count"] ?? 0 + let dt: Date = d["date"] ?? Date(timeIntervalSince1970: 0) self.guid = g self.displayName = dn @@ -122,16 +119,16 @@ enum Serialized init?(xpc dict: xpc_object_t) { - guard let g: String = dict["id"] else { return nil } + guard let d = XPCDictionary(dict), let g: String = d["id"] else { return nil } - let s: String = dict["sender"] ?? "" - let t: String = dict["text"] ?? "" - let d: Date = dict["date"] ?? Date(timeIntervalSince1970: 0) + let s: String = d["sender"] ?? "" + let t: String = d["text"] ?? "" + let dd: Date = d["date"] ?? Date(timeIntervalSince1970: 0) self.guid = g self.sender = s self.text = t - self.date = d + self.date = dd } } } diff --git a/kordophone2/XPC/XPCClient.swift b/kordophone2/XPC/XPCClient.swift index b9a0b56..b3ae6cb 100644 --- a/kordophone2/XPC/XPCClient.swift +++ b/kordophone2/XPC/XPCClient.swift @@ -182,48 +182,48 @@ extension XPCClient } private func handleIncomingXPCEvent(_ event: xpc_object_t) { - let type = xpc_get_type(event) - switch type { - case XPC_TYPE_DICTIONARY: - guard let namePtr = xpc_dictionary_get_string(event, "name") else { return } - let name = String(cString: namePtr) + switch xpc_get_type(event) { + case XPC_TYPE_DICTIONARY: + guard let eventDict = XPCDictionary(event), let name: String = eventDict["name"] else { return } - var signal: Signal? - if name == "ConversationsUpdated" { - signal = .conversationsUpdated - } else if name == "MessagesUpdated" { - if let args = xpc_dictionary_get_value(event, "arguments"), xpc_get_type(args) == XPC_TYPE_DICTIONARY, - let cidPtr = xpc_dictionary_get_string(args, "conversation_id") { - signal = .messagesUpdated(conversationId: String(cString: cidPtr)) + let args = eventDict.object("arguments").flatMap { XPCDictionary($0) } + let signal: Signal? = { + switch name { + case "ConversationsUpdated": + return .conversationsUpdated + case "MessagesUpdated": + if let args, let cid: String = args["conversation_id"] { return .messagesUpdated(conversationId: cid) } + return nil + case "AttachmentDownloadCompleted": + if let args, let aid: String = args["attachment_id"] { return .attachmentDownloaded(attachmentId: aid) } + return nil + case "AttachmentUploadCompleted": + if let args, + let uploadGuid: String = args["upload_guid"], + let attachmentGuid: String = args["attachment_guid"] { + return .attachmentUploaded(uploadGuid: uploadGuid, attachmentGuid: attachmentGuid) } - } else if name == "AttachmentDownloadCompleted" { - if let args = xpc_dictionary_get_value(event, "arguments"), xpc_get_type(args) == XPC_TYPE_DICTIONARY, - let aidPtr = xpc_dictionary_get_string(args, "attachment_id") { - signal = .attachmentDownloaded(attachmentId: String(cString: aidPtr)) - } - } else if name == "AttachmentUploadCompleted" { - if let args = xpc_dictionary_get_value(event, "arguments"), xpc_get_type(args) == XPC_TYPE_DICTIONARY, - let ugPtr = xpc_dictionary_get_string(args, "upload_guid"), - let agPtr = xpc_dictionary_get_string(args, "attachment_guid") { - signal = .attachmentUploaded(uploadGuid: String(cString: ugPtr), attachmentGuid: String(cString: agPtr)) - } - } else if name == "UpdateStreamReconnected" { - signal = .updateStreamReconnected + return nil + case "UpdateStreamReconnected": + return .updateStreamReconnected + default: + return nil } + }() - if let signal { - signalLock.lock() - let sinks = signalSinks.values - signalLock.unlock() - for sink in sinks { sink(signal) } - } + if let signal { + signalLock.lock() + let sinks = signalSinks.values + signalLock.unlock() + for sink in sinks { sink(signal) } + } - case XPC_TYPE_ERROR: - if let errStr = xpc_string_get_string_ptr(event) { - print("XPC event error: \(String(cString: errStr))") - } - default: - break + case XPC_TYPE_ERROR: + if let errStr = xpc_string_get_string_ptr(event) { + print("XPC event error: \(String(cString: errStr))") + } + default: + break } } } diff --git a/kordophone2/XPC/XPCConvertible.swift b/kordophone2/XPC/XPCConvertible.swift index 1613273..4dd164b 100644 --- a/kordophone2/XPC/XPCConvertible.swift +++ b/kordophone2/XPC/XPCConvertible.swift @@ -78,6 +78,15 @@ extension Array: XPCConvertible where Element: XPCConvertible 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 @@ -88,7 +97,42 @@ extension xpc_object_t return T.fromXPC(value) } - subscript(key: String) -> T? { - return get(key) + 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) } }