Save last used profile (#1036)

Especially useful on macOS and tvOS where Network Extension does not
retain this information when the profile is disabled. On these
platforms, there's no native way to tell the last used profile, so save
it to UserDefaults and fall back to it when tunnel.currentProfile is
nil.
This commit is contained in:
Davide 2024-12-21 22:39:55 +01:00 committed by GitHub
parent 60336ba110
commit f112ea8061
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 3 deletions

View File

@ -29,6 +29,8 @@ import PassepartoutKit
@MainActor @MainActor
public final class ExtendedTunnel: ObservableObject { public final class ExtendedTunnel: ObservableObject {
private let defaults: UserDefaults?
private let tunnel: Tunnel private let tunnel: Tunnel
private let environment: TunnelEnvironment private let environment: TunnelEnvironment
@ -54,11 +56,13 @@ public final class ExtendedTunnel: ObservableObject {
private var subscriptions: Set<AnyCancellable> private var subscriptions: Set<AnyCancellable>
public init( public init(
defaults: UserDefaults? = nil,
tunnel: Tunnel, tunnel: Tunnel,
environment: TunnelEnvironment, environment: TunnelEnvironment,
processor: AppTunnelProcessor? = nil, processor: AppTunnelProcessor? = nil,
interval: TimeInterval interval: TimeInterval
) { ) {
self.defaults = defaults
self.tunnel = tunnel self.tunnel = tunnel
self.environment = environment self.environment = environment
self.processor = processor self.processor = processor
@ -83,12 +87,15 @@ extension ExtendedTunnel {
extension ExtendedTunnel { extension ExtendedTunnel {
public var currentProfile: TunnelCurrentProfile? { public var currentProfile: TunnelCurrentProfile? {
tunnel.currentProfile tunnel.currentProfile ?? lastUsedProfile
} }
public var currentProfilePublisher: AnyPublisher<TunnelCurrentProfile?, Never> { public var currentProfilePublisher: AnyPublisher<TunnelCurrentProfile?, Never> {
tunnel tunnel
.$currentProfile .$currentProfile
.map {
$0 ?? self.lastUsedProfile
}
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
@ -154,7 +161,10 @@ private extension ExtendedTunnel {
tunnel tunnel
.$currentProfile .$currentProfile
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] _ in .sink { [weak self] in
if let id = $0?.id {
self?.defaults?.set(id.uuidString, forKey: AppPreference.lastUsedProfileId.key)
}
self?.objectWillChange.send() self?.objectWillChange.send()
} }
.store(in: &subscriptions) .store(in: &subscriptions)
@ -195,8 +205,18 @@ private extension ExtendedTunnel {
// MARK: - Helpers // MARK: - Helpers
private extension ExtendedTunnel {
var lastUsedProfile: TunnelCurrentProfile? {
guard let uuidString = defaults?.object(forKey: AppPreference.lastUsedProfileId.key) as? String,
let uuid = UUID(uuidString: uuidString) else {
return nil
}
return TunnelCurrentProfile(id: uuid, onDemand: false)
}
}
extension TunnelStatus { extension TunnelStatus {
public func withEnvironment(_ environment: TunnelEnvironment) -> TunnelStatus { func withEnvironment(_ environment: TunnelEnvironment) -> TunnelStatus {
var status = self var status = self
if status == .active, let connectionStatus = environment.environmentValue(forKey: TunnelEnvironmentKeys.connectionStatus) { if status == .active, let connectionStatus = environment.environmentValue(forKey: TunnelEnvironmentKeys.connectionStatus) {
if connectionStatus == .connected { if connectionStatus == .connected {

View File

@ -26,6 +26,8 @@
import Foundation import Foundation
public enum AppPreference: String, PreferenceProtocol { public enum AppPreference: String, PreferenceProtocol {
case lastUsedProfileId
case logsPrivateData case logsPrivateData
public var key: String { public var key: String {

View File

@ -113,6 +113,7 @@ extension AppContext {
) )
let tunnel = ExtendedTunnel( let tunnel = ExtendedTunnel(
defaults: .standard,
tunnel: Tunnel(strategy: tunnelStrategy), tunnel: Tunnel(strategy: tunnelStrategy),
environment: tunnelEnvironment, environment: tunnelEnvironment,
processor: processor, processor: processor,