Skip to content

Commit 382bf09

Browse files
author
Jon Petersson
committed
Allow users to import settings by pasting JSON blobs
1 parent 3ad7afe commit 382bf09

17 files changed

+752
-29
lines changed

ios/MullvadSettings/IPOverride.swift

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// IPOverride.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2024-01-16.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Network
10+
11+
public struct RelayOverrides: Codable {
12+
public let overrides: [IPOverride]
13+
14+
private enum CodingKeys: String, CodingKey {
15+
case overrides = "relay_overrides"
16+
}
17+
}
18+
19+
public struct IPOverride: Codable, Equatable {
20+
public let hostname: String
21+
public var ipv4Address: IPv4Address?
22+
public var ipv6Address: IPv6Address?
23+
24+
private enum CodingKeys: String, CodingKey {
25+
case hostname
26+
case ipv4Address = "ipv4_addr_in"
27+
case ipv6Address = "ipv6_addr_in"
28+
}
29+
30+
init(hostname: String, ipv4Address: IPv4Address?, ipv6Address: IPv6Address?) throws {
31+
self.hostname = hostname
32+
self.ipv4Address = ipv4Address
33+
self.ipv6Address = ipv6Address
34+
35+
if self.ipv4Address.isNil && self.ipv6Address.isNil {
36+
throw NSError(domain: "IPOverrideInitDomain", code: NSFormattingError)
37+
}
38+
}
39+
40+
public init(from decoder: Decoder) throws {
41+
let container = try decoder.container(keyedBy: CodingKeys.self)
42+
43+
self.hostname = try container.decode(String.self, forKey: .hostname)
44+
self.ipv4Address = try container.decodeIfPresent(IPv4Address.self, forKey: .ipv4Address)
45+
self.ipv6Address = try container.decodeIfPresent(IPv6Address.self, forKey: .ipv6Address)
46+
47+
if self.ipv4Address.isNil && self.ipv6Address.isNil {
48+
throw NSError(domain: "IPOverrideInitDomain", code: NSFormattingError)
49+
}
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// IPOverrideRepository.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2024-01-16.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import MullvadLogging
11+
12+
public protocol IPOverrideRepositoryProtocol {
13+
func add(_ overrides: [IPOverride])
14+
func fetchAll() -> [IPOverride]
15+
func fetchByHostname(_ hostname: String) -> IPOverride?
16+
func deleteAll()
17+
func parse(data: Data) throws -> [IPOverride]
18+
}
19+
20+
public class IPOverrideRepository: IPOverrideRepositoryProtocol {
21+
private let logger = Logger(label: "IPOverrideRepository")
22+
23+
public init() {}
24+
25+
public func add(_ overrides: [IPOverride]) {
26+
var storedOverrides = fetchAll()
27+
28+
overrides.forEach { override in
29+
if let existingOverrideIndex = storedOverrides.firstIndex(where: { $0.hostname == override.hostname }) {
30+
var existingOverride = storedOverrides[existingOverrideIndex]
31+
32+
if let ipv4Address = override.ipv4Address {
33+
existingOverride.ipv4Address = ipv4Address
34+
}
35+
36+
if let ipv6Address = override.ipv6Address {
37+
existingOverride.ipv6Address = ipv6Address
38+
}
39+
40+
storedOverrides[existingOverrideIndex] = existingOverride
41+
} else {
42+
storedOverrides.append(override)
43+
}
44+
}
45+
46+
do {
47+
try writeIpOverrides(storedOverrides)
48+
} catch {
49+
logger.error("Could not add override(s): \(overrides) \nError: \(error)")
50+
}
51+
}
52+
53+
public func fetchAll() -> [IPOverride] {
54+
return (try? readIpOverrides()) ?? []
55+
}
56+
57+
public func fetchByHostname(_ hostname: String) -> IPOverride? {
58+
return fetchAll().first { $0.hostname == hostname }
59+
}
60+
61+
public func deleteAll() {
62+
do {
63+
try SettingsManager.store.delete(key: .ipOverrides)
64+
} catch {
65+
logger.error("Could not delete all overrides. \nError: \(error)")
66+
}
67+
}
68+
69+
public func parse(data: Data) throws -> [IPOverride] {
70+
let decoder = JSONDecoder()
71+
let jsonData = try decoder.decode(RelayOverrides.self, from: data)
72+
73+
return jsonData.overrides
74+
}
75+
76+
private func readIpOverrides() throws -> [IPOverride] {
77+
let parser = makeParser()
78+
let data = try SettingsManager.store.read(key: .ipOverrides)
79+
80+
return try parser.parseUnversionedPayload(as: [IPOverride].self, from: data)
81+
}
82+
83+
private func writeIpOverrides(_ overrides: [IPOverride]) throws {
84+
let parser = makeParser()
85+
let data = try parser.produceUnversionedPayload(overrides)
86+
87+
try SettingsManager.store.write(data, for: .ipOverrides)
88+
}
89+
90+
private func makeParser() -> SettingsParser {
91+
SettingsParser(decoder: JSONDecoder(), encoder: JSONEncoder())
92+
}
93+
}

ios/MullvadSettings/SettingsStore.swift

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public enum SettingsKey: String, CaseIterable {
1212
case settings = "Settings"
1313
case deviceState = "DeviceState"
1414
case apiAccessMethods = "ApiAccessMethods"
15+
case ipOverrides = "IPOverrides"
1516
case lastUsedAccount = "LastUsedAccount"
1617
case shouldWipeSettings = "ShouldWipeSettings"
1718
}

ios/MullvadVPN.xcodeproj/project.pbxproj

+36
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,13 @@
495495
7A5869A82B5140C200640D27 /* MethodSettingsValidationErrorContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869A72B5140C200640D27 /* MethodSettingsValidationErrorContentView.swift */; };
496496
7A5869AB2B55527C00640D27 /* IPOverrideCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869AA2B55527C00640D27 /* IPOverrideCoordinator.swift */; };
497497
7A5869AD2B5552E200640D27 /* IPOverrideViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869AC2B5552E200640D27 /* IPOverrideViewController.swift */; };
498+
7A5869B72B56B41500640D27 /* IPOverrideTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869B62B56B41500640D27 /* IPOverrideTextViewController.swift */; };
499+
7A5869B92B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869B82B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift */; };
500+
7A5869BC2B56EF3400640D27 /* IPOverrideRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869BA2B56EE9500640D27 /* IPOverrideRepository.swift */; };
501+
7A5869BD2B56EF7300640D27 /* IPOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869B22B5697AC00640D27 /* IPOverride.swift */; };
502+
7A5869BF2B57D0A100640D27 /* IPOverrideStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */; };
503+
7A5869C12B57D21A00640D27 /* IPOverrideStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */; };
504+
7A5869C32B5820CE00640D27 /* IPOverrideRepositoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869C22B5820CE00640D27 /* IPOverrideRepositoryTests.swift */; };
498505
7A5869C52B5A899C00640D27 /* MethodSettingsCellConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869C42B5A899C00640D27 /* MethodSettingsCellConfiguration.swift */; };
499506
7A5869C72B5A8E4C00640D27 /* MethodSettingsDataSourceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869C62B5A8E4C00640D27 /* MethodSettingsDataSourceConfiguration.swift */; };
500507
7A6000F62B60092F001CF0D9 /* AccessMethodViewModelEditing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6000F52B60092F001CF0D9 /* AccessMethodViewModelEditing.swift */; };
@@ -539,6 +546,8 @@
539546
7A9CCCC42A96302800DD6A34 /* TunnelCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */; };
540547
7A9FA1422A2E3306000B728D /* CheckboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1412A2E3306000B728D /* CheckboxView.swift */; };
541548
7A9FA1442A2E3FE5000B728D /* CheckableSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */; };
549+
7AB4CCB92B69097E006037F5 /* IPOverrideTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */; };
550+
7AB4CCBB2B691BBB006037F5 /* IPOverrideInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB4CCBA2B691BBB006037F5 /* IPOverrideInteractor.swift */; };
542551
7ABCA5B32A9349F20044A708 /* Routing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A88DCCE2A8FABBE00D2FF0E /* Routing.framework */; };
543552
7ABCA5B42A9349F20044A708 /* Routing.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7A88DCCE2A8FABBE00D2FF0E /* Routing.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
544553
7ABCA5B72A9353C60044A708 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAF9F72983D36800BE19F7 /* Coordinator.swift */; };
@@ -1656,6 +1665,13 @@
16561665
7A5869A72B5140C200640D27 /* MethodSettingsValidationErrorContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MethodSettingsValidationErrorContentView.swift; sourceTree = "<group>"; };
16571666
7A5869AA2B55527C00640D27 /* IPOverrideCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideCoordinator.swift; sourceTree = "<group>"; };
16581667
7A5869AC2B5552E200640D27 /* IPOverrideViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideViewController.swift; sourceTree = "<group>"; };
1668+
7A5869B22B5697AC00640D27 /* IPOverride.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverride.swift; sourceTree = "<group>"; };
1669+
7A5869B62B56B41500640D27 /* IPOverrideTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideTextViewController.swift; sourceTree = "<group>"; };
1670+
7A5869B82B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideViewControllerDelegate.swift; sourceTree = "<group>"; };
1671+
7A5869BA2B56EE9500640D27 /* IPOverrideRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideRepository.swift; sourceTree = "<group>"; };
1672+
7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideStatus.swift; sourceTree = "<group>"; };
1673+
7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideStatusView.swift; sourceTree = "<group>"; };
1674+
7A5869C22B5820CE00640D27 /* IPOverrideRepositoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideRepositoryTests.swift; sourceTree = "<group>"; };
16591675
7A5869C42B5A899C00640D27 /* MethodSettingsCellConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MethodSettingsCellConfiguration.swift; sourceTree = "<group>"; };
16601676
7A5869C62B5A8E4C00640D27 /* MethodSettingsDataSourceConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MethodSettingsDataSourceConfiguration.swift; sourceTree = "<group>"; };
16611677
7A6000F52B60092F001CF0D9 /* AccessMethodViewModelEditing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodViewModelEditing.swift; sourceTree = "<group>"; };
@@ -1696,6 +1712,8 @@
16961712
7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelCoordinator.swift; sourceTree = "<group>"; };
16971713
7A9FA1412A2E3306000B728D /* CheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxView.swift; sourceTree = "<group>"; };
16981714
7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckableSettingsCell.swift; sourceTree = "<group>"; };
1715+
7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideTests.swift; sourceTree = "<group>"; };
1716+
7AB4CCBA2B691BBB006037F5 /* IPOverrideInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideInteractor.swift; sourceTree = "<group>"; };
16991717
7ABE318C2A1CDD4500DF4963 /* UIFont+Weight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Weight.swift"; sourceTree = "<group>"; };
17001718
7AC8A3AD2ABC6FBB00DC4939 /* SettingsHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsHeaderView.swift; sourceTree = "<group>"; };
17011719
7AD0AA192AD69B6E00119E10 /* PacketTunnelActorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelActorProtocol.swift; sourceTree = "<group>"; };
@@ -2712,6 +2730,8 @@
27122730
58B0A2A4238EE67E00BC001D /* Info.plist */,
27132731
A9B6AC192ADE8FBB00F7802A /* InMemorySettingsStore.swift */,
27142732
F07BF2572A26112D00042943 /* InputTextFormatterTests.swift */,
2733+
7A5869C22B5820CE00640D27 /* IPOverrideRepositoryTests.swift */,
2734+
7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */,
27152735
A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */,
27162736
58C3FA652A38549D006A450A /* MockFileCache.swift */,
27172737
F09D04B42AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift */,
@@ -2770,6 +2790,8 @@
27702790
F0164EBB2B482E430020268D /* AppStorage.swift */,
27712791
A92ECC2B2A7803A50052F1B1 /* DeviceState.swift */,
27722792
580F8B8528197958002E0998 /* DNSSettings.swift */,
2793+
7A5869B22B5697AC00640D27 /* IPOverride.swift */,
2794+
7A5869BA2B56EE9500640D27 /* IPOverrideRepository.swift */,
27732795
06410DFD292CE18F00AFC18C /* KeychainSettingsStore.swift */,
27742796
068CE5732927B7A400A068BB /* Migration.swift */,
27752797
A9D96B192A8247C100A5C673 /* MigrationManager.swift */,
@@ -3243,7 +3265,12 @@
32433265
isa = PBXGroup;
32443266
children = (
32453267
7A5869AA2B55527C00640D27 /* IPOverrideCoordinator.swift */,
3268+
7AB4CCBA2B691BBB006037F5 /* IPOverrideInteractor.swift */,
3269+
7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */,
3270+
7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */,
3271+
7A5869B62B56B41500640D27 /* IPOverrideTextViewController.swift */,
32463272
7A5869AC2B5552E200640D27 /* IPOverrideViewController.swift */,
3273+
7A5869B82B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift */,
32473274
);
32483275
path = IPOverride;
32493276
sourceTree = "<group>";
@@ -4460,6 +4487,7 @@
44604487
A9A5FA402ACB05D90083449F /* DeviceCheckRemoteServiceProtocol.swift in Sources */,
44614488
A9A5FA412ACB05D90083449F /* DeviceStateAccessor.swift in Sources */,
44624489
A9A5FA422ACB05D90083449F /* DeviceStateAccessorProtocol.swift in Sources */,
4490+
7A5869C32B5820CE00640D27 /* IPOverrideRepositoryTests.swift in Sources */,
44634491
A9A5FA392ACB05910083449F /* UIColor+Palette.swift in Sources */,
44644492
A9A5FA3A2ACB05910083449F /* UIEdgeInsets+Extensions.swift in Sources */,
44654493
A9C342C52ACC42130045F00E /* ServerRelaysResponse+Stubs.swift in Sources */,
@@ -4575,6 +4603,7 @@
45754603
58DFF7D32B02570000F864E0 /* MarkdownStylingOptions.swift in Sources */,
45764604
A9A5FA342ACB05160083449F /* StringTests.swift in Sources */,
45774605
A9A5FA352ACB05160083449F /* WgKeyRotationTests.swift in Sources */,
4606+
7AB4CCB92B69097E006037F5 /* IPOverrideTests.swift in Sources */,
45784607
A9A5FA362ACB05160083449F /* TunnelManagerTests.swift in Sources */,
45794608
);
45804609
runOnlyForDeploymentPostprocessing = 0;
@@ -4583,6 +4612,7 @@
45834612
isa = PBXSourcesBuildPhase;
45844613
buildActionMask = 2147483647;
45854614
files = (
4615+
7A5869BD2B56EF7300640D27 /* IPOverride.swift in Sources */,
45864616
58B2FDEE2AA72098003EB5C6 /* ApplicationConfiguration.swift in Sources */,
45874617
58B2FDE52AA71D5C003EB5C6 /* TunnelSettingsV2.swift in Sources */,
45884618
A97D30172AE6B5E90045C0E4 /* StoredWgKeyData.swift in Sources */,
@@ -4606,6 +4636,7 @@
46064636
F08827892B3192110020A383 /* AccessMethodRepositoryProtocol.swift in Sources */,
46074637
58B2FDE22AA71D5C003EB5C6 /* StoredAccountData.swift in Sources */,
46084638
F0D7FF902B31E00B00E0FDE5 /* AccessMethodKind.swift in Sources */,
4639+
7A5869BC2B56EF3400640D27 /* IPOverrideRepository.swift in Sources */,
46094640
58B2FDE82AA71D5C003EB5C6 /* KeychainSettingsStore.swift in Sources */,
46104641
);
46114642
runOnlyForDeploymentPostprocessing = 0;
@@ -4882,6 +4913,7 @@
48824913
5888AD83227B11080051EB06 /* SelectLocationCell.swift in Sources */,
48834914
5891BF1C25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift in Sources */,
48844915
5878A26F2907E7E00096FC88 /* ProblemReportInteractor.swift in Sources */,
4916+
7AB4CCBB2B691BBB006037F5 /* IPOverrideInteractor.swift in Sources */,
48854917
7A3353912AAA014400F0A71C /* SimulatorVPNConnection.swift in Sources */,
48864918
F028A56A2A34D4E700C0CAA3 /* RedeemVoucherViewController.swift in Sources */,
48874919
7A5869C52B5A899C00640D27 /* MethodSettingsCellConfiguration.swift in Sources */,
@@ -4911,6 +4943,7 @@
49114943
58C8191829FAA2C400DEB1B4 /* NotificationConfiguration.swift in Sources */,
49124944
58FF9FE82B07650A00E4C97D /* ButtonCellContentConfiguration.swift in Sources */,
49134945
5827B0A82B0F49EF00CCBBA1 /* ProxyConfigurationInteractorProtocol.swift in Sources */,
4946+
7A5869B92B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift in Sources */,
49144947
586C0D7A2B039CE300E7CDD7 /* ShadowsocksCipherPicker.swift in Sources */,
49154948
58EFC76A2AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift in Sources */,
49164949
58B93A1326C3F13600A55733 /* TunnelState.swift in Sources */,
@@ -4953,6 +4986,7 @@
49534986
7AF10EB42ADE85BC00C090B9 /* RelayFilterCoordinator.swift in Sources */,
49544987
58FB865526E8BF3100F188BC /* StorePaymentManagerError.swift in Sources */,
49554988
F09D04B32AE919AC003D4F89 /* OutgoingConnectionProxy.swift in Sources */,
4989+
7A5869BF2B57D0A100640D27 /* IPOverrideStatus.swift in Sources */,
49564990
58FD5BF42428C67600112C88 /* InAppPurchaseButton.swift in Sources */,
49574991
7AF10EB22ADE859200C090B9 /* AlertViewController.swift in Sources */,
49584992
587D9676288989DB00CD8F1C /* NSLayoutConstraint+Helpers.swift in Sources */,
@@ -4976,6 +5010,7 @@
49765010
5871167F2910035700D41AAC /* PreferencesInteractor.swift in Sources */,
49775011
7A9CCCC22A96302800DD6A34 /* SafariCoordinator.swift in Sources */,
49785012
58CEB3082AFD484100E6E088 /* BasicCell.swift in Sources */,
5013+
7A5869C12B57D21A00640D27 /* IPOverrideStatusView.swift in Sources */,
49795014
58CEB2F52AFD0BB500E6E088 /* TextCellContentConfiguration.swift in Sources */,
49805015
58E20771274672CA00DE5D77 /* LaunchViewController.swift in Sources */,
49815016
F0E8CC032A4C753B007ED3B4 /* WelcomeViewController.swift in Sources */,
@@ -4999,6 +5034,7 @@
49995034
585B1FF02AB09F97008AD470 /* VPNConnectionProtocol.swift in Sources */,
50005035
58C3A4B222456F1B00340BDB /* AccountInputGroupView.swift in Sources */,
50015036
F09A297C2A9F8A9B00EA3B6F /* VoucherTextField.swift in Sources */,
5037+
7A5869B72B56B41500640D27 /* IPOverrideTextViewController.swift in Sources */,
50025038
58ACF64B26553C3F00ACE4B7 /* SettingsSwitchCell.swift in Sources */,
50035039
7AF9BE952A40461100DBFEDB /* RelayFilterView.swift in Sources */,
50045040
7A09C98129D99215000C2CAC /* String+FuzzyMatch.swift in Sources */,

ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel+Persistent.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ extension AccessMethodViewModel {
2828

2929
do {
3030
configuration = try intoPersistentProxyConfiguration()
31-
} catch var error as AccessMethodValidationError {
31+
} catch let error as AccessMethodValidationError {
3232
var fieldErrors = error.fieldErrors
3333

3434
do {

0 commit comments

Comments
 (0)