From e6f5d6ef72ae5778808aaf36ed0c593bd3edb2ae Mon Sep 17 00:00:00 2001 From: Jamil Date: Fri, 20 Dec 2024 08:35:05 -0800 Subject: [PATCH] refactor(apple): Use static reference for TunnelManager instance (#7555) Since we need to react to state on this instance changing, we need an instance of TunnelManager to last the lifetime of the app process. Thus, it makes more sense to use the `.shared` public static pattern to maintain an instance of it we can use from anywhere instead of having to store it in various other classes. --- .../FirezoneKit/Managers/TunnelManager.swift | 6 ++-- .../Sources/FirezoneKit/Stores/Store.swift | 32 +++++++++---------- .../FirezoneKit/Views/SettingsView.swift | 4 +-- .../PacketTunnelProvider.swift | 2 +- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Managers/TunnelManager.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Managers/TunnelManager.swift index 4419fc1f3..cc39a832f 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Managers/TunnelManager.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Managers/TunnelManager.swift @@ -82,6 +82,8 @@ public enum TunnelMessage: Codable { } public class TunnelManager { + public static let shared = TunnelManager() + // Expose closures that someone else can use to respond to events // for this manager. var statusChangeHandler: ((NEVPNStatus) async -> Void)? @@ -109,10 +111,6 @@ public class TunnelManager { public static let bundleIdentifier: String = "\(Bundle.main.bundleIdentifier!).network-extension" private let bundleDescription = "Firezone" - init() { - manager = nil - } - // Initialize and save a new VPN profile in system Preferences func create() async throws -> Settings { let protocolConfiguration = NETunnelProviderProtocol() diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Stores/Store.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Stores/Store.swift index 99d157b41..6df87bc3e 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Stores/Store.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Stores/Store.swift @@ -28,7 +28,6 @@ public final class Store: ObservableObject { // we could periodically update it if we need to. @Published private(set) var decision: UNAuthorizationStatus - public let tunnelManager: TunnelManager private var sessionNotification: SessionNotification private var cancellables: Set = [] private var resourcesTimer: Timer? @@ -38,7 +37,6 @@ public final class Store: ObservableObject { self.decision = .authorized self.settings = Settings.defaultValue - self.tunnelManager = TunnelManager() self.sessionNotification = SessionNotification() initNotifications() @@ -46,7 +44,7 @@ public final class Store: ObservableObject { } public func internetResourceEnabled() -> Bool { - tunnelManager.internetResourceEnabled + TunnelManager.shared.internetResourceEnabled } private func initNotifications() { @@ -65,11 +63,11 @@ public final class Store: ObservableObject { } private func initTunnelManager() { - // Subscribe to status updates from the tunnelManager - tunnelManager.statusChangeHandler = handleVPNStatusChange + // Subscribe to status updates from the tunnel manager + TunnelManager.shared.statusChangeHandler = handleVPNStatusChange // Load our existing VPN profile and initialize our state - tunnelManager.load() { loadedStatus, loadedSettings, loadedActorName in + TunnelManager.shared.load() { loadedStatus, loadedSettings, loadedActorName in DispatchQueue.main.async { self.status = loadedStatus @@ -98,7 +96,7 @@ public final class Store: ObservableObject { func createVPNProfile() { DispatchQueue.main.async { Task { - self.settings = try await self.tunnelManager.create() + self.settings = try await TunnelManager.shared.create() } } } @@ -114,25 +112,25 @@ public final class Store: ObservableObject { return } - tunnelManager.start(token: token) + TunnelManager.shared.start(token: token) } func stop(clearToken: Bool = false) { guard [.connected, .connecting, .reasserting].contains(status) else { return } - tunnelManager.stop(clearToken: clearToken) + TunnelManager.shared.stop(clearToken: clearToken) } func signIn(authResponse: AuthResponse) async throws { // Save actorName DispatchQueue.main.async { self.actorName = authResponse.actorName } - try await tunnelManager.saveSettings(settings) - try await tunnelManager.saveActorName(authResponse.actorName) + try await TunnelManager.shared.saveSettings(settings) + try await TunnelManager.shared.saveActorName(authResponse.actorName) // Bring the tunnel up and send it a token to start - tunnelManager.start(token: authResponse.token) + TunnelManager.shared.start(token: authResponse.token) } func signOut() async throws { @@ -145,11 +143,11 @@ public final class Store: ObservableObject { func beginUpdatingResources(callback: @escaping (ResourceList) -> Void) { Log.app.log("\(#function)") - tunnelManager.fetchResources(callback: callback) + TunnelManager.shared.fetchResources(callback: callback) let intervalInSeconds: TimeInterval = 1 let timer = Timer(timeInterval: intervalInSeconds, repeats: true) { [weak self] _ in guard let self = self else { return } - tunnelManager.fetchResources(callback: callback) + TunnelManager.shared.fetchResources(callback: callback) } RunLoop.main.add(timer, forMode: .common) resourcesTimer = timer @@ -163,7 +161,7 @@ public final class Store: ObservableObject { func save(_ newSettings: Settings) async throws { Task { do { - try await tunnelManager.saveSettings(newSettings) + try await TunnelManager.shared.saveSettings(newSettings) DispatchQueue.main.async { self.settings = newSettings } } catch { Log.app.error("\(#function): \(error)") @@ -172,9 +170,9 @@ public final class Store: ObservableObject { } func toggleInternetResource(enabled: Bool) { - tunnelManager.toggleInternetResource(enabled: enabled) + TunnelManager.shared.toggleInternetResource(enabled: enabled) var newSettings = settings - newSettings.internetResourceEnabled = tunnelManager.internetResourceEnabled + newSettings.internetResourceEnabled = TunnelManager.shared.internetResourceEnabled Task { try await save(newSettings) } diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/SettingsView.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/SettingsView.swift index 8ec5db8ee..a39bf1f9a 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/SettingsView.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/SettingsView.swift @@ -76,7 +76,7 @@ public final class SettingsViewModel: ObservableObject { do { #if os(macOS) - let providerLogFolderSize = try await store.tunnelManager.getLogFolderSize() + let providerLogFolderSize = try await TunnelManager.shared.getLogFolderSize() let totalSize = logFolderSize + providerLogFolderSize #else let totalSize = logFolderSize @@ -105,7 +105,7 @@ public final class SettingsViewModel: ObservableObject { try Log.clear(in: SharedAccess.logFolderURL) #if os(macOS) - try await store.tunnelManager.clearLogs() + try await TunnelManager.shared.clearLogs() #endif } } diff --git a/swift/apple/FirezoneNetworkExtension/PacketTunnelProvider.swift b/swift/apple/FirezoneNetworkExtension/PacketTunnelProvider.swift index 7b67aa90c..98724cfd0 100644 --- a/swift/apple/FirezoneNetworkExtension/PacketTunnelProvider.swift +++ b/swift/apple/FirezoneNetworkExtension/PacketTunnelProvider.swift @@ -78,7 +78,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { try await adapter.start() - // Tell the system the tunnel is up, moving the tunnelManager status to + // Tell the system the tunnel is up, moving the tunnel manager status to // `connected`. completionHandler(nil) } catch {