import SwiftUI import NetworkExtension import ProxyCore @Observable @MainActor final class AppState { var vpnStatus: NEVPNStatus = .disconnected var isCertificateInstalled: Bool = false var isCertificateTrusted: Bool = false private var vpnManager: NETunnelProviderManager? private var statusObservation: NSObjectProtocol? init() { Task { await loadVPNManager() } } func loadVPNManager() async { do { let managers = try await NETunnelProviderManager.loadAllFromPreferences() if let existing = managers.first { vpnManager = existing } else { let manager = NETunnelProviderManager() let proto = NETunnelProviderProtocol() proto.providerBundleIdentifier = ProxyConstants.extensionBundleIdentifier proto.serverAddress = ProxyConstants.proxyHost manager.protocolConfiguration = proto manager.localizedDescription = "Proxy" manager.isEnabled = true try await manager.saveToPreferences() try await manager.loadFromPreferences() vpnManager = manager } observeVPNStatus() vpnStatus = vpnManager?.connection.status ?? .disconnected } catch { print("[AppState] Failed to load VPN manager: \(error)") } } func toggleVPN() async { guard let manager = vpnManager else { await loadVPNManager() return } switch manager.connection.status { case .connected, .connecting: manager.connection.stopVPNTunnel() case .disconnected, .invalid: do { // Ensure saved and fresh before starting manager.isEnabled = true try await manager.saveToPreferences() try await manager.loadFromPreferences() try manager.connection.startVPNTunnel() } catch { print("[AppState] Failed to start VPN: \(error)") } default: break } } var isVPNConnected: Bool { vpnStatus == .connected } var vpnStatusText: String { switch vpnStatus { case .connected: "Connected" case .connecting: "Connecting..." case .disconnecting: "Disconnecting..." case .disconnected: "Disconnected" case .invalid: "Not Configured" case .reasserting: "Reconnecting..." @unknown default: "Unknown" } } private func observeVPNStatus() { guard let manager = vpnManager else { return } // Remove existing observer if let existing = statusObservation { NotificationCenter.default.removeObserver(existing) } statusObservation = NotificationCenter.default.addObserver( forName: .NEVPNStatusDidChange, object: manager.connection, queue: .main ) { [weak self] _ in Task { @MainActor in self?.vpnStatus = manager.connection.status } } } }