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 } }