Externalize complex bindings (#329)

Some bindings are too convoluted, move them out of initializers.
This commit is contained in:
Davide De Rosa 2023-07-17 20:44:18 +01:00 committed by GitHub
parent 0804c6b38e
commit 4173c7aa6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 112 deletions

View File

@ -47,58 +47,14 @@ extension EndpointView {
@State private var selectedPort: UInt16 = 0 @State private var selectedPort: UInt16 = 0
// XXX: do not escape mutating 'self', use constant providerManager
init(currentProfile: ObservableProfile) { init(currentProfile: ObservableProfile) {
let providerManager: ProviderManager = .shared let providerManager: ProviderManager = .shared
self.providerManager = providerManager self.providerManager = providerManager
self.currentProfile = currentProfile self.currentProfile = currentProfile
_builder = .init { _builder = currentProfile.builderBinding(providerManager: providerManager)
if currentProfile.value.isProvider { _customEndpoint = currentProfile.customEndpointBinding
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
}
}
} }
var body: some View { var body: some View {
@ -280,3 +236,58 @@ private extension EndpointView.OpenVPNView {
proxy.maybeScrollTo(customEndpoint?.id) proxy.maybeScrollTo(customEndpoint?.id)
} }
} }
// MARK: - Bindings
private extension ObservableProfile {
func builderBinding(providerManager: ProviderManager) -> Binding<OpenVPN.ConfigurationBuilder> {
.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<Endpoint?> {
.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
}
}
}
}

View File

@ -39,7 +39,6 @@ extension EndpointView {
// var customPeer: Binding<Endpoint?>? = nil // var customPeer: Binding<Endpoint?>? = nil
// XXX: do not escape mutating 'self', use constant providerManager
init(currentProfile: ObservableProfile, isReadonly: Bool) { init(currentProfile: ObservableProfile, isReadonly: Bool) {
let providerManager: ProviderManager = .shared let providerManager: ProviderManager = .shared
@ -47,36 +46,7 @@ extension EndpointView {
self.currentProfile = currentProfile self.currentProfile = currentProfile
self.isReadonly = isReadonly self.isReadonly = isReadonly
_builder = .init { _builder = currentProfile.builderBinding(providerManager: providerManager)
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()
}
}
} }
var body: some View { var body: some View {
@ -146,3 +116,40 @@ private extension EndpointView.WireGuardView {
} }
} }
} }
// MARK: - Bindings
private extension ObservableProfile {
func builderBinding(providerManager: ProviderManager) -> Binding<WireGuard.ConfigurationBuilder> {
.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()
}
}
}
}

View File

@ -43,7 +43,6 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
currentProfile.value currentProfile.value
} }
// XXX: do not escape mutating 'self', use constant providerManager
init(currentProfile: ObservableProfile, isEditable: Bool, isPresented: Binding<Bool>) { init(currentProfile: ObservableProfile, isEditable: Bool, isPresented: Binding<Bool>) {
let providerManager: ProviderManager = .shared let providerManager: ProviderManager = .shared
@ -51,24 +50,8 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
self.currentProfile = currentProfile self.currentProfile = currentProfile
self.isEditable = isEditable self.isEditable = isEditable
_selectedServer = .init { _selectedServer = currentProfile.selectedServerBinding(providerManager: providerManager, isPresented: isPresented)
guard let serverId = currentProfile.value.providerServerId else { _favoriteLocationIds = currentProfile.providerFavoriteLocationIdsBinding
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
}
} }
var body: some View { var body: some View {
@ -323,3 +306,31 @@ private extension ProviderLocationView.ServerListView {
proxy.maybeScrollTo(selectedServer?.id) proxy.maybeScrollTo(selectedServer?.id)
} }
} }
// MARK: - Bindings
private extension ObservableProfile {
func selectedServerBinding(providerManager: ProviderManager, isPresented: Binding<Bool>) -> Binding<ProviderServer?> {
.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<Set<String>?> {
.init {
self.value.providerFavoriteLocationIds
} set: {
self.value.providerFavoriteLocationIds = $0
}
}
}

View File

@ -37,7 +37,6 @@ struct ProviderPresetView: View {
@Binding private var selectedPreset: ProviderServer.Preset? @Binding private var selectedPreset: ProviderServer.Preset?
// XXX: do not escape mutating 'self', use constant providerManager
init(currentProfile: ObservableProfile) { init(currentProfile: ObservableProfile) {
let providerManager: ProviderManager = .shared let providerManager: ProviderManager = .shared
@ -45,21 +44,7 @@ struct ProviderPresetView: View {
self.currentProfile = currentProfile self.currentProfile = currentProfile
server = currentProfile.value.providerServer(providerManager) server = currentProfile.value.providerServer(providerManager)
_selectedPreset = .init { _selectedPreset = currentProfile.selectedPresetBinding(providerManager: providerManager)
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)
}
} }
var body: some View { var body: some View {
@ -106,3 +91,25 @@ private extension ProviderPresetView {
server?.presets?.sorted() ?? [] server?.presets?.sorted() ?? []
} }
} }
// MARK: - Bindings
private extension ObservableProfile {
func selectedPresetBinding(providerManager: ProviderManager) -> Binding<ProviderServer.Preset?> {
.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)
}
}
}