Files
UUVPN/iOS-SwiftUI-Code/Library/Network/SystemExtension.swift
T

127 lines
4.8 KiB
Swift

#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