summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@mullvad.net>2025-01-16 14:36:09 +0100
committerJon Petersson <jon.petersson@mullvad.net>2025-01-16 14:36:09 +0100
commit0751971715cfce2222f10bf7a43dd12c2c357e3d (patch)
tree34f971080236cfe78287ed740654c663f36ad300
parentf7aef11825d1853eaa6723cef800ed9cfeb84db1 (diff)
parente00402830ab0373d5b2e4ac4d1e095fbdfcc4926 (diff)
downloadmullvadvpn-0751971715cfce2222f10bf7a43dd12c2c357e3d.tar.xz
mullvadvpn-0751971715cfce2222f10bf7a43dd12c2c357e3d.zip
Merge branch 'fix-end-to-end-and-screenshot-tests-ios-999'
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj38
-rw-r--r--ios/MullvadVPN/Classes/AccessbilityIdentifier.swift1
-rw-r--r--ios/MullvadVPN/Coordinators/TunnelCoordinator.swift10
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ActivityIndicator.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ActivityIndicator.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift329
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ButtonPanel.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ButtonPanel.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipContainerView.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipContainerView.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipFeature.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipModel.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipModel.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipView.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipView.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipViewModelProtocol.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipViewModelProtocol.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionView.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewComponentPreview.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewViewModel.swift)10
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DetailsContainer.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsContainer.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DetailsView.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsView.swift)28
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicators/FeatureIndicatorsView.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicators/FeatureIndicatorsViewModel.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsViewModel.swift)0
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/HeaderView.swift (renamed from ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/HeaderView.swift)3
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/DisconnectSplitButton.swift55
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TranslucentButtonBlurView.swift84
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift398
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift72
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift119
-rw-r--r--ios/MullvadVPNUITests/ConnectivityTests.swift4
-rw-r--r--ios/MullvadVPNUITests/Pages/TunnelControlPage.swift75
-rw-r--r--ios/MullvadVPNUITests/RelayTests.swift96
-rw-r--r--ios/MullvadVPNUITests/Screenshots/ScreenshotTests.swift2
28 files changed, 184 insertions, 1140 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 664e6cbe93..ec046d9f94 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -146,7 +146,6 @@
585E820327F3285E00939F0E /* SendStoreReceiptOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E820227F3285E00939F0E /* SendStoreReceiptOperation.swift */; };
58607A4D2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58607A4C2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift */; };
586168692976F6BD00EF8598 /* DisplayError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586168682976F6BD00EF8598 /* DisplayError.swift */; };
- 5862805422428EF100F5A6E1 /* TranslucentButtonBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */; };
5864AF0729C78843005B0CD9 /* SettingsCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5864AF0029C7879B005B0CD9 /* SettingsCellFactory.swift */; };
5864AF0829C78849005B0CD9 /* CellFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5864AF0129C7879B005B0CD9 /* CellFactoryProtocol.swift */; };
5864AF0929C78850005B0CD9 /* VPNSettingsCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5864AF0229C7879B005B0CD9 /* VPNSettingsCellFactory.swift */; };
@@ -221,7 +220,6 @@
588D7EE02AF3A595005DF40A /* ListAccessMethodInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588D7EDF2AF3A595005DF40A /* ListAccessMethodInteractor.swift */; };
588E4EAE28FEEDD8008046E3 /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; };
58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */; };
- 58907D9524D17B4E00CFC3F5 /* DisconnectSplitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */; };
58915D682A25FA080066445B /* DeviceCheckRemoteService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58915D672A25FA080066445B /* DeviceCheckRemoteService.swift */; };
58915D6E2A26037A0066445B /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58915D6D2A26037A0066445B /* WireGuardKitTypes */; };
5891BF1C25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */; };
@@ -235,7 +233,6 @@
589A455D28E094BF00565204 /* OperationObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583E1E292848DF67004838B3 /* OperationObserverTests.swift */; };
589A455F28E094BF00565204 /* OperationConditionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580CBFB72848D503007878F0 /* OperationConditionTests.swift */; };
589E76C02A9378F100E502F3 /* RESTRequestExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 589E76BF2A9378F100E502F3 /* RESTRequestExecutor.swift */; };
- 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */; };
58A8EE5A2976BFBB009C0F8D /* SKError+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A8EE592976BFBB009C0F8D /* SKError+Localized.swift */; };
58A8EE5E2976DB00009C0F8D /* StorePaymentManagerError+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A8EE5D2976DB00009C0F8D /* StorePaymentManagerError+Display.swift */; };
58A99ED3240014A0006599E9 /* TermsOfServiceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A99ED2240014A0006599E9 /* TermsOfServiceViewController.swift */; };
@@ -270,7 +267,6 @@
58B2FDEB2AA72049003EB5C6 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58B2FDEA2AA72049003EB5C6 /* WireGuardKitTypes */; };
58B2FDEE2AA72098003EB5C6 /* ApplicationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */; };
58B2FDEF2AA720C4003EB5C6 /* ApplicationTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C76A072A33850E00100D75 /* ApplicationTarget.swift */; };
- 58B43C1925F77DB60002C8C3 /* TunnelControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B43C1825F77DB60002C8C3 /* TunnelControlView.swift */; };
58B465702A98C53300467203 /* RequestExecutorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B4656F2A98C53300467203 /* RequestExecutorTests.swift */; };
58B93A1326C3F13600A55733 /* TunnelState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B93A1226C3F13600A55733 /* TunnelState.swift */; };
58B993B12608A34500BA7811 /* LoginContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B993B02608A34500BA7811 /* LoginContentView.swift */; };
@@ -311,7 +307,6 @@
58C9B8CE2ABB252E00040B46 /* DeviceCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FE65922AB1CDE000E53CB5 /* DeviceCheck.swift */; };
58CAFA002983FF0200BE19F7 /* LoginInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAF9FF2983FF0200BE19F7 /* LoginInteractor.swift */; };
58CAFA032985367600BE19F7 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAFA01298530DC00BE19F7 /* Promise.swift */; };
- 58CCA010224249A1004F3011 /* TunnelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA00F224249A1004F3011 /* TunnelViewController.swift */; };
58CCA01222424D11004F3011 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA01122424D11004F3011 /* SettingsViewController.swift */; };
58CCA0162242560B004F3011 /* UIColor+Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA0152242560B004F3011 /* UIColor+Palette.swift */; };
58CCA01822426713004F3011 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA01722426713004F3011 /* AccountViewController.swift */; };
@@ -669,7 +664,7 @@
7AF9BE952A40461100DBFEDB /* RelayFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE942A40461100DBFEDB /* RelayFilterView.swift */; };
7AF9BE972A41C71F00DBFEDB /* ChipViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE962A41C71F00DBFEDB /* ChipViewCell.swift */; };
7AFBE3872D084C9D002335FC /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE3862D084C96002335FC /* ActivityIndicator.swift */; };
- 7AFBE3892D089163002335FC /* FI_TunnelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE3882D08915D002335FC /* FI_TunnelViewController.swift */; };
+ 7AFBE3892D089163002335FC /* TunnelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE3882D08915D002335FC /* TunnelViewController.swift */; };
7AFBE38B2D09AAFF002335FC /* SinglehopPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE38A2D09AAFF002335FC /* SinglehopPicker.swift */; };
7AFBE38D2D09AB2E002335FC /* MultihopPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE38C2D09AB2E002335FC /* MultihopPicker.swift */; };
850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DA2B503D7700EF8C96 /* RelayTests.swift */; };
@@ -736,7 +731,6 @@
A90C48672C36BC2600DCB94C /* EphemeralPeerReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90C48662C36BC2600DCB94C /* EphemeralPeerReceiver.swift */; };
A90C48692C36BF3900DCB94C /* TunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90C48682C36BF3900DCB94C /* TunnelProvider.swift */; };
A91614D12B108D1B00F416EB /* TransportLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91614D02B108D1B00F416EB /* TransportLayer.swift */; };
- A91614D62B10B26B00F416EB /* TunnelControlViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91614D52B10B26B00F416EB /* TunnelControlViewModel.swift */; };
A917352129FAAA5200D5DCFD /* TransportStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */; };
A9173C322C36CCDD00F6A08C /* EphemeralPeerReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A557F42B7E3E5C0017ADA8 /* EphemeralPeerReceiver.swift */; };
A9173C372C36CD2B00F6A08C /* MullvadTypes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; platformFilter = ios; };
@@ -1608,7 +1602,6 @@
585E820227F3285E00939F0E /* SendStoreReceiptOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendStoreReceiptOperation.swift; sourceTree = "<group>"; };
58607A4C2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryInAppNotificationProvider.swift; sourceTree = "<group>"; };
586168682976F6BD00EF8598 /* DisplayError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayError.swift; sourceTree = "<group>"; };
- 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslucentButtonBlurView.swift; sourceTree = "<group>"; };
5864AF0029C7879B005B0CD9 /* SettingsCellFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsCellFactory.swift; sourceTree = "<group>"; };
5864AF0129C7879B005B0CD9 /* CellFactoryProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellFactoryProtocol.swift; sourceTree = "<group>"; };
5864AF0229C7879B005B0CD9 /* VPNSettingsCellFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNSettingsCellFactory.swift; sourceTree = "<group>"; };
@@ -1689,7 +1682,6 @@
588D7EDF2AF3A595005DF40A /* ListAccessMethodInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAccessMethodInteractor.swift; sourceTree = "<group>"; };
58900D0228BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FixedWidthInteger+Arithmetics.swift"; sourceTree = "<group>"; };
58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEProviderStopReason+Debug.swift"; sourceTree = "<group>"; };
- 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectSplitButton.swift; sourceTree = "<group>"; };
58915D622A25F8400066445B /* DeviceCheckOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceCheckOperationTests.swift; sourceTree = "<group>"; };
58915D672A25FA080066445B /* DeviceCheckRemoteService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceCheckRemoteService.swift; sourceTree = "<group>"; };
5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+ProductVersion.swift"; sourceTree = "<group>"; };
@@ -1714,7 +1706,6 @@
589D28812846306C00F9A7B3 /* GroupOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupOperation.swift; sourceTree = "<group>"; };
589E76BF2A9378F100E502F3 /* RESTRequestExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTRequestExecutor.swift; sourceTree = "<group>"; };
58A1AA8623F43901009F7EA6 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
- 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionPanelView.swift; sourceTree = "<group>"; };
58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WgStats.swift; sourceTree = "<group>"; };
58A8EE592976BFBB009C0F8D /* SKError+Localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SKError+Localized.swift"; sourceTree = "<group>"; };
58A8EE5D2976DB00009C0F8D /* StorePaymentManagerError+Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorePaymentManagerError+Display.swift"; sourceTree = "<group>"; };
@@ -1737,7 +1728,6 @@
58B26E292943545A00D5980C /* NotificationManagerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerDelegate.swift; sourceTree = "<group>"; };
58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadSettings.framework; sourceTree = BUILT_PRODUCTS_DIR; };
58B2FDD52AA71D2A003EB5C6 /* MullvadSettings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadSettings.h; sourceTree = "<group>"; };
- 58B43C1825F77DB60002C8C3 /* TunnelControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelControlView.swift; sourceTree = "<group>"; };
58B4656F2A98C53300467203 /* RequestExecutorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestExecutorTests.swift; sourceTree = "<group>"; };
58B93A1226C3F13600A55733 /* TunnelState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelState.swift; sourceTree = "<group>"; };
58B993B02608A34500BA7811 /* LoginContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginContentView.swift; sourceTree = "<group>"; };
@@ -1766,7 +1756,6 @@
58CAF9FF2983FF0200BE19F7 /* LoginInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginInteractor.swift; sourceTree = "<group>"; };
58CAFA01298530DC00BE19F7 /* Promise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = "<group>"; };
58CC40EE24A601900019D96E /* ObserverList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObserverList.swift; sourceTree = "<group>"; };
- 58CCA00F224249A1004F3011 /* TunnelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelViewController.swift; sourceTree = "<group>"; };
58CCA01122424D11004F3011 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
58CCA0152242560B004F3011 /* UIColor+Palette.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Palette.swift"; sourceTree = "<group>"; };
58CCA01722426713004F3011 /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
@@ -2054,7 +2043,7 @@
7AF9BE942A40461100DBFEDB /* RelayFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterView.swift; sourceTree = "<group>"; };
7AF9BE962A41C71F00DBFEDB /* ChipViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChipViewCell.swift; sourceTree = "<group>"; };
7AFBE3862D084C96002335FC /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = "<group>"; };
- 7AFBE3882D08915D002335FC /* FI_TunnelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FI_TunnelViewController.swift; sourceTree = "<group>"; };
+ 7AFBE3882D08915D002335FC /* TunnelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelViewController.swift; sourceTree = "<group>"; };
7AFBE38A2D09AAFF002335FC /* SinglehopPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SinglehopPicker.swift; sourceTree = "<group>"; };
7AFBE38C2D09AB2E002335FC /* MultihopPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopPicker.swift; sourceTree = "<group>"; };
85006A8E2B73EF67004AD8FB /* MullvadVPNUITestsSmoke.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MullvadVPNUITestsSmoke.xctestplan; sourceTree = "<group>"; };
@@ -2132,7 +2121,6 @@
A90C48662C36BC2600DCB94C /* EphemeralPeerReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralPeerReceiver.swift; sourceTree = "<group>"; };
A90C48682C36BF3900DCB94C /* TunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelProvider.swift; sourceTree = "<group>"; };
A91614D02B108D1B00F416EB /* TransportLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransportLayer.swift; sourceTree = "<group>"; };
- A91614D52B10B26B00F416EB /* TunnelControlViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelControlViewModel.swift; sourceTree = "<group>"; };
A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransportStrategyTests.swift; sourceTree = "<group>"; };
A91EBED92C1337040004A84D /* RetryStrategyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryStrategyTests.swift; sourceTree = "<group>"; };
A92962582B1F4FDB00DFB93B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
@@ -2713,6 +2701,7 @@
isa = PBXGroup;
children = (
F0ADF1CF2D01B50B00299F09 /* ChipView */,
+ 7AA130972CFF364F00640DF9 /* FeatureIndicators */,
449E9A6E2D283C7400F8574A /* ButtonPanel.swift */,
7AA130982CFF365A00640DF9 /* ConnectionView.swift */,
449E9A6C2D283A2500F8574A /* ConnectionViewComponentPreview.swift */,
@@ -3063,17 +3052,13 @@
583FE01E29C197D5006E85F9 /* Tunnel */ = {
isa = PBXGroup;
children = (
- 7AA130972CFF364F00640DF9 /* FeatureIndicators */,
- 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */,
+ 4419AA862D28264D001B13C9 /* ConnectionView */,
+ 7AFBE3862D084C96002335FC /* ActivityIndicator.swift */,
5878A27A2909649A0096FC88 /* CustomOverlayRenderer.swift */,
- 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */,
58C3F4F82964B08300D72515 /* MapViewController.swift */,
F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */,
- 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */,
- 58B43C1825F77DB60002C8C3 /* TunnelControlView.swift */,
- 58CCA00F224249A1004F3011 /* TunnelViewController.swift */,
+ 7AFBE3882D08915D002335FC /* TunnelViewController.swift */,
5878A278290954790096FC88 /* TunnelViewControllerInteractor.swift */,
- A91614D52B10B26B00F416EB /* TunnelControlViewModel.swift */,
);
path = Tunnel;
sourceTree = "<group>";
@@ -4116,11 +4101,8 @@
7AA130972CFF364F00640DF9 /* FeatureIndicators */ = {
isa = PBXGroup;
children = (
- 4419AA862D28264D001B13C9 /* ConnectionView */,
- 7AFBE3862D084C96002335FC /* ActivityIndicator.swift */,
F0B4957B2D03154200CFEC2A /* FeatureIndicatorsView.swift */,
F0ADF1D22D01B6B400299F09 /* FeatureIndicatorsViewModel.swift */,
- 7AFBE3882D08915D002335FC /* FI_TunnelViewController.swift */,
);
path = FeatureIndicators;
sourceTree = "<group>";
@@ -5928,7 +5910,7 @@
7A8A19142CEF2548000BCB5B /* DAITATunnelSettingsViewModel.swift in Sources */,
7A8A18F92CE34EA8000BCB5B /* SettingsMultihopView.swift in Sources */,
44BB5F972BE527F4002520EB /* TunnelState+UI.swift in Sources */,
- 7AFBE3892D089163002335FC /* FI_TunnelViewController.swift in Sources */,
+ 7AFBE3892D089163002335FC /* TunnelViewController.swift in Sources */,
7A11DD0B2A9495D400098CD8 /* AppRoutes.swift in Sources */,
5827B0902B0CAA0500CCBBA1 /* EditAccessMethodCoordinator.swift in Sources */,
5846227126E229F20035F7C2 /* StoreSubscription.swift in Sources */,
@@ -5963,11 +5945,9 @@
58B26E2A2943545A00D5980C /* NotificationManagerDelegate.swift in Sources */,
7A8A19072CE4E9D3000BCB5B /* SettingsInfoView.swift in Sources */,
7AA1309B2D0048D800640DF9 /* MainButton.swift in Sources */,
- 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */,
5878A27B2909649A0096FC88 /* CustomOverlayRenderer.swift in Sources */,
7A0EAEA02D0333CE00D3EB8B /* Color+Helpers.swift in Sources */,
7A8A19052CE4E9A9000BCB5B /* SwitchRowView.swift in Sources */,
- A91614D62B10B26B00F416EB /* TunnelControlViewModel.swift in Sources */,
7A5869972B32EA4500640D27 /* AppButton.swift in Sources */,
586C0D8F2B03D88100E7CDD7 /* ProxyProtocolConfigurationItemIdentifier.swift in Sources */,
7A27E3CF2CBD4A8C0088BCFF /* SelectableSettingsDetailsCell.swift in Sources */,
@@ -5983,7 +5963,6 @@
58EFC7752AFB4CEF00E9F4CB /* AboutViewController.swift in Sources */,
5878A27129091CF20096FC88 /* AccountInteractor.swift in Sources */,
7AF9BE882A30C62100DBFEDB /* SelectableSettingsCell.swift in Sources */,
- 58CCA010224249A1004F3011 /* TunnelViewController.swift in Sources */,
F0B495782D02038B00CFEC2A /* ChipViewModelProtocol.swift in Sources */,
58CEB30A2AFD584700E6E088 /* CustomCellDisclosureHandling.swift in Sources */,
58B26E22294351EA00D5980C /* InAppNotificationProvider.swift in Sources */,
@@ -6051,7 +6030,6 @@
F0E8CC0C2A4EE672007ED3B4 /* SetupAccountCompletedController.swift in Sources */,
5846227726E22A7C0035F7C2 /* StorePaymentManagerDelegate.swift in Sources */,
58EF581125D69DB400AEBA94 /* StatusImageView.swift in Sources */,
- 58907D9524D17B4E00CFC3F5 /* DisconnectSplitButton.swift in Sources */,
58EE2E3B272FF814003BFF93 /* SettingsDataSourceDelegate.swift in Sources */,
5823FA5426CE49F700283BF8 /* TunnelObserver.swift in Sources */,
5888AD87227B17950051EB06 /* LocationViewController.swift in Sources */,
@@ -6130,7 +6108,6 @@
7A6F2FA72AFBB9AE006D0856 /* AccountExpiry.swift in Sources */,
5819C2172729595500D6EC38 /* SettingsAddDNSEntryCell.swift in Sources */,
7A1A26452A29CEF700B978AA /* RelayFilterViewController.swift in Sources */,
- 5862805422428EF100F5A6E1 /* TranslucentButtonBlurView.swift in Sources */,
587EB66A270EFACB00123C75 /* CharacterSet+IPAddress.swift in Sources */,
5888AD83227B11080051EB06 /* LocationCell.swift in Sources */,
5891BF1C25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift in Sources */,
@@ -6274,7 +6251,6 @@
581DFAEA2B176C51005D6D1C /* PersistentProxyConfiguration+ViewModel.swift in Sources */,
A99E5EE02B7628150033F241 /* ProblemReportViewModel.swift in Sources */,
58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */,
- 58B43C1925F77DB60002C8C3 /* TunnelControlView.swift in Sources */,
F0ADF1D12D01B55C00299F09 /* ChipModel.swift in Sources */,
F09A297B2A9F8A9B00EA3B6F /* LogoutDialogueView.swift in Sources */,
58CEB2FB2AFD13E600E6E088 /* UIListContentConfiguration+Extensions.swift in Sources */,
diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
index 1feeed7331..728684ff95 100644
--- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
+++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
@@ -157,6 +157,7 @@ public enum AccessibilityIdentifier: Equatable {
case connectionPanelInAddressRow
case connectionPanelOutAddressRow
case connectionPanelOutIpv6AddressRow
+ case connectionPanelServerLabel
case customSwitch
case customWireGuardPortTextField
case dnsContentBlockersHeaderView
diff --git a/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift b/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift
index 0c55f7e1af..4cfcb98b71 100644
--- a/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/TunnelCoordinator.swift
@@ -12,13 +12,7 @@ import UIKit
class TunnelCoordinator: Coordinator, Presenting {
private let tunnelManager: TunnelManager
-
- #if DEBUG
- private let controller: FI_TunnelViewController
- #else
private let controller: TunnelViewController
- #endif
-
private var tunnelObserver: TunnelObserver?
var presentationContext: UIViewController {
@@ -44,11 +38,7 @@ class TunnelCoordinator: Coordinator, Presenting {
ipOverrideRepository: ipOverrideRepository
)
- #if DEBUG
- controller = FI_TunnelViewController(interactor: interactor)
- #else
controller = TunnelViewController(interactor: interactor)
- #endif
super.init()
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ActivityIndicator.swift b/ios/MullvadVPN/View controllers/Tunnel/ActivityIndicator.swift
index 9b42bab8e6..9b42bab8e6 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ActivityIndicator.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ActivityIndicator.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift
deleted file mode 100644
index ab61264800..0000000000
--- a/ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift
+++ /dev/null
@@ -1,329 +0,0 @@
-//
-// ConnectionPanelView.swift
-// MullvadVPN
-//
-// Created by pronebird on 12/02/2020.
-// Copyright © 2020 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-import UIKit
-
-struct ConnectionPanelData {
- var inAddress: String
- var outAddress: String?
-}
-
-class ConnectionPanelView: UIView {
- var dataSource: ConnectionPanelData? {
- didSet {
- didChangeDataSource()
- }
- }
-
- var showsConnectionInfo = false {
- didSet {
- updateConnectionInfoVisibility()
- }
- }
-
- var connectedRelayName = "" {
- didSet {
- collapseView.setAccessibilityIdentifier(.relayStatusCollapseButton)
- collapseView.title.text = connectedRelayName
- collapseView.accessibilityLabel = NSLocalizedString(
- "RELAY_ACCESSIBILITY_LABEL",
- tableName: "ConnectionPanel",
- value: "Connected relay",
- comment: ""
- )
- collapseView.accessibilityAttributedValue = NSAttributedString(
- string: connectedRelayName.replacingOccurrences(
- of: "-wireguard",
- with: " WireGuard"
- ),
- attributes: [.accessibilitySpeechLanguage: "en"]
- )
- }
- }
-
- private let collapseView: ConnectionPanelCollapseView = {
- let collapseView = ConnectionPanelCollapseView()
- collapseView.axis = .horizontal
- collapseView.alignment = .top
- collapseView.distribution = .fill
- collapseView.translatesAutoresizingMaskIntoConstraints = false
- collapseView.tintColor = .white
- collapseView.isAccessibilityElement = false
- return collapseView
- }()
-
- private let inAddressRow = ConnectionPanelAddressRow()
- private let outAddressRow = ConnectionPanelAddressRow()
-
- private lazy var stackView: UIStackView = {
- let stackView = UIStackView(arrangedSubviews: [inAddressRow, outAddressRow])
- stackView.axis = .vertical
- stackView.translatesAutoresizingMaskIntoConstraints = false
- return stackView
- }()
-
- private let textLabelLayoutGuide: UILayoutGuide = {
- let layoutGuide = UILayoutGuide()
- layoutGuide.identifier = "TextLabelLayoutGuide"
- return layoutGuide
- }()
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- inAddressRow.translatesAutoresizingMaskIntoConstraints = false
- outAddressRow.translatesAutoresizingMaskIntoConstraints = false
-
- inAddressRow.setAccessibilityIdentifier(.connectionPanelInAddressRow)
- outAddressRow.setAccessibilityIdentifier(.connectionPanelOutAddressRow)
-
- inAddressRow.title = NSLocalizedString(
- "IN_ADDRESS_LABEL",
- tableName: "ConnectionPanel",
- value: "In",
- comment: ""
- )
- outAddressRow.title = NSLocalizedString(
- "OUT_ADDRESS_LABEL",
- tableName: "ConnectionPanel",
- value: "Out",
- comment: ""
- )
-
- addSubview(collapseView)
- addSubview(stackView)
- addLayoutGuide(textLabelLayoutGuide)
-
- NSLayoutConstraint.activate([
- collapseView.topAnchor.constraint(equalTo: topAnchor),
- collapseView.leadingAnchor.constraint(equalTo: leadingAnchor),
- collapseView.trailingAnchor.constraint(equalTo: trailingAnchor),
-
- stackView.topAnchor.constraint(equalTo: collapseView.bottomAnchor, constant: 4),
- stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
- stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
- stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
-
- inAddressRow.heightAnchor.constraint(equalToConstant: UIMetrics.ConnectionPanelView.inRowHeight),
- outAddressRow.heightAnchor.constraint(equalToConstant: UIMetrics.ConnectionPanelView.outRowHeight),
-
- // Align all text labels with the guide, so that they maintain equal width
- textLabelLayoutGuide.trailingAnchor
- .constraint(equalTo: inAddressRow.textLabelLayoutGuide.trailingAnchor),
- textLabelLayoutGuide.trailingAnchor
- .constraint(equalTo: outAddressRow.textLabelLayoutGuide.trailingAnchor),
- ])
-
- updateConnectionInfoVisibility()
- updateCollapseButtonAccessibilityHint()
-
- let longPressGestureRecognizer = UILongPressGestureRecognizer(
- target: self,
- action: #selector(toggleCollapse(_:))
- )
- longPressGestureRecognizer.minimumPressDuration = 0
- collapseView.addGestureRecognizer(longPressGestureRecognizer)
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- private func didChangeDataSource() {
- inAddressRow.value = dataSource?.inAddress
- inAddressRow.alpha = dataSource?.inAddress == nil ? 0 : 1.0
-
- outAddressRow.value = dataSource?.outAddress
- outAddressRow.alpha = dataSource?.outAddress == nil ? 0 : 1.0
- }
-
- private func toggleConnectionInfoVisibility() {
- showsConnectionInfo = !showsConnectionInfo
- }
-
- @objc private func toggleCollapse(_ sender: UILongPressGestureRecognizer) {
- switch sender.state {
- case .began:
- collapseView.title.textColor = .lightGray
- collapseView.imageView.tintColor = .lightGray
- case .ended:
- collapseView.title.textColor = .white
- collapseView.imageView.tintColor = .white
- toggleConnectionInfoVisibility()
- default:
- break
- }
- }
-
- private func updateConnectionInfoVisibility() {
- stackView.isHidden = !showsConnectionInfo
- collapseView.style = showsConnectionInfo ? .up : .down
-
- if collapseView.accessibilityElementIsFocused(), showsConnectionInfo {
- UIAccessibility.post(
- notification: .layoutChanged,
- argument: stackView.arrangedSubviews.first
- )
- }
- updateCollapseButtonAccessibilityHint()
- }
-
- private func updateCollapseButtonAccessibilityHint() {
- if showsConnectionInfo {
- collapseView.accessibilityHint = NSLocalizedString(
- "COLLAPSE_BUTTON_ACCESSIBILITY_HINT",
- tableName: "ConnectionPanel",
- value: "Double tap to collapse the connection info panel.",
- comment: ""
- )
- } else {
- collapseView.accessibilityHint = NSLocalizedString(
- "EXPAND_BUTTON_ACCESSIBILITY_HINT",
- tableName: "ConnectionPanel",
- value: "Double tap to expand the connection info panel.",
- comment: ""
- )
- }
- }
-}
-
-class ConnectionPanelAddressRow: UIView {
- private let textLabel: UILabel = {
- let textLabel = UILabel()
- textLabel.font = .systemFont(ofSize: 17)
- textLabel.textColor = .white
- textLabel.translatesAutoresizingMaskIntoConstraints = false
- textLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- return textLabel
- }()
-
- private let detailTextLabel: UILabel = {
- let detailTextLabel = UILabel()
- detailTextLabel.setAccessibilityIdentifier(.connectionPanelDetailLabel)
- detailTextLabel.font = .systemFont(ofSize: 17)
- detailTextLabel.textColor = .white
- detailTextLabel.translatesAutoresizingMaskIntoConstraints = false
- detailTextLabel.numberOfLines = .zero
- detailTextLabel.lineBreakMode = .byWordWrapping
- return detailTextLabel
- }()
-
- private lazy var stackView: UIStackView = {
- let stackView = UIStackView(arrangedSubviews: [textLabel, detailTextLabel])
- stackView.spacing = UIStackView.spacingUseSystem
- stackView.translatesAutoresizingMaskIntoConstraints = false
- stackView.alignment = .top
- return stackView
- }()
-
- let textLabelLayoutGuide = UILayoutGuide()
-
- var title: String? {
- get {
- textLabel.text
- }
- set {
- textLabel.text = newValue
- accessibilityLabel = newValue
- }
- }
-
- var value: String? {
- get {
- detailTextLabel.text
- }
- set {
- detailTextLabel.text = newValue
- accessibilityValue = newValue
- }
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- isAccessibilityElement = true
-
- addSubview(stackView)
- addLayoutGuide(textLabelLayoutGuide)
-
- NSLayoutConstraint.activate([
- stackView.topAnchor.constraint(equalTo: topAnchor),
- stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
- stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
- stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
-
- textLabelLayoutGuide.leadingAnchor.constraint(equalTo: textLabel.leadingAnchor),
- textLabelLayoutGuide.trailingAnchor.constraint(equalTo: textLabel.trailingAnchor),
- textLabelLayoutGuide.topAnchor.constraint(equalTo: textLabel.topAnchor),
- textLabelLayoutGuide.bottomAnchor.constraint(equalTo: textLabel.bottomAnchor),
- ])
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-}
-
-class ConnectionPanelCollapseView: UIStackView {
- enum Style {
- case up, down
-
- var image: UIImage? {
- switch self {
- case .up:
- return UIImage(named: "IconChevronUp")
- case .down:
- return UIImage(named: "IconChevronDown")
- }
- }
- }
-
- var style = Style.up {
- didSet {
- updateImage()
- }
- }
-
- private(set) var title: UILabel = {
- let button = UILabel()
- button.textColor = .white
- button.numberOfLines = 0
- return button
- }()
-
- private(set) var imageView: UIImageView = {
- let imageView = UIImageView()
- imageView.contentMode = .scaleAspectFit
- return imageView
- }()
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- addArrangedSubview(title)
- addArrangedSubview(imageView)
-
- title.setContentHuggingPriority(.defaultLow, for: .horizontal)
- title.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
-
- imageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- imageView.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
- addArrangedSubview(UIView()) // Pushes content left.
-
- updateImage()
- }
-
- required init(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- private func updateImage() {
- imageView.image = style.image
- }
-}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ButtonPanel.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ButtonPanel.swift
index 5159120046..5159120046 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ButtonPanel.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ButtonPanel.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipContainerView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipContainerView.swift
index addc6f92b5..addc6f92b5 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipContainerView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipContainerView.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipFeature.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift
index a661a2cefc..a661a2cefc 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipFeature.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipModel.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipModel.swift
index a746897c06..a746897c06 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipModel.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipModel.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipView.swift
index 57fc7cb042..57fc7cb042 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipView.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipViewModelProtocol.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipViewModelProtocol.swift
index 3a4c9da337..3a4c9da337 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ChipView/ChipViewModelProtocol.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipViewModelProtocol.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift
index 6dfee3abed..6dfee3abed 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionView.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewComponentPreview.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift
index cc24537f13..cc24537f13 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewComponentPreview.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift
index 962eaa0d63..96a47d5ab4 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewViewModel.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift
@@ -24,7 +24,7 @@ class ConnectionViewViewModel: ObservableObject {
case selectLocation
}
- @Published var tunnelStatus: TunnelStatus
+ @Published private(set) var tunnelStatus: TunnelStatus
@Published var outgoingConnectionInfo: OutgoingConnectionInfo?
@Published var showsActivityIndicator = false
@@ -46,6 +46,14 @@ class ConnectionViewViewModel: ObservableObject {
init(tunnelStatus: TunnelStatus) {
self.tunnelStatus = tunnelStatus
}
+
+ func update(tunnelStatus: TunnelStatus) {
+ self.tunnelStatus = tunnelStatus
+
+ if !tunnelIsConnected {
+ outgoingConnectionInfo = nil
+ }
+ }
}
extension ConnectionViewViewModel {
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsContainer.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DetailsContainer.swift
index 6b2bb00399..6b2bb00399 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsContainer.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DetailsContainer.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DetailsView.swift
index ff07dc94b5..87daea5046 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DetailsView.swift
@@ -24,17 +24,26 @@ extension ConnectionView {
VStack(alignment: .leading, spacing: 0) {
if let inAddress = viewModel.inAddress {
- connectionDetailRow(title: LocalizedStringKey("In"), value: inAddress)
- .accessibilityIdentifier(AccessibilityIdentifier.connectionPanelInAddressRow.asString)
+ connectionDetailRow(
+ title: LocalizedStringKey("In"),
+ value: inAddress,
+ accessibilityId: .connectionPanelInAddressRow
+ )
}
if viewModel.tunnelIsConnected {
if let outAddressIpv4 = viewModel.outAddressIpv4 {
- connectionDetailRow(title: LocalizedStringKey("Out IPv4"), value: outAddressIpv4)
- .accessibilityIdentifier(AccessibilityIdentifier.connectionPanelOutAddressRow.asString)
+ connectionDetailRow(
+ title: LocalizedStringKey("Out IPv4"),
+ value: outAddressIpv4,
+ accessibilityId: .connectionPanelOutAddressRow
+ )
}
if let outAddressIpv6 = viewModel.outAddressIpv6 {
- connectionDetailRow(title: LocalizedStringKey("Out IPv6"), value: outAddressIpv6)
- .accessibilityIdentifier(AccessibilityIdentifier.connectionPanelOutAddressRow.asString)
+ connectionDetailRow(
+ title: LocalizedStringKey("Out IPv6"),
+ value: outAddressIpv6,
+ accessibilityId: .connectionPanelOutAddressRow
+ )
}
}
}
@@ -42,7 +51,11 @@ extension ConnectionView {
}
@ViewBuilder
- private func connectionDetailRow(title: LocalizedStringKey, value: String) -> some View {
+ private func connectionDetailRow(
+ title: LocalizedStringKey,
+ value: String,
+ accessibilityId: AccessibilityIdentifier
+ ) -> some View {
HStack(alignment: .top, spacing: 8) {
Text(title)
.font(.subheadline)
@@ -52,6 +65,7 @@ extension ConnectionView {
Text(value)
.font(.subheadline)
.foregroundStyle(UIColor.primaryTextColor.color)
+ .accessibilityIdentifier(accessibilityId.asString)
}
}
}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicators/FeatureIndicatorsView.swift
index 70e49a9c04..70e49a9c04 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicators/FeatureIndicatorsView.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicators/FeatureIndicatorsViewModel.swift
index 97eac59ca8..97eac59ca8 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsViewModel.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicators/FeatureIndicatorsViewModel.swift
diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/HeaderView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/HeaderView.swift
index fa1112a80e..388754f596 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/HeaderView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/HeaderView.swift
@@ -21,6 +21,7 @@ extension ConnectionView {
.font(.title3.weight(.semibold))
.foregroundStyle(viewModel.textColorForSecureLabel.color)
.accessibilityIdentifier(viewModel.accessibilityIdForSecureLabel.asString)
+ .accessibilityLabel(viewModel.localizedAccessibilityLabelForSecureLabel)
if let countryAndCity = viewModel.titleForCountryAndCity {
Text(countryAndCity)
@@ -34,9 +35,9 @@ extension ConnectionView {
.font(.body)
.foregroundStyle(UIColor.primaryTextColor.color.opacity(0.6))
.padding(.top, 2)
+ .accessibilityIdentifier(AccessibilityIdentifier.connectionPanelServerLabel.asString)
}
}
- .accessibilityLabel(viewModel.localizedAccessibilityLabelForSecureLabel)
Group {
Spacer()
diff --git a/ios/MullvadVPN/View controllers/Tunnel/DisconnectSplitButton.swift b/ios/MullvadVPN/View controllers/Tunnel/DisconnectSplitButton.swift
deleted file mode 100644
index 640d799de3..0000000000
--- a/ios/MullvadVPN/View controllers/Tunnel/DisconnectSplitButton.swift
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// DisconnectSplitButton.swift
-// MullvadVPN
-//
-// Created by pronebird on 29/07/2020.
-// Copyright © 2020 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-import UIKit
-
-class DisconnectSplitButton: UIView {
- let primaryButton = AppButton(style: .translucentDangerSplitLeft)
- let secondaryButton = AppButton(style: .translucentDangerSplitRight)
-
- override init(frame: CGRect) {
- super.init(frame: .zero)
- commonInit()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- private func commonInit() {
- let primaryButtonBlurView = TranslucentButtonBlurView(button: primaryButton)
- let secondaryButtonBlurView = TranslucentButtonBlurView(button: secondaryButton)
-
- let stackView = UIStackView(arrangedSubviews: [primaryButtonBlurView, secondaryButtonBlurView])
- stackView.translatesAutoresizingMaskIntoConstraints = false
- stackView.axis = .horizontal
- stackView.distribution = .fill
- stackView.alignment = .fill
- stackView.spacing = 1
-
- let secondaryButtonSize = UIMetrics.DisconnectSplitButton.secondaryButton
-
- addConstrainedSubviews([stackView]) {
- stackView.pinEdgesToSuperview()
-
- secondaryButton.widthAnchor.constraint(equalToConstant: secondaryButtonSize.width)
- secondaryButton.heightAnchor.constraint(equalToConstant: secondaryButtonSize.height)
- }
-
- primaryButton.configuration?.contentInsets.leading += secondaryButtonSize.width
-
- // Ideally, we shouldn't need to manually resize the image ourselves.
- // However, since UIButton.Configuration doesn't provide a direct property
- // for controlling image scaling (like imageScaling or contentMode in other contexts),
- // manual resizing has been one approach to ensure the image fits within bounds.
- secondaryButton.configuration?.image = UIImage(resource: .iconReload)
- .resizeImage(targetSize: secondaryButtonSize.deducting(insets: secondaryButton.defaultContentInsets))
- .imageFlippedForRightToLeftLayoutDirection()
- }
-}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TranslucentButtonBlurView.swift b/ios/MullvadVPN/View controllers/Tunnel/TranslucentButtonBlurView.swift
deleted file mode 100644
index 5f27e8be5a..0000000000
--- a/ios/MullvadVPN/View controllers/Tunnel/TranslucentButtonBlurView.swift
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-// TranslucentButtonBlurView.swift
-// MullvadVPN
-//
-// Created by pronebird on 20/03/2019.
-// Copyright © 2019 Mullvad VPN AB. All rights reserved.
-//
-
-import UIKit
-
-class TranslucentButtonBlurView: UIVisualEffectView {
- let appButton: AppButton
-
- var isEnabled: Bool {
- didSet {
- appButton.isEnabled = isEnabled
- effect = appButton.blurEffect(isEnabled: isEnabled)
- }
- }
-
- init(button: AppButton) {
- appButton = button
- isEnabled = button.isEnabled
-
- let effect = appButton.blurEffect(isEnabled: isEnabled)
- super.init(effect: effect)
-
- contentView.addConstrainedSubviews([button]) {
- button.pinEdgesToSuperview()
- }
-
- layer.cornerRadius = UIMetrics.controlCornerRadius
- layer.maskedCorners = button.style.cornerMask(effectiveUserInterfaceLayoutDirection)
- layer.masksToBounds = true
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-}
-
-private extension AppButton {
- func blurEffect(isEnabled: Bool) -> UIBlurEffect {
- let style = isEnabled ? style.blurEffectStyle : style.disabledStateBlurEffectStyle
- return UIBlurEffect(style: style)
- }
-}
-
-private extension AppButton.Style {
- func cornerMask(_ userInterfaceLayoutDirection: UIUserInterfaceLayoutDirection)
- -> CACornerMask {
- switch (self, userInterfaceLayoutDirection) {
- case (.translucentDangerSplitLeft, .leftToRight),
- (.translucentDangerSplitRight, .rightToLeft):
- return [.layerMinXMinYCorner, .layerMinXMaxYCorner]
- case (.translucentDangerSplitRight, .leftToRight),
- (.translucentDangerSplitLeft, .rightToLeft):
- return [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
- default:
- return [
- .layerMinXMinYCorner, .layerMinXMaxYCorner,
- .layerMaxXMinYCorner, .layerMaxXMaxYCorner,
- ]
- }
- }
-
- var blurEffectStyle: UIBlurEffect.Style {
- switch self {
- case .translucentDangerSplitLeft, .translucentDangerSplitRight, .translucentDanger:
- return .systemUltraThinMaterialDark
- default:
- return .light
- }
- }
-
- var disabledStateBlurEffectStyle: UIBlurEffect.Style {
- switch self {
- case .success, .translucentNeutral:
- return .systemThinMaterialDark
- default:
- return blurEffectStyle
- }
- }
-}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
deleted file mode 100644
index cff5222976..0000000000
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
+++ /dev/null
@@ -1,398 +0,0 @@
-//
-// TunnelControlView.swift
-// MullvadVPN
-//
-// Created by pronebird on 09/03/2021.
-// Copyright © 2021 Mullvad VPN AB. All rights reserved.
-//
-
-import MapKit
-import MullvadREST
-import MullvadTypes
-import PacketTunnelCore
-import UIKit
-
-enum TunnelControlAction {
- case connect
- case disconnect
- case cancel
- case reconnect
- case selectLocation
-}
-
-final class TunnelControlView: UIView {
- private let secureLabel = makeBoldTextLabel(ofSize: 20, numberOfLines: 0)
- private let cityLabel = makeBoldTextLabel(ofSize: 34)
- private let countryLabel = makeBoldTextLabel(ofSize: 34)
-
- private let activityIndicator: SpinnerActivityIndicatorView = {
- let activityIndicator = SpinnerActivityIndicatorView(style: .large)
- activityIndicator.translatesAutoresizingMaskIntoConstraints = false
- activityIndicator.tintColor = .white
- activityIndicator.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- activityIndicator.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
- return activityIndicator
- }()
-
- private let locationContainerView: UIStackView = {
- let view = UIStackView()
- view.translatesAutoresizingMaskIntoConstraints = false
- view.isAccessibilityElement = false
- view.accessibilityTraits = .summaryElement
- view.axis = .vertical
- view.spacing = 8
- return view
- }()
-
- private let connectionPanel: ConnectionPanelView = {
- let view = ConnectionPanelView()
- view.translatesAutoresizingMaskIntoConstraints = false
- return view
- }()
-
- private let buttonsStackView: UIStackView = {
- let stackView = UIStackView()
- stackView.spacing = UIMetrics.interButtonSpacing
- stackView.axis = .vertical
- stackView.translatesAutoresizingMaskIntoConstraints = false
- return stackView
- }()
-
- private let connectButton: AppButton = {
- let button = AppButton(style: .success)
- button.setAccessibilityIdentifier(.connectButton)
- button.translatesAutoresizingMaskIntoConstraints = false
- return button
- }()
-
- private let cancelButton: AppButton = {
- let button = AppButton(style: .translucentDanger)
- button.setAccessibilityIdentifier(.cancelButton)
- button.translatesAutoresizingMaskIntoConstraints = false
- return button
- }()
-
- private let selectLocationButton: AppButton = {
- let button = AppButton(style: .translucentNeutral)
- button.setAccessibilityIdentifier(.selectLocationButton)
- button.translatesAutoresizingMaskIntoConstraints = false
- return button
- }()
-
- private let selectLocationButtonBlurView: TranslucentButtonBlurView
- private let connectButtonBlurView: TranslucentButtonBlurView
- private let cancelButtonBlurView: TranslucentButtonBlurView
-
- private let splitDisconnectButton: DisconnectSplitButton = {
- let button = DisconnectSplitButton()
- button.primaryButton.setAccessibilityIdentifier(.disconnectButton)
- button.translatesAutoresizingMaskIntoConstraints = false
- return button
- }()
-
- private let containerView: UIView = {
- let view = UIView()
- view.translatesAutoresizingMaskIntoConstraints = false
- return view
- }()
-
- private var traitConstraints = [NSLayoutConstraint]()
- private var viewModel: TunnelControlViewModel?
-
- var actionHandler: ((TunnelControlAction) -> Void)?
-
- var mapCenterAlignmentView: UIView {
- activityIndicator
- }
-
- override init(frame: CGRect) {
- selectLocationButtonBlurView = TranslucentButtonBlurView(button: selectLocationButton)
- connectButtonBlurView = TranslucentButtonBlurView(button: connectButton)
- cancelButtonBlurView = TranslucentButtonBlurView(button: cancelButton)
-
- super.init(frame: frame)
-
- backgroundColor = .clear
- directionalLayoutMargins = UIMetrics.contentLayoutMargins
- accessibilityContainerType = .semanticGroup
- setAccessibilityIdentifier(.connectionView)
-
- addSubviews()
- addButtonHandlers()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- func update(with model: TunnelControlViewModel) {
- viewModel = model
- let tunnelState = model.tunnelStatus.state
- secureLabel.text = model.secureLabelText
- secureLabel.textColor = tunnelState.textColorForSecureLabel
- selectLocationButtonBlurView.isEnabled = model.enableButtons
- connectButtonBlurView.isEnabled = model.enableButtons
- cityLabel.attributedText = attributedStringForLocation(string: model.city)
- countryLabel.attributedText = attributedStringForLocation(string: model.country)
- connectionPanel.connectedRelayName = model.connectedRelaysName
- connectionPanel.dataSource = model.connectionPanel
-
- updateSecureLabel(tunnelState: tunnelState)
- updateActionButtons(tunnelState: tunnelState)
- updateTunnelRelays(tunnelStatus: model.tunnelStatus)
- }
-
- func setAnimatingActivity(_ isAnimating: Bool) {
- if isAnimating {
- activityIndicator.startAnimating()
- } else {
- activityIndicator.stopAnimating()
- }
- }
-
- private func updateActionButtons(tunnelState: TunnelState) {
- let view = view(forActionButton: tunnelState.actionButton)
-
- updateButtonTitles(tunnelState: tunnelState)
- updateButtonEnabledStates(shouldEnableButtons: tunnelState.shouldEnableButtons)
- setArrangedButtons([selectLocationButtonBlurView, view])
- }
-
- private func updateSecureLabel(tunnelState: TunnelState) {
- secureLabel.text = tunnelState.localizedTitleForSecureLabel.uppercased()
- secureLabel.textColor = tunnelState.textColorForSecureLabel
-
- switch tunnelState {
- case .connected:
- secureLabel.setAccessibilityIdentifier(.connectionStatusConnectedLabel)
- case .connecting:
- secureLabel.setAccessibilityIdentifier(.connectionStatusConnectingLabel)
- default:
- secureLabel.setAccessibilityIdentifier(.connectionStatusNotConnectedLabel)
- }
- }
-
- private func updateButtonTitles(tunnelState: TunnelState) {
- connectButton.setTitle(
- NSLocalizedString(
- "CONNECT_BUTTON_TITLE",
- tableName: "Main",
- value: "Secure connection",
- comment: ""
- ), for: .normal
- )
- selectLocationButton.setTitle(
- tunnelState.localizedTitleForSelectLocationButton,
- for: .normal
- )
- cancelButton.setTitle(
- NSLocalizedString(
- "CANCEL_BUTTON_TITLE",
- tableName: "Main",
- value: tunnelState == .waitingForConnectivity(.noConnection) ? "Disconnect" : "Cancel",
- comment: ""
- ), for: .normal
- )
- splitDisconnectButton.primaryButton.setTitle(
- NSLocalizedString(
- "DISCONNECT_BUTTON_TITLE",
- tableName: "Main",
- value: "Disconnect",
- comment: ""
- ), for: .normal
- )
- splitDisconnectButton.secondaryButton.accessibilityLabel = NSLocalizedString(
- "RECONNECT_BUTTON_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Reconnect",
- comment: ""
- )
- }
-
- private func updateButtonEnabledStates(shouldEnableButtons: Bool) {
- selectLocationButtonBlurView.isEnabled = shouldEnableButtons
- connectButtonBlurView.isEnabled = shouldEnableButtons
- }
-
- private func updateTunnelRelays(tunnelStatus: TunnelStatus) {
- let tunnelState = tunnelStatus.state
- let observedState = tunnelStatus.observedState
-
- if tunnelState.isSecured, let tunnelRelays = tunnelState.relays {
- cityLabel.attributedText = attributedStringForLocation(
- string: tunnelRelays.exit.location.city
- )
- countryLabel.attributedText = attributedStringForLocation(
- string: tunnelRelays.exit.location.country
- )
-
- let exitName = tunnelRelays.exit.hostname
- let entryName = tunnelRelays.entry?.hostname
- let usingDaita = observedState.connectionState?.isDaitaEnabled == true
-
- let connectedRelayName = if let entryName {
- String(format: NSLocalizedString(
- "CONNECT_PANEL_TITLE",
- tableName: "Main",
- value: "%@ via %@\(usingDaita ? " using DAITA" : "")",
- comment: ""
- ), exitName, entryName)
- } else {
- String(format: NSLocalizedString(
- "CONNECT_PANEL_TITLE",
- tableName: "Main",
- value: "%@\(usingDaita ? " using DAITA" : "")",
- comment: ""
- ), exitName)
- }
-
- connectionPanel.isHidden = false
- connectionPanel.connectedRelayName = NSLocalizedString(
- "CONNECT_PANEL_TITLE",
- tableName: "Main",
- value: connectedRelayName,
- comment: ""
- )
- } else {
- countryLabel.attributedText = attributedStringForLocation(string: " ")
- cityLabel.attributedText = attributedStringForLocation(string: " ")
- connectionPanel.dataSource = nil
- connectionPanel.isHidden = true
- }
-
- locationContainerView.accessibilityLabel = viewModel?.tunnelStatus.state.localizedAccessibilityLabel
- }
-
- // MARK: - Private
-
- private func addSubviews() {
- for subview in [secureLabel, countryLabel, cityLabel, connectionPanel] {
- locationContainerView.addArrangedSubview(subview)
- }
-
- for subview in [activityIndicator, buttonsStackView, locationContainerView] {
- containerView.addSubview(subview)
- }
-
- addSubview(containerView)
-
- NSLayoutConstraint.activate([
- containerView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
- containerView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
- containerView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
- containerView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
-
- locationContainerView.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor),
- locationContainerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
- locationContainerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
-
- activityIndicator.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
- locationContainerView.topAnchor.constraint(
- equalTo: activityIndicator.bottomAnchor,
- constant: 22
- ),
-
- buttonsStackView.topAnchor.constraint(
- equalTo: locationContainerView.bottomAnchor,
- constant: 24
- ),
- buttonsStackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
- buttonsStackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
- buttonsStackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
- ])
- }
-
- private func addButtonHandlers() {
- connectButton.addTarget(
- self,
- action: #selector(handleConnect),
- for: .touchUpInside
- )
- cancelButton.addTarget(
- self,
- action: #selector(handleCancel),
- for: .touchUpInside
- )
- splitDisconnectButton.primaryButton.addTarget(
- self,
- action: #selector(handleDisconnect),
- for: .touchUpInside
- )
- splitDisconnectButton.secondaryButton.addTarget(
- self,
- action: #selector(handleReconnect),
- for: .touchUpInside
- )
- selectLocationButton.addTarget(
- self,
- action: #selector(handleSelectLocation),
- for: .touchUpInside
- )
- }
-
- private func setArrangedButtons(_ newButtons: [UIView]) {
- buttonsStackView.arrangedSubviews.forEach { button in
- if !newButtons.contains(button) {
- buttonsStackView.removeArrangedSubview(button)
- button.removeFromSuperview()
- }
- }
-
- newButtons.forEach { button in
- buttonsStackView.addArrangedSubview(button)
- }
- }
-
- private func view(forActionButton actionButton: TunnelState.TunnelControlActionButton) -> UIView {
- switch actionButton {
- case .connect:
- return connectButton
- case .disconnect:
- return splitDisconnectButton
- case .cancel:
- return cancelButtonBlurView
- }
- }
-
- private func attributedStringForLocation(string: String) -> NSAttributedString {
- let paragraphStyle = NSMutableParagraphStyle()
- paragraphStyle.lineSpacing = 0
- paragraphStyle.lineHeightMultiple = 0.8
-
- return NSAttributedString(
- string: string,
- attributes: [.paragraphStyle: paragraphStyle]
- )
- }
-
- private static func makeBoldTextLabel(ofSize fontSize: CGFloat, numberOfLines: Int = 1) -> UILabel {
- let textLabel = UILabel()
- textLabel.translatesAutoresizingMaskIntoConstraints = false
- textLabel.font = UIFont.boldSystemFont(ofSize: fontSize)
- textLabel.textColor = .white
- textLabel.numberOfLines = numberOfLines
- return textLabel
- }
-
- // MARK: - Actions
-
- @objc private func handleConnect() {
- actionHandler?(.connect)
- }
-
- @objc private func handleCancel() {
- actionHandler?(.cancel)
- }
-
- @objc private func handleDisconnect() {
- actionHandler?(.disconnect)
- }
-
- @objc private func handleReconnect() {
- actionHandler?(.reconnect)
- }
-
- @objc private func handleSelectLocation() {
- actionHandler?(.selectLocation)
- }
-}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift
deleted file mode 100644
index d1ce7b3a7a..0000000000
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-// TunnelControlViewModel.swift
-// MullvadVPN
-//
-// Created by Marco Nikic on 2023-11-24.
-// Copyright © 2023 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-
-struct TunnelControlViewModel {
- let tunnelStatus: TunnelStatus
- let secureLabelText: String
- let enableButtons: Bool
- let city: String
- let country: String
- let connectedRelaysName: String
- let outgoingConnectionInfo: OutgoingConnectionInfo?
-
- var connectionPanel: ConnectionPanelData? {
- guard let tunnelRelays = tunnelStatus.state.relays else {
- return nil
- }
-
- var portAndTransport = ""
- if let inPort = tunnelStatus.observedState.connectionState?.remotePort {
- let protocolLayer = tunnelStatus.observedState.connectionState?.transportLayer == .tcp ? "TCP" : "UDP"
- portAndTransport = ":\(inPort) \(protocolLayer)"
- }
-
- return ConnectionPanelData(
- inAddress: "\(tunnelRelays.entry?.endpoint.ipv4Relay.ip ?? tunnelRelays.exit.endpoint.ipv4Relay.ip)\(portAndTransport)",
- outAddress: outgoingConnectionInfo?.outAddress
- )
- }
-
- static var empty: Self {
- TunnelControlViewModel(
- tunnelStatus: TunnelStatus(),
- secureLabelText: "",
- enableButtons: true,
- city: "",
- country: "",
- connectedRelaysName: "",
- outgoingConnectionInfo: nil
- )
- }
-
- func update(status: TunnelStatus) -> TunnelControlViewModel {
- TunnelControlViewModel(
- tunnelStatus: status,
- secureLabelText: secureLabelText,
- enableButtons: enableButtons,
- city: city,
- country: country,
- connectedRelaysName: connectedRelaysName,
- outgoingConnectionInfo: nil
- )
- }
-
- func update(outgoingConnectionInfo: OutgoingConnectionInfo) -> TunnelControlViewModel {
- TunnelControlViewModel(
- tunnelStatus: tunnelStatus,
- secureLabelText: secureLabelText,
- enableButtons: enableButtons,
- city: city,
- country: country,
- connectedRelaysName: connectedRelaysName,
- outgoingConnectionInfo: outgoingConnectionInfo
- )
- }
-}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
index 78bd6c27b0..a486014e72 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
@@ -2,21 +2,25 @@
// TunnelViewController.swift
// MullvadVPN
//
-// Created by pronebird on 20/03/2019.
-// Copyright © 2019 Mullvad VPN AB. All rights reserved.
+// Created by Jon Petersson on 2024-12-10.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
+import Combine
import MapKit
import MullvadLogging
+import MullvadSettings
import MullvadTypes
-import UIKit
+import SwiftUI
class TunnelViewController: UIViewController, RootContainment {
private let logger = Logger(label: "TunnelViewController")
private let interactor: TunnelViewControllerInteractor
- private let contentView = TunnelControlView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
private var tunnelState: TunnelState = .disconnected
- private var viewModel = TunnelControlViewModel.empty
+ private var connectionViewViewModel: ConnectionViewViewModel
+ private var indicatorsViewViewModel: FeatureIndicatorsViewModel
+ private var connectionView: ConnectionView
+ private var connectionController: UIHostingController<ConnectionView>?
var shouldShowSelectLocationPicker: (() -> Void)?
var shouldShowCancelTunnelAlert: (() -> Void)?
@@ -46,7 +50,26 @@ class TunnelViewController: UIViewController, RootContainment {
init(interactor: TunnelViewControllerInteractor) {
self.interactor = interactor
+ tunnelState = interactor.tunnelStatus.state
+ connectionViewViewModel = ConnectionViewViewModel(tunnelStatus: interactor.tunnelStatus)
+ indicatorsViewViewModel = FeatureIndicatorsViewModel(
+ tunnelSettings: interactor.tunnelSettings,
+ ipOverrides: interactor.ipOverrides
+ )
+
+ connectionView = ConnectionView(
+ connectionViewModel: self.connectionViewViewModel,
+ indicatorsViewModel: self.indicatorsViewViewModel
+ )
+
super.init(nibName: nil, bundle: nil)
+
+ // When content size is updated in SwiftUI we need to explicitly tell UIKit to
+ // update its view size. This is not necessary on iOS 16 where we can set
+ // hostingController.sizingOptions instead.
+ connectionView.onContentUpdate = { [weak self] in
+ self?.connectionController?.view.setNeedsUpdateConstraints()
+ }
}
required init?(coder: NSCoder) {
@@ -61,15 +84,24 @@ class TunnelViewController: UIViewController, RootContainment {
}
interactor.didUpdateTunnelStatus = { [weak self] tunnelStatus in
+ self?.connectionViewViewModel.update(tunnelStatus: tunnelStatus)
self?.setTunnelState(tunnelStatus.state, animated: true)
- self?.updateViewModel(tunnelStatus: tunnelStatus)
+ self?.view.setNeedsLayout()
}
interactor.didGetOutgoingAddress = { [weak self] outgoingConnectionInfo in
- self?.updateViewModel(outgoingConnectionInfo: outgoingConnectionInfo)
+ self?.connectionViewViewModel.outgoingConnectionInfo = outgoingConnectionInfo
+ }
+
+ interactor.didUpdateTunnelSettings = { [weak self] tunnelSettings in
+ self?.indicatorsViewViewModel.tunnelSettings = tunnelSettings
+ }
+
+ interactor.didUpdateIpOverrides = { [weak self] overrides in
+ self?.indicatorsViewViewModel.ipOverrides = overrides
}
- contentView.actionHandler = { [weak self] action in
+ connectionView.action = { [weak self] action in
switch action {
case .connect:
self?.interactor.startTunnel()
@@ -94,37 +126,12 @@ class TunnelViewController: UIViewController, RootContainment {
addMapController()
addContentView()
-
- tunnelState = interactor.tunnelStatus.state
updateMap(animated: false)
- updateViewModel(tunnelStatus: interactor.tunnelStatus)
- }
-
- func updateViewModel(
- tunnelStatus: TunnelStatus? = nil,
- outgoingConnectionInfo: OutgoingConnectionInfo? = nil
- ) {
- if let tunnelStatus {
- viewModel = viewModel.update(status: tunnelStatus)
- }
- if let outgoingConnectionInfo {
- viewModel = viewModel.update(outgoingConnectionInfo: outgoingConnectionInfo)
- }
- contentView.update(with: viewModel)
- }
-
- override func viewWillTransition(
- to size: CGSize,
- with coordinator: UIViewControllerTransitionCoordinator
- ) {
- super.viewWillTransition(to: size, with: coordinator)
-
- contentView.update(with: viewModel)
}
func setMainContentHidden(_ isHidden: Bool, animated: Bool) {
let actions = {
- self.contentView.alpha = isHidden ? 0 : 1
+ _ = self.connectionView.opacity(isHidden ? 0 : 1)
}
if animated {
@@ -138,6 +145,7 @@ class TunnelViewController: UIViewController, RootContainment {
private func setTunnelState(_ tunnelState: TunnelState, animated: Bool) {
self.tunnelState = tunnelState
+
setNeedsHeaderBarStyleAppearanceUpdate()
guard isViewLoaded else { return }
@@ -149,18 +157,18 @@ class TunnelViewController: UIViewController, RootContainment {
switch tunnelState {
case let .connecting(tunnelRelays, _, _):
mapViewController.removeLocationMarker()
- contentView.setAnimatingActivity(true)
mapViewController.setCenter(tunnelRelays?.exit.location.geoCoordinate, animated: animated)
+ connectionViewViewModel.showsActivityIndicator = true
case let .reconnecting(tunnelRelays, _, _), let .negotiatingEphemeralPeer(tunnelRelays, _, _, _):
mapViewController.removeLocationMarker()
- contentView.setAnimatingActivity(true)
mapViewController.setCenter(tunnelRelays.exit.location.geoCoordinate, animated: animated)
+ connectionViewViewModel.showsActivityIndicator = true
case let .connected(tunnelRelays, _, _):
let center = tunnelRelays.exit.location.geoCoordinate
mapViewController.setCenter(center, animated: animated) {
- self.contentView.setAnimatingActivity(false)
+ self.connectionViewViewModel.showsActivityIndicator = false
// Connection can change during animation, so make sure we're still connected before adding marker.
if case .connected = self.tunnelState {
@@ -170,45 +178,42 @@ class TunnelViewController: UIViewController, RootContainment {
case .pendingReconnect:
mapViewController.removeLocationMarker()
- contentView.setAnimatingActivity(true)
+ connectionViewViewModel.showsActivityIndicator = true
case .waitingForConnectivity, .error:
mapViewController.removeLocationMarker()
- contentView.setAnimatingActivity(false)
+ connectionViewViewModel.showsActivityIndicator = false
case .disconnected, .disconnecting:
mapViewController.removeLocationMarker()
- contentView.setAnimatingActivity(false)
mapViewController.setCenter(nil, animated: animated)
+ connectionViewViewModel.showsActivityIndicator = false
}
}
private func addMapController() {
let mapView = mapViewController.view!
- mapView.translatesAutoresizingMaskIntoConstraints = false
- mapViewController.alignmentView = contentView.mapCenterAlignmentView
addChild(mapViewController)
- view.addSubview(mapView)
mapViewController.didMove(toParent: self)
- NSLayoutConstraint.activate([
- mapView.topAnchor.constraint(equalTo: view.topAnchor),
- mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ view.addConstrainedSubviews([mapView]) {
+ mapView.pinEdgesToSuperview()
+ }
}
private func addContentView() {
- contentView.translatesAutoresizingMaskIntoConstraints = false
- view.addSubview(contentView)
+ let connectionController = UIHostingController(rootView: connectionView)
+ self.connectionController = connectionController
+
+ let connectionViewProxy = connectionController.view!
+ connectionViewProxy.backgroundColor = .clear
- NSLayoutConstraint.activate([
- contentView.topAnchor.constraint(equalTo: view.topAnchor),
- contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ addChild(connectionController)
+ connectionController.didMove(toParent: self)
+
+ view.addConstrainedSubviews([connectionViewProxy]) {
+ connectionViewProxy.pinEdgesToSuperview(.all())
+ }
}
}
diff --git a/ios/MullvadVPNUITests/ConnectivityTests.swift b/ios/MullvadVPNUITests/ConnectivityTests.swift
index 909cdce159..791c5c9697 100644
--- a/ios/MullvadVPNUITests/ConnectivityTests.swift
+++ b/ios/MullvadVPNUITests/ConnectivityTests.swift
@@ -205,12 +205,12 @@ class ConnectivityTests: LoggedOutUITestCase {
// Actual test. Make sure it is possible to connect to a relay
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
HeaderBar(app)
.tapAccountButton()
diff --git a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
index da47a2c334..d1a3b3dcea 100644
--- a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
+++ b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
@@ -30,7 +30,7 @@ class TunnelControlPage: Page {
let startTime = Date()
let pollingInterval = TimeInterval(0.5) // How often to check for changes
- let inAddressRow = app.otherElements[AccessibilityIdentifier.connectionPanelInAddressRow]
+ let inAddressRow = app.staticTexts[AccessibilityIdentifier.connectionPanelInAddressRow]
while Date().timeIntervalSince(startTime) < timeout {
let expectation = XCTestExpectation(description: "Wait for connection attempts")
@@ -41,29 +41,29 @@ class TunnelControlPage: Page {
_ = XCTWaiter.wait(for: [expectation], timeout: pollingInterval + 0.5)
- if let currentText = inAddressRow.value as? String {
- // Skip initial label value with IP address only - no port or protocol
- guard currentText.contains(" ") == true else {
- continue
- }
+ let currentText = inAddressRow.label
+
+ // Skip initial label value with IP address only - no port or protocol
+ guard currentText.contains(" ") == true else {
+ continue
+ }
- let addressPortComponent = currentText.components(separatedBy: " ")[0]
- let ipAddress = addressPortComponent.components(separatedBy: ":")[0]
- let port = addressPortComponent.components(separatedBy: ":")[1]
- let protocolName = currentText.components(separatedBy: " ")[1]
- let connectionAttempt = ConnectionAttempt(
- ipAddress: ipAddress,
- port: port,
- protocolName: protocolName
- )
+ let addressPortComponent = currentText.components(separatedBy: " ")[0]
+ let ipAddress = addressPortComponent.components(separatedBy: ":")[0]
+ let port = addressPortComponent.components(separatedBy: ":")[1]
+ let protocolName = currentText.components(separatedBy: " ")[1]
+ let connectionAttempt = ConnectionAttempt(
+ ipAddress: ipAddress,
+ port: port,
+ protocolName: protocolName
+ )
- if connectionAttempt != lastConnectionAttempt {
- connectionAttempts.append(connectionAttempt)
- lastConnectionAttempt = connectionAttempt
+ if connectionAttempt != lastConnectionAttempt {
+ connectionAttempts.append(connectionAttempt)
+ lastConnectionAttempt = connectionAttempt
- if connectionAttempts.count == attemptsCount {
- break
- }
+ if connectionAttempts.count == attemptsCount {
+ break
}
}
}
@@ -83,7 +83,7 @@ class TunnelControlPage: Page {
return self
}
- @discardableResult func tapSecureConnectionButton() -> Self {
+ @discardableResult func tapConnectButton() -> Self {
app.buttons[AccessibilityIdentifier.connectButton].tap()
return self
}
@@ -112,7 +112,7 @@ class TunnelControlPage: Page {
return self
}
- @discardableResult func waitForSecureConnectionLabel() -> Self {
+ @discardableResult func waitForConnectedLabel() -> Self {
let labelFound = app.staticTexts[.connectionStatusConnectedLabel]
.waitForExistence(timeout: BaseUITestCase.extremelyLongTimeout)
XCTAssertTrue(labelFound, "Secure connection label presented")
@@ -121,7 +121,7 @@ class TunnelControlPage: Page {
}
@discardableResult func tapRelayStatusExpandCollapseButton() -> Self {
- app.otherElements[AccessibilityIdentifier.relayStatusCollapseButton].press(forDuration: .leastNonzeroMagnitude)
+ app.images[AccessibilityIdentifier.relayStatusCollapseButton].press(forDuration: .leastNonzeroMagnitude)
return self
}
@@ -194,38 +194,23 @@ class TunnelControlPage: Page {
/// Verify that the app attempts to connect over Multihop.
@discardableResult func verifyConnectingOverMultihop() -> Self {
- let relayName = getCurrentRelayName().lowercased()
- XCTAssertTrue(relayName.contains("via"))
+ XCTAssertTrue(app.staticTexts["Multihop"].exists)
return self
}
/// Verify that the app attempts to connect using DAITA.
@discardableResult func verifyConnectingUsingDAITA() -> Self {
- let relayName = getCurrentRelayName().lowercased()
- XCTAssertTrue(relayName.contains("using daita"))
+ XCTAssertTrue(app.staticTexts["DAITA"].exists)
return self
}
func getInIPAddressFromConnectionStatus() -> String {
- let inAddressRow = app.otherElements[AccessibilityIdentifier.connectionPanelInAddressRow]
-
- if let textValue = inAddressRow.value as? String {
- let ipAddress = textValue.components(separatedBy: ":")[0]
- return ipAddress
- } else {
- XCTFail("Failed to read relay IP address from status label")
- return String()
- }
+ let inAddressRow = app.staticTexts[.connectionPanelInAddressRow]
+ return inAddressRow.label.components(separatedBy: ":")[0]
}
func getCurrentRelayName() -> String {
- let relayExpandButton = app.otherElements[.relayStatusCollapseButton]
-
- guard let relayName = relayExpandButton.value as? String else {
- XCTFail("Failed to read relay name from tunnel control page")
- return String()
- }
-
- return relayName
+ let server = app.staticTexts[.connectionPanelServerLabel]
+ return server.label
}
}
diff --git a/ios/MullvadVPNUITests/RelayTests.swift b/ios/MullvadVPNUITests/RelayTests.swift
index 9bf9c79042..f92feee29e 100644
--- a/ios/MullvadVPNUITests/RelayTests.swift
+++ b/ios/MullvadVPNUITests/RelayTests.swift
@@ -75,12 +75,12 @@ class RelayTests: LoggedInWithTimeUITestCase {
.swipeDownToDismissModal()
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked() // Allow adding VPN configurations iOS permission
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
try Networking.verifyCannotReachAdServingDomain()
@@ -90,12 +90,12 @@ class RelayTests: LoggedInWithTimeUITestCase {
func testAppConnection() throws {
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
try Networking.verifyCanAccessInternet()
try Networking.verifyConnectedThroughMullvad()
@@ -158,12 +158,12 @@ class RelayTests: LoggedInWithTimeUITestCase {
.tapDoneButton()
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
try Networking.verifyCanAccessInternet()
@@ -199,12 +199,12 @@ class RelayTests: LoggedInWithTimeUITestCase {
.tapDoneButton()
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
try Networking.verifyCanAccessInternet()
@@ -253,7 +253,7 @@ class RelayTests: LoggedInWithTimeUITestCase {
// Should be two UDP connection attempts but sometimes only one is shown in the UI
TunnelControlPage(app)
.verifyConnectingOverTCPAfterUDPAttempts()
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
.tapDisconnectButton()
}
@@ -275,14 +275,14 @@ class RelayTests: LoggedInWithTimeUITestCase {
.swipeDownToDismissModal()
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
.tapRelayStatusExpandCollapseButton()
.verifyConnectingToPort("4001")
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
.tapDisconnectButton()
}
@@ -318,12 +318,12 @@ class RelayTests: LoggedInWithTimeUITestCase {
.tapDoneButton()
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
.verifyConnectingUsingDAITA()
.tapDisconnectButton()
}
@@ -358,57 +358,27 @@ class RelayTests: LoggedInWithTimeUITestCase {
.tapDoneButton()
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
.verifyConnectingOverMultihop()
.tapDisconnectButton()
}
- /// Connect to a relay in the default country and city, get name and IP address of the relay the app successfully connects to. Assumes user is logged on and at tunnel control page.
- private func getDefaultRelayInfo() -> RelayInfo {
- TunnelControlPage(app)
- .tapSelectLocationButton()
-
- if SelectLocationPage(app).locationCellIsExpanded(BaseUITestCase.testsDefaultCountryName) {
- // Already expanded - just make sure the correct city cell is selected
- SelectLocationPage(app)
- .tapLocationCell(withName: BaseUITestCase.testsDefaultCityName)
- } else {
- SelectLocationPage(app)
- .tapLocationCellExpandButton(withName: BaseUITestCase.testsDefaultCountryName)
- .tapLocationCell(withName: BaseUITestCase.testsDefaultCityName)
- }
-
- allowAddVPNConfigurationsIfAsked()
-
- let relayIPAddress = TunnelControlPage(app)
- .waitForSecureConnectionLabel()
- .tapRelayStatusExpandCollapseButton()
- .getInIPAddressFromConnectionStatus()
-
- let relayName = TunnelControlPage(app).getCurrentRelayName()
-
- TunnelControlPage(app)
- .tapDisconnectButton()
-
- return RelayInfo(name: relayName, ipAddress: relayIPAddress)
- }
-
func testCustomDNS() throws {
let dnsServerIPAddress = "8.8.8.8"
let dnsServerProviderName = "GOOGLE"
TunnelControlPage(app)
- .tapSecureConnectionButton()
+ .tapConnectButton()
allowAddVPNConfigurationsIfAsked()
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
try Networking.verifyCanAccessInternet()
@@ -432,4 +402,36 @@ class RelayTests: LoggedInWithTimeUITestCase {
try Networking.verifyDNSServerProvider(dnsServerProviderName, isMullvad: false)
}
+}
+
+extension RelayTests {
+ /// Connect to a relay in the default country and city, get name and IP address of the relay the app successfully connects to. Assumes user is logged on and at tunnel control page.
+ private func getDefaultRelayInfo() -> RelayInfo {
+ TunnelControlPage(app)
+ .tapSelectLocationButton()
+
+ if SelectLocationPage(app).locationCellIsExpanded(BaseUITestCase.testsDefaultCountryName) {
+ // Already expanded - just make sure the correct city cell is selected
+ SelectLocationPage(app)
+ .tapLocationCell(withName: BaseUITestCase.testsDefaultCityName)
+ } else {
+ SelectLocationPage(app)
+ .tapLocationCellExpandButton(withName: BaseUITestCase.testsDefaultCountryName)
+ .tapLocationCell(withName: BaseUITestCase.testsDefaultCityName)
+ }
+
+ allowAddVPNConfigurationsIfAsked()
+
+ let relayIPAddress = TunnelControlPage(app)
+ .waitForConnectedLabel()
+ .tapRelayStatusExpandCollapseButton()
+ .getInIPAddressFromConnectionStatus()
+
+ let relayName = TunnelControlPage(app).getCurrentRelayName()
+
+ TunnelControlPage(app)
+ .tapDisconnectButton()
+
+ return RelayInfo(name: relayName, ipAddress: relayIPAddress)
+ }
} // swiftlint:disable:this file_length
diff --git a/ios/MullvadVPNUITests/Screenshots/ScreenshotTests.swift b/ios/MullvadVPNUITests/Screenshots/ScreenshotTests.swift
index 326a5c56a1..dc53791101 100644
--- a/ios/MullvadVPNUITests/Screenshots/ScreenshotTests.swift
+++ b/ios/MullvadVPNUITests/Screenshots/ScreenshotTests.swift
@@ -50,7 +50,7 @@ class ScreenshotTests: LoggedInWithTimeUITestCase {
.tapLocationCell(withName: "Sweden")
TunnelControlPage(app)
- .waitForSecureConnectionLabel()
+ .waitForConnectedLabel()
snapshot("QuantumConnectionSecured")
}