diff --git a/TophatKit/Sources/TophatKit/ArtifactProvider.swift b/TophatKit/Sources/TophatKit/ArtifactProvider.swift index d6bfead..2252e73 100644 --- a/TophatKit/Sources/TophatKit/ArtifactProvider.swift +++ b/TophatKit/Sources/TophatKit/ArtifactProvider.swift @@ -35,6 +35,9 @@ public protocol ArtifactProvider { /// /// Throw any errors if they ocurred. Use any parameters wrapped with ``Parameter`` to /// collect inputs from Tophat to implement the retrieval mechanism. + /// + /// To display an error message in the user interface, conform thrown errors to the + /// `LocalizedError` protocol. /// - Returns: A ``ArtifactProviderResult`` containing the output. func retrieve() async throws -> Result diff --git a/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCGenericLocalizedError.swift b/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCGenericLocalizedError.swift new file mode 100644 index 0000000..f557e7d --- /dev/null +++ b/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCGenericLocalizedError.swift @@ -0,0 +1,28 @@ +// +// ExtensionXPCGenericLocalizedError.swift +// TophatKit +// +// Created by Lukas Romsicki on 2024-11-27. +// + +import Foundation + +struct ExtensionXPCGenericLocalizedError: LocalizedError { + let errorDescription: String? + let failureReason: String? + let recoverySuggestion: String? + let helpAnchor: String? + + init?(nsError: NSError) { + let userInfo = nsError.userInfo + + self.errorDescription = userInfo["errorDescription"] as? String + self.failureReason = userInfo["failureReason"] as? String + self.recoverySuggestion = userInfo["recoverySuggestion"] as? String + self.helpAnchor = userInfo["helpAnchor"] as? String + + guard errorDescription != nil || failureReason != nil || recoverySuggestion != nil || helpAnchor != nil else { + return nil + } + } +} diff --git a/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCReceivedMessage.swift b/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCReceivedMessage.swift index d892caa..8e6b423 100644 --- a/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCReceivedMessage.swift +++ b/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCReceivedMessage.swift @@ -39,7 +39,8 @@ struct ExtensionXPCReceivedMessageContainer { replyHandler(nil, error) } case .failure(let error): - replyHandler(nil, error) + // The error will be an NSError over XPC anyway. + replyHandler(nil, NSError(embeddingLocalizedDescriptionsFrom: error)) } } } diff --git a/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCSession.swift b/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCSession.swift index 923d801..268d115 100644 --- a/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCSession.swift +++ b/TophatKit/Sources/TophatKit/Internal/XPC/ExtensionXPCSession.swift @@ -82,7 +82,10 @@ extension ExtensionXPCSession { } service.send(identifier: message.identifier, data: dataToSend) { dataFromReply, error in - if let error { + if let nsError = error as? NSError, let localizedError = ExtensionXPCGenericLocalizedError(nsError: nsError) { + continuation.resume(throwing: localizedError) + return + } else if let error { continuation.resume(throwing: error) return } diff --git a/TophatKit/Sources/TophatKit/Internal/XPC/NSError+Extensions.swift b/TophatKit/Sources/TophatKit/Internal/XPC/NSError+Extensions.swift new file mode 100644 index 0000000..df29cb5 --- /dev/null +++ b/TophatKit/Sources/TophatKit/Internal/XPC/NSError+Extensions.swift @@ -0,0 +1,24 @@ +// +// NSError+Extensions.swift +// TophatKit +// +// Created by Lukas Romsicki on 2024-11-27. +// + +import Foundation + +extension NSError { + convenience init(embeddingLocalizedDescriptionsFrom error: Error) { + let nsError = error as NSError + var newUserInfo = nsError.userInfo + + if let localizedError = error as? LocalizedError { + newUserInfo["errorDescription"] = localizedError.errorDescription + newUserInfo["failureReason"] = localizedError.failureReason + newUserInfo["recoverySuggestion"] = localizedError.recoverySuggestion + newUserInfo["helpAnchor"] = localizedError.helpAnchor + } + + self.init(domain: nsError.domain, code: nsError.code, userInfo: newUserInfo) + } +}