mirror of
https://github.com/jkcoxson/LocalDevVPN.git
synced 2026-03-02 06:26:16 +01:00
Fix UI issues and Update Version Number
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// Configuration settings file format documentation can be found at:
|
// Configuration settings file format documentation can be found at:
|
||||||
// https://help.apple.com/xcode/#/dev745c5c974
|
// https://help.apple.com/xcode/#/dev745c5c974
|
||||||
|
|
||||||
MARKETING_VERSION = 1.2
|
MARKETING_VERSION = 1.2.1
|
||||||
CURRENT_PROJECT_VERSION = 1
|
CURRENT_PROJECT_VERSION = 1
|
||||||
|
|
||||||
// Vars to be overwritten by `CodeSigning.xcconfig` if exists
|
// Vars to be overwritten by `CodeSigning.xcconfig` if exists
|
||||||
|
|||||||
Binary file not shown.
@@ -44,6 +44,7 @@ class TunnelManager: ObservableObject {
|
|||||||
@Published var waitingOnSettings: Bool = false
|
@Published var waitingOnSettings: Bool = false
|
||||||
@Published var vpnManager: NETunnelProviderManager?
|
@Published var vpnManager: NETunnelProviderManager?
|
||||||
private var vpnObserver: NSObjectProtocol?
|
private var vpnObserver: NSObjectProtocol?
|
||||||
|
private var isProcessingStatusChange = false
|
||||||
|
|
||||||
private var tunnelDeviceIp: String {
|
private var tunnelDeviceIp: String {
|
||||||
UserDefaults.standard.string(forKey: "TunnelDeviceIP") ?? "10.7.0.0"
|
UserDefaults.standard.string(forKey: "TunnelDeviceIP") ?? "10.7.0.0"
|
||||||
@@ -105,8 +106,8 @@ class TunnelManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
loadTunnelPreferences()
|
|
||||||
setupStatusObserver()
|
setupStatusObserver()
|
||||||
|
loadTunnelPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
@@ -118,32 +119,29 @@ class TunnelManager: ObservableObject {
|
|||||||
if let error = error {
|
if let error = error {
|
||||||
VPNLogger.shared.log("Error loading preferences: \(error.localizedDescription)")
|
VPNLogger.shared.log("Error loading preferences: \(error.localizedDescription)")
|
||||||
self.tunnelStatus = .error
|
self.tunnelStatus = .error
|
||||||
|
self.waitingOnSettings = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer {
|
|
||||||
self.waitingOnSettings = true
|
|
||||||
}
|
|
||||||
|
|
||||||
self.hasLocalDeviceSupport = true
|
self.hasLocalDeviceSupport = true
|
||||||
|
self.waitingOnSettings = true
|
||||||
|
|
||||||
if let managers = managers, !managers.isEmpty {
|
if let managers = managers, !managers.isEmpty {
|
||||||
var stosManagers = [NETunnelProviderManager]()
|
let stosManagers = managers.filter { manager in
|
||||||
|
guard let proto = manager.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||||
for manager in managers {
|
return false
|
||||||
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol,
|
|
||||||
proto.providerBundleIdentifier == self.tunnelBundleId {
|
|
||||||
stosManagers.append(manager)
|
|
||||||
}
|
}
|
||||||
|
return proto.providerBundleIdentifier == self.tunnelBundleId
|
||||||
}
|
}
|
||||||
|
|
||||||
if !stosManagers.isEmpty {
|
if !stosManagers.isEmpty {
|
||||||
if stosManagers.count > 1 {
|
if stosManagers.count > 1 {
|
||||||
self.cleanupDuplicateManagers(stosManagers)
|
self.cleanupDuplicateManagers(stosManagers)
|
||||||
} else {
|
} else if let manager = stosManagers.first {
|
||||||
self.vpnManager = stosManagers.first
|
self.vpnManager = manager
|
||||||
self.updateTunnelStatus(from: stosManagers.first!.connection.status)
|
let currentStatus = manager.connection.status
|
||||||
VPNLogger.shared.log("Loaded existing StosVPN tunnel configuration")
|
VPNLogger.shared.log("Loaded existing StosVPN tunnel configuration with status: \(currentStatus.rawValue)")
|
||||||
|
self.updateTunnelStatus(from: currentStatus)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VPNLogger.shared.log("No StosVPN tunnel configuration found")
|
VPNLogger.shared.log("No StosVPN tunnel configuration found")
|
||||||
@@ -163,19 +161,18 @@ class TunnelManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let managerToKeep = activeManager ?? managers.first!
|
let managerToKeep = activeManager ?? managers.first!
|
||||||
self.vpnManager = managerToKeep
|
|
||||||
self.updateTunnelStatus(from: managerToKeep.connection.status)
|
|
||||||
|
|
||||||
var removedCount = 0
|
DispatchQueue.main.async { [weak self] in
|
||||||
for manager in managers {
|
self?.vpnManager = managerToKeep
|
||||||
if manager != managerToKeep {
|
self?.updateTunnelStatus(from: managerToKeep.connection.status)
|
||||||
manager.removeFromPreferences { error in
|
}
|
||||||
if let error = error {
|
|
||||||
VPNLogger.shared.log("Error removing duplicate VPN: \(error.localizedDescription)")
|
for manager in managers where manager != managerToKeep {
|
||||||
} else {
|
manager.removeFromPreferences { error in
|
||||||
removedCount += 1
|
if let error = error {
|
||||||
VPNLogger.shared.log("Successfully removed duplicate VPN configuration")
|
VPNLogger.shared.log("Error removing duplicate VPN: \(error.localizedDescription)")
|
||||||
}
|
} else {
|
||||||
|
VPNLogger.shared.log("Successfully removed duplicate VPN configuration")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,9 +184,14 @@ class TunnelManager: ObservableObject {
|
|||||||
object: nil,
|
object: nil,
|
||||||
queue: .main
|
queue: .main
|
||||||
) { [weak self] notification in
|
) { [weak self] notification in
|
||||||
guard let self = self,
|
guard let self = self else { return }
|
||||||
let connection = notification.object as? NEVPNConnection else {
|
guard let connection = notification.object as? NEVPNConnection else { return }
|
||||||
return
|
|
||||||
|
VPNLogger.shared.log("VPN Status notification received: \(connection.status.rawValue)")
|
||||||
|
|
||||||
|
// Update status immediately if it's our manager
|
||||||
|
if let manager = self.vpnManager, connection == manager.connection {
|
||||||
|
self.updateTunnelStatus(from: connection.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handleVPNStatusChange(notification: notification)
|
self.handleVPNStatusChange(notification: notification)
|
||||||
@@ -197,29 +199,37 @@ class TunnelManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateTunnelStatus(from connectionStatus: NEVPNStatus) {
|
private func updateTunnelStatus(from connectionStatus: NEVPNStatus) {
|
||||||
DispatchQueue.main.async {
|
let newStatus: TunnelStatus
|
||||||
switch connectionStatus {
|
switch connectionStatus {
|
||||||
case .invalid, .disconnected:
|
case .invalid, .disconnected:
|
||||||
self.tunnelStatus = .disconnected
|
newStatus = .disconnected
|
||||||
case .connecting:
|
case .connecting:
|
||||||
self.tunnelStatus = .connecting
|
newStatus = .connecting
|
||||||
case .connected:
|
case .connected:
|
||||||
self.tunnelStatus = .connected
|
newStatus = .connected
|
||||||
case .disconnecting:
|
case .disconnecting:
|
||||||
self.tunnelStatus = .disconnecting
|
newStatus = .disconnecting
|
||||||
case .reasserting:
|
case .reasserting:
|
||||||
self.tunnelStatus = .connecting
|
newStatus = .connecting
|
||||||
@unknown default:
|
@unknown default:
|
||||||
self.tunnelStatus = .error
|
newStatus = .error
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if self.tunnelStatus != newStatus {
|
||||||
|
VPNLogger.shared.log("StosVPN status updated from \(self.tunnelStatus) to \(newStatus)")
|
||||||
}
|
}
|
||||||
|
self.tunnelStatus = newStatus
|
||||||
VPNLogger.shared.log("StosVPN status updated: \(self.tunnelStatus)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createStosVPNConfiguration(completion: @escaping (NETunnelProviderManager?) -> Void) {
|
private func createStosVPNConfiguration(completion: @escaping (NETunnelProviderManager?) -> Void) {
|
||||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] (managers, error) in
|
NETunnelProviderManager.loadAllFromPreferences { [weak self] (managers, error) in
|
||||||
guard let self = self else { return }
|
guard let self = self else {
|
||||||
|
completion(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
VPNLogger.shared.log("Error checking existing VPN configurations: \(error.localizedDescription)")
|
VPNLogger.shared.log("Error checking existing VPN configurations: \(error.localizedDescription)")
|
||||||
@@ -229,16 +239,15 @@ class TunnelManager: ObservableObject {
|
|||||||
|
|
||||||
if let managers = managers {
|
if let managers = managers {
|
||||||
let stosManagers = managers.filter { manager in
|
let stosManagers = managers.filter { manager in
|
||||||
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol {
|
guard let proto = manager.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||||
return proto.providerBundleIdentifier == self.tunnelBundleId
|
return false
|
||||||
}
|
}
|
||||||
return false
|
return proto.providerBundleIdentifier == self.tunnelBundleId
|
||||||
}
|
}
|
||||||
|
|
||||||
if !stosManagers.isEmpty {
|
if let existingManager = stosManagers.first {
|
||||||
let manager = stosManagers.first!
|
|
||||||
VPNLogger.shared.log("Found existing StosVPN configuration, using it instead of creating new one")
|
VPNLogger.shared.log("Found existing StosVPN configuration, using it instead of creating new one")
|
||||||
completion(manager)
|
completion(existingManager)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,15 +300,13 @@ class TunnelManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let activeManager = managers.first { manager in
|
let activeManager = managers.first { manager in
|
||||||
return manager.connection.status == .connected ||
|
manager.connection.status == .connected || manager.connection.status == .connecting
|
||||||
manager.connection.status == .connecting
|
|
||||||
}
|
}
|
||||||
|
|
||||||
completion(activeManager)
|
completion(activeManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
|
|
||||||
func toggleVPNConnection() {
|
func toggleVPNConnection() {
|
||||||
@@ -311,6 +318,27 @@ class TunnelManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startVPN() {
|
func startVPN() {
|
||||||
|
if let manager = vpnManager {
|
||||||
|
let currentStatus = manager.connection.status
|
||||||
|
VPNLogger.shared.log("Current manager status: \(currentStatus.rawValue)")
|
||||||
|
|
||||||
|
if currentStatus == .connected {
|
||||||
|
VPNLogger.shared.log("VPN already connected, updating UI")
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .connected
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentStatus == .connecting {
|
||||||
|
VPNLogger.shared.log("VPN already connecting, updating UI")
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .connecting
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getActiveVPNManager { [weak self] activeManager in
|
getActiveVPNManager { [weak self] activeManager in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
@@ -329,71 +357,123 @@ class TunnelManager: ObservableObject {
|
|||||||
|
|
||||||
private func initializeAndStartStosVPN() {
|
private func initializeAndStartStosVPN() {
|
||||||
if let manager = vpnManager {
|
if let manager = vpnManager {
|
||||||
startExistingVPN(manager: manager)
|
manager.loadFromPreferences { [weak self] error in
|
||||||
} else {
|
|
||||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
VPNLogger.shared.log("Error reloading VPN configurations: \(error.localizedDescription)")
|
VPNLogger.shared.log("Error reloading manager: \(error.localizedDescription)")
|
||||||
self.createStosVPNConfiguration { manager in
|
self.createAndStartVPN()
|
||||||
guard let manager = manager else { return }
|
return
|
||||||
self.vpnManager = manager
|
}
|
||||||
|
|
||||||
|
self.startExistingVPN(manager: manager)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
createAndStartVPN()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createAndStartVPN() {
|
||||||
|
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
if let error = error {
|
||||||
|
VPNLogger.shared.log("Error reloading VPN configurations: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let managers = managers {
|
||||||
|
let stosManagers = managers.filter { manager in
|
||||||
|
guard let proto = manager.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return proto.providerBundleIdentifier == self.tunnelBundleId
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stosManagers.isEmpty {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.vpnManager = stosManagers.first
|
||||||
|
}
|
||||||
|
|
||||||
|
if stosManagers.count > 1 {
|
||||||
|
self.cleanupDuplicateManagers(stosManagers)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let manager = stosManagers.first {
|
||||||
self.startExistingVPN(manager: manager)
|
self.startExistingVPN(manager: manager)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if let managers = managers {
|
|
||||||
let stosManagers = managers.filter { manager in
|
self.createStosVPNConfiguration { [weak self] manager in
|
||||||
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol {
|
guard let self = self, let manager = manager else { return }
|
||||||
return proto.providerBundleIdentifier == self.tunnelBundleId
|
DispatchQueue.main.async { [weak self] in
|
||||||
}
|
self?.vpnManager = manager
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !stosManagers.isEmpty {
|
|
||||||
self.vpnManager = stosManagers.first
|
|
||||||
|
|
||||||
if stosManagers.count > 1 {
|
|
||||||
self.cleanupDuplicateManagers(stosManagers)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.startExistingVPN(manager: stosManagers.first!)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.createStosVPNConfiguration { manager in
|
|
||||||
guard let manager = manager else { return }
|
|
||||||
self.vpnManager = manager
|
|
||||||
self.startExistingVPN(manager: manager)
|
|
||||||
}
|
}
|
||||||
|
self.startExistingVPN(manager: manager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startExistingVPN(manager: NETunnelProviderManager) {
|
private func startExistingVPN(manager: NETunnelProviderManager) {
|
||||||
guard tunnelStatus != .connected else {
|
// First check the actual current status
|
||||||
|
let currentStatus = manager.connection.status
|
||||||
|
VPNLogger.shared.log("Current VPN status before start attempt: \(currentStatus.rawValue)")
|
||||||
|
|
||||||
|
if currentStatus == .connected {
|
||||||
VPNLogger.shared.log("StosVPN tunnel is already connected")
|
VPNLogger.shared.log("StosVPN tunnel is already connected")
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .connected
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentStatus == .connecting {
|
||||||
|
VPNLogger.shared.log("StosVPN tunnel is already connecting")
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .connecting
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.isEnabled = true
|
manager.isEnabled = true
|
||||||
manager.saveToPreferences { error in
|
manager.saveToPreferences { [weak self] error in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
VPNLogger.shared.log(error.localizedDescription)
|
VPNLogger.shared.log("Error saving preferences: \(error.localizedDescription)")
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .error
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload it to apply
|
manager.loadFromPreferences { [weak self] error in
|
||||||
manager.loadFromPreferences { error in
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
VPNLogger.shared.log(error.localizedDescription)
|
VPNLogger.shared.log("Error reloading preferences: \(error.localizedDescription)")
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .error
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tunnelStatus = .connecting
|
// Check status again after reload
|
||||||
|
let statusAfterReload = manager.connection.status
|
||||||
|
VPNLogger.shared.log("VPN status after reload: \(statusAfterReload.rawValue)")
|
||||||
|
|
||||||
|
if statusAfterReload == .connected {
|
||||||
|
VPNLogger.shared.log("VPN is already connected after reload")
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .connected
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .connecting
|
||||||
|
}
|
||||||
|
|
||||||
let options: [String: NSObject] = [
|
let options: [String: NSObject] = [
|
||||||
"TunnelDeviceIP": self.tunnelDeviceIp as NSObject,
|
"TunnelDeviceIP": self.tunnelDeviceIp as NSObject,
|
||||||
@@ -405,7 +485,9 @@ class TunnelManager: ObservableObject {
|
|||||||
try manager.connection.startVPNTunnel(options: options)
|
try manager.connection.startVPNTunnel(options: options)
|
||||||
VPNLogger.shared.log("StosVPN tunnel start initiated")
|
VPNLogger.shared.log("StosVPN tunnel start initiated")
|
||||||
} catch {
|
} catch {
|
||||||
self.tunnelStatus = .error
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .error
|
||||||
|
}
|
||||||
VPNLogger.shared.log("Failed to start StosVPN tunnel: \(error.localizedDescription)")
|
VPNLogger.shared.log("Failed to start StosVPN tunnel: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,9 +495,15 @@ class TunnelManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func stopVPN() {
|
func stopVPN() {
|
||||||
guard let manager = vpnManager else { return }
|
guard let manager = vpnManager else {
|
||||||
|
VPNLogger.shared.log("No VPN manager available to stop")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.tunnelStatus = .disconnecting
|
||||||
|
}
|
||||||
|
|
||||||
tunnelStatus = .disconnecting
|
|
||||||
manager.connection.stopVPNTunnel()
|
manager.connection.stopVPNTunnel()
|
||||||
VPNLogger.shared.log("StosVPN tunnel stop initiated")
|
VPNLogger.shared.log("StosVPN tunnel stop initiated")
|
||||||
|
|
||||||
@@ -425,9 +513,12 @@ class TunnelManager: ObservableObject {
|
|||||||
func handleVPNStatusChange(notification: Notification) {
|
func handleVPNStatusChange(notification: Notification) {
|
||||||
guard let connection = notification.object as? NEVPNConnection else { return }
|
guard let connection = notification.object as? NEVPNConnection else { return }
|
||||||
|
|
||||||
|
VPNLogger.shared.log("Handling VPN status change: \(connection.status.rawValue)")
|
||||||
|
|
||||||
|
// Always update status if it's our manager's connection
|
||||||
if let manager = vpnManager, connection == manager.connection {
|
if let manager = vpnManager, connection == manager.connection {
|
||||||
|
VPNLogger.shared.log("Status change is for our StosVPN manager")
|
||||||
updateTunnelStatus(from: connection.status)
|
updateTunnelStatus(from: connection.status)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if connection.status == .disconnected &&
|
if connection.status == .disconnected &&
|
||||||
@@ -437,23 +528,40 @@ class TunnelManager: ObservableObject {
|
|||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
|
||||||
self?.initializeAndStartStosVPN()
|
self?.initializeAndStartStosVPN()
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a different StosVPN manager (perhaps a duplicate)
|
// Prevent recursive calls when checking for duplicates
|
||||||
// This helps discover duplicates created by other means
|
guard !isProcessingStatusChange else { return }
|
||||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
isProcessingStatusChange = true
|
||||||
|
|
||||||
|
// Check for duplicates asynchronously without blocking
|
||||||
|
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let managers = managers, !managers.isEmpty {
|
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||||
let stosManagers = managers.filter { manager in
|
guard let self = self, let managers = managers, !managers.isEmpty else {
|
||||||
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol {
|
DispatchQueue.main.async { [weak self] in
|
||||||
return proto.providerBundleIdentifier == self.tunnelBundleId
|
self?.isProcessingStatusChange = false
|
||||||
}
|
}
|
||||||
return false
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let stosManagers = managers.filter { manager in
|
||||||
|
guard let proto = manager.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return proto.providerBundleIdentifier == self.tunnelBundleId
|
||||||
}
|
}
|
||||||
|
|
||||||
if stosManagers.count > 1 {
|
if stosManagers.count > 1 {
|
||||||
self.cleanupDuplicateManagers(stosManagers)
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.cleanupDuplicateManagers(stosManagers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.isProcessingStatusChange = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -462,7 +570,9 @@ class TunnelManager: ObservableObject {
|
|||||||
// MARK: - Cleanup Utilities
|
// MARK: - Cleanup Utilities
|
||||||
|
|
||||||
func cleanupAllVPNConfigurations() {
|
func cleanupAllVPNConfigurations() {
|
||||||
NETunnelProviderManager.loadAllFromPreferences { managers, error in
|
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
VPNLogger.shared.log("Error loading VPN configurations for cleanup: \(error.localizedDescription)")
|
VPNLogger.shared.log("Error loading VPN configurations for cleanup: \(error.localizedDescription)")
|
||||||
return
|
return
|
||||||
@@ -471,28 +581,28 @@ class TunnelManager: ObservableObject {
|
|||||||
guard let managers = managers else { return }
|
guard let managers = managers else { return }
|
||||||
|
|
||||||
for manager in managers {
|
for manager in managers {
|
||||||
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol,
|
guard let proto = manager.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
proto.providerBundleIdentifier == self.tunnelBundleId {
|
proto.providerBundleIdentifier == self.tunnelBundleId else {
|
||||||
|
continue
|
||||||
// If connected, disconnect first
|
}
|
||||||
if manager.connection.status == .connected ||
|
|
||||||
manager.connection.status == .connecting {
|
if manager.connection.status == .connected || manager.connection.status == .connecting {
|
||||||
manager.connection.stopVPNTunnel()
|
manager.connection.stopVPNTunnel()
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.removeFromPreferences { error in
|
manager.removeFromPreferences { error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
VPNLogger.shared.log("Error removing VPN configuration: \(error.localizedDescription)")
|
VPNLogger.shared.log("Error removing VPN configuration: \(error.localizedDescription)")
|
||||||
} else {
|
} else {
|
||||||
VPNLogger.shared.log("Successfully removed VPN configuration")
|
VPNLogger.shared.log("Successfully removed VPN configuration")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
self.vpnManager = nil
|
self?.vpnManager = nil
|
||||||
self.tunnelStatus = .disconnected
|
self?.tunnelStatus = .disconnected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user