Files
ProxyIOS/App/AppState.swift
2026-04-06 11:28:40 -05:00

104 lines
3.1 KiB
Swift

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