Store wasOnDemandEnabled flag in providerConfiguration so the widget can restore On Demand state on next start. Clear the flag when On Demand is explicitly disabled in settings or when starting from the main app.
90 lines
3.0 KiB
Swift
90 lines
3.0 KiB
Swift
import Foundation
|
|
import NetworkExtension
|
|
|
|
enum WidgetAppConfiguration {
|
|
static let packageName: String = {
|
|
guard let value = Bundle.main.object(forInfoDictionaryKey: "BasePackageIdentifier") as? String else {
|
|
fatalError("Missing BasePackageIdentifier in Info.plist")
|
|
}
|
|
return value
|
|
}()
|
|
|
|
static let appGroupID: String = {
|
|
guard let value = Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String else {
|
|
fatalError("Missing AppGroupIdentifier in Info.plist")
|
|
}
|
|
return value
|
|
}()
|
|
|
|
static var widgetControlKind: String {
|
|
"\(packageName).widget.ServiceToggle"
|
|
}
|
|
}
|
|
|
|
extension NEVPNStatus {
|
|
var isStarted: Bool {
|
|
switch self {
|
|
case .connecting, .connected, .reasserting:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
enum WidgetTunnelControl {
|
|
static func currentIsStarted() async throws -> Bool {
|
|
guard let manager = try await loadManager() else {
|
|
return false
|
|
}
|
|
return manager.connection.status.isStarted
|
|
}
|
|
|
|
static func setStarted(_ started: Bool) async throws {
|
|
guard let manager = try await loadManager() else {
|
|
NSLog("[WidgetTunnelControl] No tunnel configuration found")
|
|
throw NSError(domain: "WidgetTunnelControl", code: 1, userInfo: [
|
|
NSLocalizedDescriptionKey: "Tunnel configuration not found",
|
|
])
|
|
}
|
|
|
|
if started {
|
|
if manager.isEnabled == false {
|
|
manager.isEnabled = true
|
|
}
|
|
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol,
|
|
let config = proto.providerConfiguration,
|
|
config["wasOnDemandEnabled"] as? Bool == true
|
|
{
|
|
var newConfig = config
|
|
newConfig.removeValue(forKey: "wasOnDemandEnabled")
|
|
proto.providerConfiguration = newConfig
|
|
manager.isOnDemandEnabled = true
|
|
}
|
|
try await manager.saveToPreferences()
|
|
do {
|
|
try manager.connection.startVPNTunnel()
|
|
} catch {
|
|
NSLog("[WidgetTunnelControl] startVPNTunnel failed: \(error.localizedDescription)")
|
|
throw error
|
|
}
|
|
} else {
|
|
if manager.isOnDemandEnabled {
|
|
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol {
|
|
var config = proto.providerConfiguration ?? [:]
|
|
config["wasOnDemandEnabled"] = true
|
|
proto.providerConfiguration = config
|
|
}
|
|
manager.isOnDemandEnabled = false
|
|
try await manager.saveToPreferences()
|
|
}
|
|
manager.connection.stopVPNTunnel()
|
|
}
|
|
}
|
|
|
|
private static func loadManager() async throws -> NETunnelProviderManager? {
|
|
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
|
|
return managers.first
|
|
}
|
|
}
|