diff --git a/Passepartout/App/Views/EndpointView+OpenVPN.swift b/Passepartout/App/Views/EndpointView+OpenVPN.swift index 54c17fd2..7e354317 100644 --- a/Passepartout/App/Views/EndpointView+OpenVPN.swift +++ b/Passepartout/App/Views/EndpointView+OpenVPN.swift @@ -47,58 +47,14 @@ extension EndpointView { @State private var selectedPort: UInt16 = 0 - // XXX: do not escape mutating 'self', use constant providerManager init(currentProfile: ObservableProfile) { let providerManager: ProviderManager = .shared self.providerManager = providerManager self.currentProfile = currentProfile - _builder = .init { - if currentProfile.value.isProvider { - guard let server = currentProfile.value.providerServer(providerManager) else { - assertionFailure("Server not found") - return .init() - } - guard let preset = currentProfile.value.providerPreset(server) else { - assertionFailure("Preset not found") - return .init() - } - guard let cfg = preset.openVPNConfiguration else { - assertionFailure("Preset \(preset.id) (\(preset.name)) has no OpenVPN configuration") - return .init() - } - var builder = cfg.builder(withFallbacks: true) - try? builder.setRemotes(from: preset, with: server, excludingHostname: false) - return builder - } else if let cfg = currentProfile.value.hostOpenVPNSettings?.configuration { - let builder = cfg.builder(withFallbacks: true) -// pp_log.debug("Loading OpenVPN configuration: \(builder)") - return builder - } - // fall back gracefully - return .init() - } set: { - if currentProfile.value.isProvider { - // readonly - } else { - pp_log.debug("Saving OpenVPN configuration: \($0)") - currentProfile.value.hostOpenVPNSettings?.configuration = $0.build() - } - } - _customEndpoint = .init { - if currentProfile.value.isProvider { - return currentProfile.value.providerCustomEndpoint - } else { - return currentProfile.value.hostOpenVPNSettings?.customEndpoint - } - } set: { - if currentProfile.value.isProvider { - currentProfile.value.providerCustomEndpoint = $0 - } else { - currentProfile.value.hostOpenVPNSettings?.customEndpoint = $0 - } - } + _builder = currentProfile.builderBinding(providerManager: providerManager) + _customEndpoint = currentProfile.customEndpointBinding } var body: some View { @@ -280,3 +236,58 @@ private extension EndpointView.OpenVPNView { proxy.maybeScrollTo(customEndpoint?.id) } } + +// MARK: - Bindings + +private extension ObservableProfile { + func builderBinding(providerManager: ProviderManager) -> Binding { + .init { + if self.value.isProvider { + guard let server = self.value.providerServer(providerManager) else { + assertionFailure("Server not found") + return .init() + } + guard let preset = self.value.providerPreset(server) else { + assertionFailure("Preset not found") + return .init() + } + guard let cfg = preset.openVPNConfiguration else { + assertionFailure("Preset \(preset.id) (\(preset.name)) has no OpenVPN configuration") + return .init() + } + var builder = cfg.builder(withFallbacks: true) + try? builder.setRemotes(from: preset, with: server, excludingHostname: false) + return builder + } else if let cfg = self.value.hostOpenVPNSettings?.configuration { + let builder = cfg.builder(withFallbacks: true) +// pp_log.debug("Loading OpenVPN configuration: \(builder)") + return builder + } + // fall back gracefully + return .init() + } set: { + if self.value.isProvider { + // readonly + } else { + pp_log.debug("Saving OpenVPN configuration: \($0)") + self.value.hostOpenVPNSettings?.configuration = $0.build() + } + } + } + + var customEndpointBinding: Binding { + .init { + if self.value.isProvider { + return self.value.providerCustomEndpoint + } else { + return self.value.hostOpenVPNSettings?.customEndpoint + } + } set: { + if self.value.isProvider { + self.value.providerCustomEndpoint = $0 + } else { + self.value.hostOpenVPNSettings?.customEndpoint = $0 + } + } + } +} diff --git a/Passepartout/App/Views/EndpointView+WireGuard.swift b/Passepartout/App/Views/EndpointView+WireGuard.swift index 185bfd34..3cfe481d 100644 --- a/Passepartout/App/Views/EndpointView+WireGuard.swift +++ b/Passepartout/App/Views/EndpointView+WireGuard.swift @@ -39,7 +39,6 @@ extension EndpointView { // var customPeer: Binding? = nil - // XXX: do not escape mutating 'self', use constant providerManager init(currentProfile: ObservableProfile, isReadonly: Bool) { let providerManager: ProviderManager = .shared @@ -47,36 +46,7 @@ extension EndpointView { self.currentProfile = currentProfile self.isReadonly = isReadonly - _builder = .init { - if currentProfile.value.isProvider { - guard let server = currentProfile.value.providerServer(providerManager) else { - assertionFailure("Server not found") - return .init() - } - guard let preset = currentProfile.value.providerPreset(server) else { - assertionFailure("Preset not found") - return .init() - } - guard let cfg = preset.wireGuardConfiguration else { - assertionFailure("Preset \(preset.id) (\(preset.name)) has no WireGuard configuration") - return .init() - } - return cfg.builder() - } else if let cfg = currentProfile.value.hostWireGuardSettings?.configuration { - let builder = cfg.builder() -// pp_log.debug("Loading WireGuard configuration: \(builder)") - return builder - } - // fall back gracefully - return .init() - } set: { - if currentProfile.value.isProvider { - // readonly - } else { - pp_log.debug("Saving WireGuard configuration: \($0)") - currentProfile.value.hostWireGuardSettings?.configuration = $0.build() - } - } + _builder = currentProfile.builderBinding(providerManager: providerManager) } var body: some View { @@ -146,3 +116,40 @@ private extension EndpointView.WireGuardView { } } } + +// MARK: - Bindings + +private extension ObservableProfile { + func builderBinding(providerManager: ProviderManager) -> Binding { + .init { + if self.value.isProvider { + guard let server = self.value.providerServer(providerManager) else { + assertionFailure("Server not found") + return .init() + } + guard let preset = self.value.providerPreset(server) else { + assertionFailure("Preset not found") + return .init() + } + guard let cfg = preset.wireGuardConfiguration else { + assertionFailure("Preset \(preset.id) (\(preset.name)) has no WireGuard configuration") + return .init() + } + return cfg.builder() + } else if let cfg = self.value.hostWireGuardSettings?.configuration { + let builder = cfg.builder() +// pp_log.debug("Loading WireGuard configuration: \(builder)") + return builder + } + // fall back gracefully + return .init() + } set: { + if self.value.isProvider { + // readonly + } else { + pp_log.debug("Saving WireGuard configuration: \($0)") + self.value.hostWireGuardSettings?.configuration = $0.build() + } + } + } +} diff --git a/Passepartout/App/Views/ProviderLocationView.swift b/Passepartout/App/Views/ProviderLocationView.swift index dfce3369..ef9647bb 100644 --- a/Passepartout/App/Views/ProviderLocationView.swift +++ b/Passepartout/App/Views/ProviderLocationView.swift @@ -43,7 +43,6 @@ struct ProviderLocationView: View, ProviderProfileAvailability { currentProfile.value } - // XXX: do not escape mutating 'self', use constant providerManager init(currentProfile: ObservableProfile, isEditable: Bool, isPresented: Binding) { let providerManager: ProviderManager = .shared @@ -51,24 +50,8 @@ struct ProviderLocationView: View, ProviderProfileAvailability { self.currentProfile = currentProfile self.isEditable = isEditable - _selectedServer = .init { - guard let serverId = currentProfile.value.providerServerId else { - return nil - } - return providerManager.server(withId: serverId) - } set: { - // user never selects a nil server - guard let server = $0 else { - return - } - currentProfile.value.setProviderServer(server) - isPresented.wrappedValue = false - } - _favoriteLocationIds = .init { - currentProfile.value.providerFavoriteLocationIds - } set: { - currentProfile.value.providerFavoriteLocationIds = $0 - } + _selectedServer = currentProfile.selectedServerBinding(providerManager: providerManager, isPresented: isPresented) + _favoriteLocationIds = currentProfile.providerFavoriteLocationIdsBinding } var body: some View { @@ -323,3 +306,31 @@ private extension ProviderLocationView.ServerListView { proxy.maybeScrollTo(selectedServer?.id) } } + +// MARK: - Bindings + +private extension ObservableProfile { + func selectedServerBinding(providerManager: ProviderManager, isPresented: Binding) -> Binding { + .init { + guard let serverId = self.value.providerServerId else { + return nil + } + return providerManager.server(withId: serverId) + } set: { + // user never selects a nil server + guard let server = $0 else { + return + } + self.value.setProviderServer(server) + isPresented.wrappedValue = false + } + } + + var providerFavoriteLocationIdsBinding: Binding?> { + .init { + self.value.providerFavoriteLocationIds + } set: { + self.value.providerFavoriteLocationIds = $0 + } + } +} diff --git a/Passepartout/App/Views/ProviderPresetView.swift b/Passepartout/App/Views/ProviderPresetView.swift index f11092a2..3d9b16b0 100644 --- a/Passepartout/App/Views/ProviderPresetView.swift +++ b/Passepartout/App/Views/ProviderPresetView.swift @@ -37,7 +37,6 @@ struct ProviderPresetView: View { @Binding private var selectedPreset: ProviderServer.Preset? - // XXX: do not escape mutating 'self', use constant providerManager init(currentProfile: ObservableProfile) { let providerManager: ProviderManager = .shared @@ -45,21 +44,7 @@ struct ProviderPresetView: View { self.currentProfile = currentProfile server = currentProfile.value.providerServer(providerManager) - _selectedPreset = .init { - guard let serverId = currentProfile.value.providerServerId else { - return nil - } - guard let server = providerManager.server(withId: serverId) else { - return nil - } - return currentProfile.value.providerPreset(server) - } set: { - // user never selects a nil preset - guard let preset = $0 else { - return - } - currentProfile.value.setProviderPreset(preset) - } + _selectedPreset = currentProfile.selectedPresetBinding(providerManager: providerManager) } var body: some View { @@ -106,3 +91,25 @@ private extension ProviderPresetView { server?.presets?.sorted() ?? [] } } + +// MARK: - Bindings + +private extension ObservableProfile { + func selectedPresetBinding(providerManager: ProviderManager) -> Binding { + .init { + guard let serverId = self.value.providerServerId else { + return nil + } + guard let server = providerManager.server(withId: serverId) else { + return nil + } + return self.value.providerPreset(server) + } set: { + // user never selects a nil preset + guard let preset = $0 else { + return + } + self.value.setProviderPreset(preset) + } + } +}