Initial project setup - Phases 1-3 complete
This commit is contained in:
103
App/AppState.swift
Normal file
103
App/AppState.swift
Normal file
@@ -0,0 +1,103 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user