refactor: Fix macOS standalone application
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
#if os(macOS)
|
||||
import Foundation
|
||||
import Libbox
|
||||
import os
|
||||
|
||||
private let logger = Logger(category: "UserServiceXPC")
|
||||
|
||||
@objc public protocol UserServiceProtocol {
|
||||
func getWIFIState(reply: @escaping (String?, String?, NSError?) -> Void)
|
||||
func sendNotification(
|
||||
identifier: String,
|
||||
typeName: String,
|
||||
typeID: Int32,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
body: String,
|
||||
openURL: String,
|
||||
reply: @escaping (NSError?) -> Void
|
||||
)
|
||||
}
|
||||
|
||||
public class UserServiceClient {
|
||||
public static let shared = UserServiceClient()
|
||||
|
||||
private var connection: NSXPCConnection?
|
||||
private let connectionLock = NSLock()
|
||||
|
||||
private init() {}
|
||||
|
||||
private func getConnection() -> NSXPCConnection? {
|
||||
connectionLock.lock()
|
||||
defer { connectionLock.unlock() }
|
||||
|
||||
if let existing = connection {
|
||||
return existing
|
||||
}
|
||||
|
||||
guard let endpoint = UserServiceEndpointRegistry.shared.get() else {
|
||||
logger.error("UserService endpoint unavailable")
|
||||
return nil
|
||||
}
|
||||
|
||||
let newConnection = NSXPCConnection(listenerEndpoint: endpoint)
|
||||
newConnection.remoteObjectInterface = NSXPCInterface(with: UserServiceProtocol.self)
|
||||
|
||||
newConnection.invalidationHandler = { [weak self] in
|
||||
guard let self else { return }
|
||||
connectionLock.lock()
|
||||
connection = nil
|
||||
connectionLock.unlock()
|
||||
}
|
||||
|
||||
newConnection.resume()
|
||||
connection = newConnection
|
||||
return newConnection
|
||||
}
|
||||
|
||||
private func getProxy() -> UserServiceProtocol? {
|
||||
guard let conn = getConnection() else {
|
||||
return nil
|
||||
}
|
||||
guard let proxy = conn.remoteObjectProxyWithErrorHandler { [weak self] error in
|
||||
guard let self else { return }
|
||||
logger.error("UserService XPC error: \(error.localizedDescription)")
|
||||
connectionLock.lock()
|
||||
connection = nil
|
||||
connectionLock.unlock()
|
||||
} as? UserServiceProtocol else {
|
||||
connectionLock.lock()
|
||||
connection = nil
|
||||
connectionLock.unlock()
|
||||
conn.invalidate()
|
||||
return nil
|
||||
}
|
||||
return proxy
|
||||
}
|
||||
|
||||
private func performXPCCallVoid(
|
||||
_ operation: String,
|
||||
call: (UserServiceProtocol, @escaping (NSError?) -> Void) -> Void
|
||||
) throws {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
var resultError: NSError?
|
||||
|
||||
guard let proxy = getProxy() else {
|
||||
throw NSError(domain: "UserService", code: -1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "UserService connection unavailable",
|
||||
])
|
||||
}
|
||||
|
||||
call(proxy) { error in
|
||||
resultError = error
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
let deadline = DispatchTime.now() + .seconds(5)
|
||||
if semaphore.wait(timeout: deadline) == .timedOut {
|
||||
let error = NSError(domain: "UserService", code: -1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "\(operation) request timeout",
|
||||
])
|
||||
logger.error("\(operation): timeout")
|
||||
throw error
|
||||
}
|
||||
|
||||
if let error = resultError {
|
||||
logger.error("\(operation) error: \(error.localizedDescription)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public func readWIFIState() -> LibboxWIFIState? {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
var resultSSID: String?
|
||||
var resultBSSID: String?
|
||||
|
||||
guard let proxy = getProxy() else {
|
||||
logger.error("readWIFIState: no UserService connection")
|
||||
return nil
|
||||
}
|
||||
|
||||
proxy.getWIFIState { ssid, bssid, error in
|
||||
if let error {
|
||||
logger.error("readWIFIState error: \(error.localizedDescription)")
|
||||
} else {
|
||||
resultSSID = ssid
|
||||
resultBSSID = bssid
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
let timeout = DispatchTime.now() + .seconds(5)
|
||||
if semaphore.wait(timeout: timeout) == .timedOut {
|
||||
logger.error("readWIFIState: timeout")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let ssid = resultSSID, let bssid = resultBSSID else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return LibboxWIFIState(ssid, wifiBSSID: bssid)
|
||||
}
|
||||
|
||||
public func sendNotification(_ notification: LibboxNotification) throws {
|
||||
try performXPCCallVoid("sendNotification") { proxy, reply in
|
||||
proxy.sendNotification(
|
||||
identifier: notification.identifier,
|
||||
typeName: notification.typeName,
|
||||
typeID: notification.typeID,
|
||||
title: notification.title,
|
||||
subtitle: notification.subtitle,
|
||||
body: notification.body,
|
||||
openURL: notification.openURL,
|
||||
reply: reply
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user