#if os(macOS) import Foundation import SystemExtensions public class SystemExtension: NSObject, OSSystemExtensionRequestDelegate { private let forceUpdate: Bool private let inBackground: Bool private let semaphore = DispatchSemaphore(value: 0) private var result: OSSystemExtensionRequest.Result? private var properties: [OSSystemExtensionProperties]? private var error: Error? private init(_ forceUpdate: Bool = false, _ inBackground: Bool = false) { self.forceUpdate = forceUpdate self.inBackground = inBackground } public func request(_: OSSystemExtensionRequest, actionForReplacingExtension existing: OSSystemExtensionProperties, withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction { if forceUpdate { return .replace } if existing.isAwaitingUserApproval, !inBackground { return .replace } if existing.bundleIdentifier == ext.bundleIdentifier, existing.bundleVersion == ext.bundleVersion, existing.bundleShortVersion == ext.bundleShortVersion { NSLog("Skip update system extension") return .cancel } else { NSLog("Update system extension") return .replace } } public func requestNeedsUserApproval(_: OSSystemExtensionRequest) { semaphore.signal() } public func request(_: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) { self.result = result semaphore.signal() } public func request(_: OSSystemExtensionRequest, didFailWithError error: Error) { self.error = error semaphore.signal() } public func request(_: OSSystemExtensionRequest, foundProperties properties: [OSSystemExtensionProperties]) { self.properties = properties semaphore.signal() } public func activation() throws -> OSSystemExtensionRequest.Result? { let request = OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: FilePath.packageName + ".system", queue: .main) request.delegate = self OSSystemExtensionManager.shared.submitRequest(request) semaphore.wait() if let error { throw error } return result } public func deactivation() throws -> OSSystemExtensionRequest.Result? { let request = OSSystemExtensionRequest.deactivationRequest(forExtensionWithIdentifier: FilePath.packageName + ".system", queue: .main) request.delegate = self OSSystemExtensionManager.shared.submitRequest(request) semaphore.wait() if let error { throw error } return result } public func getProperties() throws -> [OSSystemExtensionProperties] { let request = OSSystemExtensionRequest.propertiesRequest(forExtensionWithIdentifier: FilePath.packageName + ".system", queue: .main) request.delegate = self OSSystemExtensionManager.shared.submitRequest(request) semaphore.wait() if let error { throw error } return properties! } public static func isInstalled() async -> Bool { await (try? Task { try await isInstalledBackground() }.result.get()) == true } public nonisolated static func isInstalledBackground() async throws -> Bool { for _ in 0 ..< 3 { do { let propList = try SystemExtension().getProperties() if propList.isEmpty { return false } for extensionProp in propList { if !extensionProp.isAwaitingUserApproval, !extensionProp.isUninstalling { return true } } } catch { try await Task.sleep(nanoseconds: NSEC_PER_SEC) } } return false } public nonisolated static func install(forceUpdate: Bool = false, inBackground: Bool = false) async throws -> OSSystemExtensionRequest.Result? { try await Task.detached { try SystemExtension(forceUpdate, inBackground).activation() }.result.get() } public nonisolated static func uninstall() async throws -> OSSystemExtensionRequest.Result? { try await Task.detached { try SystemExtension().deactivation() }.result.get() } } #endif