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.
This commit is contained in:
Jamil
2024-12-20 08:35:05 -08:00
committed by GitHub
parent 1009b80266
commit e6f5d6ef72
4 changed files with 20 additions and 24 deletions

View File

@@ -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()

View File

@@ -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<AnyCancellable> = []
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)
}

View File

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

View File

@@ -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 {