summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2020-01-08 17:31:47 +0100
committerAndrej Mihajlov <and@mullvad.net>2020-01-08 17:31:47 +0100
commitfcf0ff71fa8c50759029ed59b70a8ea39c4f6ac0 (patch)
treead69aabb691770ffcb1d6c4e46417e1a99f38ca1
parent69a36607f22a7a7cb87d4b4d0319b9e8bb118416 (diff)
parentd8dea90147e543a3333565f53e9bb8dd92cf6f44 (diff)
downloadmullvadvpn-fcf0ff71fa8c50759029ed59b70a8ea39c4f6ac0.tar.xz
mullvadvpn-fcf0ff71fa8c50759029ed59b70a8ea39c4f6ac0.zip
Merge branch 'add-wireguard-key-management'
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj28
-rw-r--r--ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/Contents.json21
-rw-r--r--ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/MapBackground.pngbin74602 -> 0 bytes
-rw-r--r--ios/MullvadVPN/Base.lproj/Main.storyboard305
-rw-r--r--ios/MullvadVPN/CustomButton.swift1
-rw-r--r--ios/MullvadVPN/EmbeddedViewContainerView.swift60
-rw-r--r--ios/MullvadVPN/LoginViewController.swift4
-rw-r--r--ios/MullvadVPN/SegueIdentifier.swift4
-rw-r--r--ios/MullvadVPN/SelectLocationController.swift6
-rw-r--r--ios/MullvadVPN/SettingsBasicCell.swift13
-rw-r--r--ios/MullvadVPN/SettingsViewController.swift15
-rw-r--r--ios/MullvadVPN/SpinnerActivityIndicatorView.swift26
-rw-r--r--ios/MullvadVPN/StaticTableViewDataSource.swift6
-rw-r--r--ios/MullvadVPN/WebLinks.swift5
-rw-r--r--ios/MullvadVPN/WireguardKeysViewController.swift284
15 files changed, 708 insertions, 70 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index ea382d9c6d..49a7d0a18c 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -39,7 +39,10 @@
586BD68322B7BBD800BB7F9F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 586BD68222B7BBD800BB7F9F /* NetworkExtension.framework */; };
586BD68422B7BBE400BB7F9F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 586BD68222B7BBD800BB7F9F /* NetworkExtension.framework */; };
58723E7522A54CB2009837F5 /* libwg-go.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58723E7422A54C63009837F5 /* libwg-go.a */; };
+ 5873884D239E6D7E00E96C4E /* EmbeddedViewContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5873884C239E6D7E00E96C4E /* EmbeddedViewContainerView.swift */; };
587425C12299833500CA2045 /* RootContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587425C02299833500CA2045 /* RootContainerViewController.swift */; };
+ 5877152E23981C5B001F8237 /* SettingsBasicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5877152D23981C5B001F8237 /* SettingsBasicCell.swift */; };
+ 5877153023981F7B001F8237 /* WireguardKeysViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5877152F23981F7B001F8237 /* WireguardKeysViewController.swift */; };
58781CC922AE7CA8009B9D8E /* RelayConstraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CC822AE7CA8009B9D8E /* RelayConstraints.swift */; };
58781CCE22AE8918009B9D8E /* RelayConstraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CC822AE7CA8009B9D8E /* RelayConstraints.swift */; };
58781CD522AFBA39009B9D8E /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; };
@@ -167,7 +170,10 @@
5867A51B2248F26A005513C0 /* SegueIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegueIdentifier.swift; sourceTree = "<group>"; };
586BD68222B7BBD800BB7F9F /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
58723E7422A54C63009837F5 /* libwg-go.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libwg-go.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5873884C239E6D7E00E96C4E /* EmbeddedViewContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmbeddedViewContainerView.swift; sourceTree = "<group>"; };
587425C02299833500CA2045 /* RootContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootContainerViewController.swift; sourceTree = "<group>"; };
+ 5877152D23981C5B001F8237 /* SettingsBasicCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBasicCell.swift; sourceTree = "<group>"; };
+ 5877152F23981F7B001F8237 /* WireguardKeysViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireguardKeysViewController.swift; sourceTree = "<group>"; };
58781CC822AE7CA8009B9D8E /* RelayConstraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConstraints.swift; sourceTree = "<group>"; };
58781CD422AFBA39009B9D8E /* RelaySelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelector.swift; sourceTree = "<group>"; };
587AD7C523421D7000E93A53 /* TunnelConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelConfiguration.swift; sourceTree = "<group>"; };
@@ -310,6 +316,7 @@
5894FC482296A8090017471D /* CustomButton.swift */,
582BB1B0229569620055B6EF /* CustomNavigationBar.swift */,
58C6B35D22BBBFE3003C19AD /* Data+HexCoding.swift */,
+ 5873884C239E6D7E00E96C4E /* EmbeddedViewContainerView.swift */,
58CE5E6F224146210008646E /* Info.plist */,
5840250022B1124600E4CFEC /* IpAddress+Codable.swift */,
58C6B34E22BB7AC0003C19AD /* IPAddressRange.swift */,
@@ -340,6 +347,7 @@
5888AD86227B17950051EB06 /* SelectLocationController.swift */,
582BB1B2229574F40055B6EF /* SettingsAccountCell.swift */,
581CBCEB2298041B00727D7F /* SettingsAppVersionCell.swift */,
+ 5877152D23981C5B001F8237 /* SettingsBasicCell.swift */,
582BB1AE229566420055B6EF /* SettingsCell.swift */,
58CCA01122424D11004F3011 /* SettingsViewController.swift */,
58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */,
@@ -356,6 +364,7 @@
581CBCE52296B97300727D7F /* ViewControllerIdentifier.swift */,
587B08E1229460C1000E6F17 /* WebLinks.swift */,
58B8743122B25A7600015324 /* WireguardAssociatedAddresses.swift */,
+ 5877152F23981F7B001F8237 /* WireguardKeysViewController.swift */,
58C6B35322BB87C4003C19AD /* WireguardPrivateKey.swift */,
);
path = MullvadVPN;
@@ -610,6 +619,7 @@
581CBCE62296B97300727D7F /* ViewControllerIdentifier.swift in Sources */,
58BFA5C622A7C97F00A6173D /* RelayCache.swift in Sources */,
582BB1B1229569620055B6EF /* CustomNavigationBar.swift in Sources */,
+ 5877153023981F7B001F8237 /* WireguardKeysViewController.swift in Sources */,
58C6B35422BB87C4003C19AD /* WireguardPrivateKey.swift in Sources */,
5888AD87227B17950051EB06 /* SelectLocationController.swift in Sources */,
58F19E35228C15BA00C7710B /* SpinnerActivityIndicatorView.swift in Sources */,
@@ -629,10 +639,12 @@
587B08E0229433EB000E6F17 /* LoginState.swift in Sources */,
58C6B34F22BB7AC0003C19AD /* IPAddressRange.swift in Sources */,
582BB1AF229566420055B6EF /* SettingsCell.swift in Sources */,
+ 5873884D239E6D7E00E96C4E /* EmbeddedViewContainerView.swift in Sources */,
582650862384116F00FA7A86 /* ReplaceNilWithError.swift in Sources */,
5862805422428EF100F5A6E1 /* TranslucentButtonBlurView.swift in Sources */,
5888AD83227B11080051EB06 /* SelectLocationCell.swift in Sources */,
58CE5E66224146200008646E /* LoginViewController.swift in Sources */,
+ 5877152E23981C5B001F8237 /* SettingsBasicCell.swift in Sources */,
5835B7CC233B76CB0096D79F /* TunnelManager.swift in Sources */,
58ADDB3C227B1BD200FAFEA7 /* JsonRpc.swift in Sources */,
581CBCEE229826FD00727D7F /* StaticTableViewDataSource.swift in Sources */,
@@ -895,7 +907,7 @@
CODE_SIGN_ENTITLEMENTS = MullvadVPN/MullvadVPN.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = G7CDBEG477;
INFOPLIST_FILE = MullvadVPN/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -903,7 +915,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 2019.10;
+ MARKETING_VERSION = 2020.1;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -923,7 +935,7 @@
CODE_SIGN_ENTITLEMENTS = MullvadVPN/MullvadVPN.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = G7CDBEG477;
INFOPLIST_FILE = MullvadVPN/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -931,7 +943,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 2019.10;
+ MARKETING_VERSION = 2020.1;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -947,7 +959,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = G7CDBEG477;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
@@ -956,7 +968,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 2019.10;
+ MARKETING_VERSION = 2020.1;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnel;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -973,7 +985,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = G7CDBEG477;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
@@ -982,7 +994,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 2019.10;
+ MARKETING_VERSION = 2020.1;
PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnel;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
diff --git a/ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/Contents.json
deleted file mode 100644
index 3eca6b2b52..0000000000
--- a/ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/Contents.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "universal",
- "filename" : "MapBackground.png",
- "scale" : "1x"
- },
- {
- "idiom" : "universal",
- "scale" : "2x"
- },
- {
- "idiom" : "universal",
- "scale" : "3x"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-} \ No newline at end of file
diff --git a/ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/MapBackground.png b/ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/MapBackground.png
deleted file mode 100644
index 6d96307f81..0000000000
--- a/ios/MullvadVPN/Assets.xcassets/MapBackground.imageset/MapBackground.png
+++ /dev/null
Binary files differ
diff --git a/ios/MullvadVPN/Base.lproj/Main.storyboard b/ios/MullvadVPN/Base.lproj/Main.storyboard
index b1c99c9877..199b69e272 100644
--- a/ios/MullvadVPN/Base.lproj/Main.storyboard
+++ b/ios/MullvadVPN/Base.lproj/Main.storyboard
@@ -104,7 +104,7 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pID-oa-Rrg" customClass="SpinnerActivityIndicatorView" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="163.5" y="172.5" width="48" height="48"/>
+ <rect key="frame" x="163.5" y="173" width="48" height="48"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="48" id="2J4-Qc-ctc"/>
@@ -112,10 +112,10 @@
</constraints>
</view>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.0" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="IconSuccess" translatesAutoresizingMaskIntoConstraints="NO" id="7ux-Tb-Fzq">
- <rect key="frame" x="157.5" y="166.5" width="60" height="60"/>
+ <rect key="frame" x="127.5" y="137" width="120" height="120"/>
</imageView>
<view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="V3j-Lb-fSQ" userLabel="Form">
- <rect key="frame" x="0.0" y="250.5" width="375" height="126"/>
+ <rect key="frame" x="0.0" y="251" width="375" height="125.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Login" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Nxn-Fc-EGe">
<rect key="frame" x="24" y="0.0" width="327" height="39"/>
@@ -130,7 +130,7 @@
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VmT-ya-ufe" customClass="AccountInputGroupView" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="24" y="78" width="327" height="48"/>
+ <rect key="frame" x="24" y="77.5" width="327" height="48"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="0000 0000 0000 0000" textAlignment="natural" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XOB-ct-yLU" userLabel="Account Text Field" customClass="AccountTextField" customModule="MullvadVPN" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="327" height="48"/>
@@ -331,20 +331,20 @@
<color key="separatorColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Account" id="ghE-jC-RWf" customClass="SettingsAccountCell" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="0.0" y="55.5" width="375" height="43"/>
+ <rect key="frame" x="0.0" y="55.5" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ghE-jC-RWf" id="sTl-gI-g2a">
- <rect key="frame" x="0.0" y="0.0" width="348" height="43"/>
+ <rect key="frame" x="0.0" y="0.0" width="348" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Lve-Kd-qTr">
- <rect key="frame" x="16" y="11" width="63.5" height="21"/>
+ <rect key="frame" x="16" y="11" width="63.5" height="21.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" text="A YEAR LEFT" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QeD-EQ-Ruo">
- <rect key="frame" x="259" y="11" width="81" height="21"/>
+ <rect key="frame" x="259" y="11" width="81" height="21.5"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
@@ -368,20 +368,20 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="AppVersion" id="pbd-iC-Emm" customClass="SettingsAppVersionCell" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="0.0" y="98.5" width="375" height="43"/>
+ <rect key="frame" x="0.0" y="99" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pbd-iC-Emm" id="lYp-Z8-1sN">
- <rect key="frame" x="0.0" y="0.0" width="375" height="43"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="App version" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pYC-Zb-8N9">
- <rect key="frame" x="16" y="11" width="91" height="21"/>
+ <rect key="frame" x="16" y="11" width="91" height="21.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" text="2018.3" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sOr-vj-cg7">
- <rect key="frame" x="316.5" y="11" width="42.5" height="21"/>
+ <rect key="frame" x="316.5" y="11" width="42.5" height="21.5"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
@@ -402,6 +402,32 @@
<outlet property="versionLabel" destination="sOr-vj-cg7" id="xgH-No-26f"/>
</connections>
</tableViewCell>
+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="BasicDisclosure" id="Ahs-gu-nTM" customClass="SettingsBasicCell" customModule="MullvadVPN" customModuleProvider="target">
+ <rect key="frame" x="0.0" y="142.5" width="375" height="43.5"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Ahs-gu-nTM" id="Drq-vk-8F2">
+ <rect key="frame" x="0.0" y="0.0" width="348" height="43.5"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Amw-A3-ePS">
+ <rect key="frame" x="16" y="11" width="324" height="21.5"/>
+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
+ <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ <constraints>
+ <constraint firstItem="Amw-A3-ePS" firstAttribute="leading" secondItem="Drq-vk-8F2" secondAttribute="leadingMargin" id="4Ug-US-l8D"/>
+ <constraint firstItem="Amw-A3-ePS" firstAttribute="top" secondItem="Drq-vk-8F2" secondAttribute="topMargin" id="6ID-BF-Jsp"/>
+ <constraint firstAttribute="bottomMargin" secondItem="Amw-A3-ePS" secondAttribute="bottom" id="JPQ-S7-zHO"/>
+ <constraint firstAttribute="trailingMargin" secondItem="Amw-A3-ePS" secondAttribute="trailing" id="lhc-g5-1mb"/>
+ </constraints>
+ </tableViewCellContentView>
+ <color key="backgroundColor" name="Primary"/>
+ <connections>
+ <outlet property="titleLabel" destination="Amw-A3-ePS" id="cGS-cX-LXr"/>
+ </connections>
+ </tableViewCell>
</prototypes>
<sections/>
<connections>
@@ -418,6 +444,7 @@
</navigationItem>
<connections>
<outlet property="staticDataSource" destination="9xf-6a-8vR" id="E8j-Z4-Ljk"/>
+ <segue destination="vAK-MJ-h3c" kind="show" identifier="ShowWireguardKeys" id="eDY-gl-kvu"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="sR5-ix-4x7" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -429,6 +456,246 @@
</objects>
<point key="canvasLocation" x="1690" y="-832"/>
</scene>
+ <!--WireGuard key-->
+ <scene sceneID="fcg-jd-GmS">
+ <objects>
+ <viewController id="vAK-MJ-h3c" customClass="WireguardKeysViewController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="NHk-FR-Mwy">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fa2-zl-Fc4">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
+ <subviews>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5VO-oQ-4jM" userLabel="Container">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="361"/>
+ <subviews>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Lx5-tV-hNL" userLabel="Content">
+ <rect key="frame" x="24" y="24" width="327" height="313"/>
+ <subviews>
+ <view contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xch-VD-kOQ" userLabel="Account number">
+ <rect key="frame" x="0.0" y="0.0" width="327" height="45.5"/>
+ <subviews>
+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="CtE-XF-2uJ">
+ <rect key="frame" x="0.0" y="0.0" width="327" height="45.5"/>
+ <subviews>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="29J-lm-A4D">
+ <rect key="frame" x="0.0" y="0.0" width="327" height="17"/>
+ <subviews>
+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="751" text="Public key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="riQ-cz-0de">
+ <rect key="frame" x="0.0" y="0.0" width="66" height="17"/>
+ <fontDescription key="fontDescription" type="system" pointSize="14"/>
+ <color key="textColor" white="1" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <nil key="highlightedColor"/>
+ </label>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Z76-2G-dkn" customClass="EmbeddedViewContainerView" customModule="MullvadVPN" customModuleProvider="target">
+ <rect key="frame" x="185" y="0.0" width="142" height="17"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="142" placeholder="YES" id="v4m-dU-J71"/>
+ </constraints>
+ <connections>
+ <outlet property="embeddedView" destination="1Ue-pb-GCu" id="uUI-08-4yA"/>
+ </connections>
+ </view>
+ </subviews>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <constraints>
+ <constraint firstAttribute="bottom" secondItem="Z76-2G-dkn" secondAttribute="bottom" id="I4r-uc-kgH"/>
+ <constraint firstItem="riQ-cz-0de" firstAttribute="leading" secondItem="29J-lm-A4D" secondAttribute="leading" id="TZy-KN-DUR"/>
+ <constraint firstAttribute="trailing" secondItem="Z76-2G-dkn" secondAttribute="trailing" id="cto-xJ-P12"/>
+ <constraint firstItem="Z76-2G-dkn" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="riQ-cz-0de" secondAttribute="trailing" constant="8" id="kUO-SM-ut1"/>
+ <constraint firstItem="riQ-cz-0de" firstAttribute="top" secondItem="29J-lm-A4D" secondAttribute="top" id="pef-9R-fZa"/>
+ <constraint firstAttribute="bottom" secondItem="riQ-cz-0de" secondAttribute="bottom" id="sTe-ay-g1z"/>
+ <constraint firstItem="Z76-2G-dkn" firstAttribute="top" secondItem="29J-lm-A4D" secondAttribute="top" id="w1K-nl-2QW"/>
+ </constraints>
+ </view>
+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="123456789" textAlignment="natural" lineBreakMode="characterWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UF1-4s-m1S">
+ <rect key="frame" x="0.0" y="25" width="327" height="20.5"/>
+ <fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
+ <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ </stackView>
+ </subviews>
+ <constraints>
+ <constraint firstItem="CtE-XF-2uJ" firstAttribute="top" secondItem="xch-VD-kOQ" secondAttribute="top" id="9pN-dP-gTH"/>
+ <constraint firstAttribute="trailing" secondItem="CtE-XF-2uJ" secondAttribute="trailing" id="Kjq-dh-nxS"/>
+ <constraint firstAttribute="bottom" secondItem="CtE-XF-2uJ" secondAttribute="bottom" id="coJ-q9-t0B"/>
+ <constraint firstItem="CtE-XF-2uJ" firstAttribute="leading" secondItem="xch-VD-kOQ" secondAttribute="leading" id="wHk-cr-fV2"/>
+ </constraints>
+ </view>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Bjs-D6-NVj" userLabel="Expiry">
+ <rect key="frame" x="0.0" y="69.5" width="327" height="45.5"/>
+ <subviews>
+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="8f3-SD-t3K">
+ <rect key="frame" x="0.0" y="0.0" width="327" height="45.5"/>
+ <subviews>
+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Key generated" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FMt-bt-4gy">
+ <rect key="frame" x="0.0" y="0.0" width="327" height="17"/>
+ <fontDescription key="fontDescription" type="system" pointSize="14"/>
+ <color key="textColor" white="1" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <nil key="highlightedColor"/>
+ </label>
+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="6 days ago" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CvU-pV-ixr">
+ <rect key="frame" x="0.0" y="25" width="327" height="20.5"/>
+ <fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
+ <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ </stackView>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="bottom" secondItem="8f3-SD-t3K" secondAttribute="bottom" id="Lou-NI-gF9"/>
+ <constraint firstAttribute="trailing" secondItem="8f3-SD-t3K" secondAttribute="trailing" id="Xok-g3-2S0"/>
+ <constraint firstItem="8f3-SD-t3K" firstAttribute="top" secondItem="Bjs-D6-NVj" secondAttribute="top" id="asr-RD-H6w"/>
+ <constraint firstItem="8f3-SD-t3K" firstAttribute="leading" secondItem="Bjs-D6-NVj" secondAttribute="leading" id="fvj-uI-LRp"/>
+ </constraints>
+ </view>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ydQ-IP-KZb" userLabel="Buttons">
+ <rect key="frame" x="0.0" y="139" width="327" height="174"/>
+ <subviews>
+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="zF0-5W-t7M">
+ <rect key="frame" x="0.0" y="0.0" width="327" height="174"/>
+ <subviews>
+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OCa-Jz-b7W" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target">
+ <rect key="frame" x="0.0" y="0.0" width="327" height="42"/>
+ <constraints>
+ <constraint firstAttribute="height" constant="42" placeholder="YES" id="IpC-KC-l52"/>
+ </constraints>
+ <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <state key="normal" title="Regenerate key" backgroundImage="SuccessButton"/>
+ <connections>
+ <action selector="handleRegenerateKey:" destination="vAK-MJ-h3c" eventType="touchUpInside" id="s39-bg-vkG"/>
+ </connections>
+ </button>
+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qEF-8w-MdR" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target">
+ <rect key="frame" x="0.0" y="66" width="327" height="42"/>
+ <constraints>
+ <constraint firstAttribute="height" constant="42" placeholder="YES" id="299-Lu-yIB"/>
+ </constraints>
+ <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <state key="normal" title="Verify key" backgroundImage="SuccessButton"/>
+ <connections>
+ <action selector="handleVerifyKey:" destination="vAK-MJ-h3c" eventType="touchUpInside" id="wGf-5k-Zw2"/>
+ </connections>
+ </button>
+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4U5-oz-pbv" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target">
+ <rect key="frame" x="0.0" y="132" width="327" height="42"/>
+ <constraints>
+ <constraint firstAttribute="height" constant="42" placeholder="YES" id="eTr-Qz-83C"/>
+ </constraints>
+ <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <state key="normal" title="Manage keys" image="IconExtlink" backgroundImage="SuccessButton"/>
+ <connections>
+ <action selector="handleManageKeys:" destination="vAK-MJ-h3c" eventType="touchUpInside" id="DDg-kZ-9k6"/>
+ </connections>
+ </button>
+ </subviews>
+ </stackView>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="bottom" secondItem="zF0-5W-t7M" secondAttribute="bottom" id="Hb8-OT-rIo"/>
+ <constraint firstAttribute="trailing" secondItem="zF0-5W-t7M" secondAttribute="trailing" id="bO9-9j-LGw"/>
+ <constraint firstItem="zF0-5W-t7M" firstAttribute="leading" secondItem="ydQ-IP-KZb" secondAttribute="leading" id="d8z-bR-Gjl"/>
+ <constraint firstItem="zF0-5W-t7M" firstAttribute="top" secondItem="ydQ-IP-KZb" secondAttribute="top" id="ylb-xz-fnV"/>
+ </constraints>
+ </view>
+ </subviews>
+ <constraints>
+ <constraint firstItem="ydQ-IP-KZb" firstAttribute="top" secondItem="Bjs-D6-NVj" secondAttribute="bottom" constant="24" id="37w-fI-zi8"/>
+ <constraint firstItem="Bjs-D6-NVj" firstAttribute="top" secondItem="xch-VD-kOQ" secondAttribute="bottom" constant="24" id="8F9-gx-ngL"/>
+ <constraint firstAttribute="trailing" secondItem="xch-VD-kOQ" secondAttribute="trailing" id="GLB-vv-DDi"/>
+ <constraint firstAttribute="trailing" secondItem="Bjs-D6-NVj" secondAttribute="trailing" id="Hpo-go-tU4"/>
+ <constraint firstAttribute="trailing" secondItem="ydQ-IP-KZb" secondAttribute="trailing" id="OwJ-Xb-q8O"/>
+ <constraint firstItem="ydQ-IP-KZb" firstAttribute="leading" secondItem="Lx5-tV-hNL" secondAttribute="leading" id="P69-3O-u0S"/>
+ <constraint firstItem="Bjs-D6-NVj" firstAttribute="leading" secondItem="Lx5-tV-hNL" secondAttribute="leading" id="Qj6-44-14Q"/>
+ <constraint firstItem="xch-VD-kOQ" firstAttribute="leading" secondItem="Lx5-tV-hNL" secondAttribute="leading" id="Y3H-6z-JyF"/>
+ <constraint firstAttribute="bottom" secondItem="ydQ-IP-KZb" secondAttribute="bottom" id="aRW-Pg-yg6"/>
+ <constraint firstItem="xch-VD-kOQ" firstAttribute="top" secondItem="Lx5-tV-hNL" secondAttribute="top" id="i3j-2f-8vV"/>
+ </constraints>
+ </view>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="bottom" secondItem="Lx5-tV-hNL" secondAttribute="bottom" constant="24" id="1sW-Lm-fPs"/>
+ <constraint firstItem="Lx5-tV-hNL" firstAttribute="top" secondItem="5VO-oQ-4jM" secondAttribute="top" constant="24" id="6F1-G5-kJW"/>
+ <constraint firstAttribute="trailing" secondItem="Lx5-tV-hNL" secondAttribute="trailing" constant="24" id="YZH-zj-zvM"/>
+ <constraint firstItem="Lx5-tV-hNL" firstAttribute="leading" secondItem="5VO-oQ-4jM" secondAttribute="leading" constant="24" id="rHz-2D-i6n"/>
+ </constraints>
+ </view>
+ </subviews>
+ <constraints>
+ <constraint firstItem="5VO-oQ-4jM" firstAttribute="width" secondItem="fa2-zl-Fc4" secondAttribute="width" id="86a-VC-VLn"/>
+ <constraint firstAttribute="bottom" secondItem="5VO-oQ-4jM" secondAttribute="bottom" id="ERb-9i-nFf"/>
+ <constraint firstItem="5VO-oQ-4jM" firstAttribute="leading" secondItem="fa2-zl-Fc4" secondAttribute="leading" id="KdL-EB-Idq"/>
+ <constraint firstItem="5VO-oQ-4jM" firstAttribute="top" secondItem="fa2-zl-Fc4" secondAttribute="top" id="avu-mu-m90"/>
+ <constraint firstAttribute="trailing" secondItem="5VO-oQ-4jM" secondAttribute="trailing" id="sUD-Wq-7oa"/>
+ </constraints>
+ </scrollView>
+ </subviews>
+ <color key="backgroundColor" name="Secondary"/>
+ <constraints>
+ <constraint firstItem="fa2-zl-Fc4" firstAttribute="top" secondItem="NHk-FR-Mwy" secondAttribute="top" id="0XS-W3-vV4"/>
+ <constraint firstItem="fa2-zl-Fc4" firstAttribute="bottom" secondItem="uHO-zG-HAA" secondAttribute="bottom" id="3Uo-OT-Y0q"/>
+ <constraint firstItem="fa2-zl-Fc4" firstAttribute="leading" secondItem="uHO-zG-HAA" secondAttribute="leading" id="4rW-4C-pRs"/>
+ <constraint firstItem="fa2-zl-Fc4" firstAttribute="trailing" secondItem="uHO-zG-HAA" secondAttribute="trailing" id="VuC-Wb-6U4"/>
+ </constraints>
+ <viewLayoutGuide key="safeArea" id="uHO-zG-HAA"/>
+ </view>
+ <navigationItem key="navigationItem" title="WireGuard key" id="6ve-v7-tYQ"/>
+ <connections>
+ <outlet property="creationDateLabel" destination="CvU-pV-ixr" id="qSg-Be-sO0"/>
+ <outlet property="publicKeyLabel" destination="UF1-4s-m1S" id="PaX-IU-VEH"/>
+ <outlet property="regenerateKeyButton" destination="OCa-Jz-b7W" id="BZD-Qc-8lO"/>
+ <outlet property="verifyKeyButton" destination="qEF-8w-MdR" id="zuU-Ts-QNG"/>
+ <outlet property="wireguardKeyStatusView" destination="1Ue-pb-GCu" id="VV8-bG-SgD"/>
+ </connections>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="WDa-g6-tXg" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+ <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="1Ue-pb-GCu" customClass="WireguardKeyStatusView" customModule="MullvadVPN" customModuleProvider="target">
+ <rect key="frame" x="0.0" y="0.0" width="240" height="16"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <subviews>
+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="751" text="Key is valid" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ocb-Gs-xuo">
+ <rect key="frame" x="0.0" y="0.0" width="240" height="16"/>
+ <fontDescription key="fontDescription" type="system" pointSize="14"/>
+ <color key="textColor" name="Success"/>
+ <nil key="highlightedColor"/>
+ </label>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZN9-r7-vKB" customClass="SpinnerActivityIndicatorView" customModule="MullvadVPN" customModuleProvider="target">
+ <rect key="frame" x="224" y="0.0" width="16" height="16"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <constraints>
+ <constraint firstAttribute="width" secondItem="ZN9-r7-vKB" secondAttribute="height" multiplier="1:1" id="7dm-3X-rB1"/>
+ </constraints>
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="number" keyPath="thickness">
+ <real key="value" value="2"/>
+ </userDefinedRuntimeAttribute>
+ </userDefinedRuntimeAttributes>
+ </view>
+ </subviews>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <constraints>
+ <constraint firstItem="ZN9-r7-vKB" firstAttribute="height" secondItem="Ocb-Gs-xuo" secondAttribute="height" id="0GM-ky-aMe"/>
+ <constraint firstAttribute="trailing" secondItem="Ocb-Gs-xuo" secondAttribute="trailing" id="C6Q-Ie-omp"/>
+ <constraint firstItem="Ocb-Gs-xuo" firstAttribute="leading" secondItem="1Ue-pb-GCu" secondAttribute="leading" id="M7j-bw-5Ye"/>
+ <constraint firstAttribute="trailing" secondItem="ZN9-r7-vKB" secondAttribute="trailing" id="YRs-9L-5CY"/>
+ <constraint firstItem="ZN9-r7-vKB" firstAttribute="centerY" secondItem="Ocb-Gs-xuo" secondAttribute="centerY" id="d43-KC-IK5"/>
+ <constraint firstAttribute="bottom" secondItem="Ocb-Gs-xuo" secondAttribute="bottom" id="ivo-8C-Rj8"/>
+ <constraint firstItem="Ocb-Gs-xuo" firstAttribute="top" secondItem="1Ue-pb-GCu" secondAttribute="top" id="kgV-z9-gxp"/>
+ </constraints>
+ <connections>
+ <outlet property="activityIndicator" destination="ZN9-r7-vKB" id="lj5-xB-7Ra"/>
+ <outlet property="textLabel" destination="Ocb-Gs-xuo" id="LoV-ls-1BU"/>
+ </connections>
+ </view>
+ </objects>
+ <point key="canvasLocation" x="2576.8000000000002" y="-513.19340329835086"/>
+ </scene>
<!--Account-->
<scene sceneID="Ca0-W1-eLb">
<objects>
@@ -592,7 +859,7 @@
<placeholder placeholderIdentifier="IBFirstResponder" id="3tt-67-nI8" userLabel="First Responder" sceneMemberID="firstResponder"/>
<exit id="P2i-eG-jQx" userLabel="Exit" sceneMemberID="exit"/>
</objects>
- <point key="canvasLocation" x="2649" y="-834"/>
+ <point key="canvasLocation" x="2578" y="-1258"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="er3-W2-NkS">
@@ -663,31 +930,31 @@
</view>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="aFz-H5-sPu" customClass="SelectLocationCell" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="0.0" y="173" width="375" height="43.5"/>
+ <rect key="frame" x="0.0" y="173" width="375" height="48.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="aFz-H5-sPu" id="6nQ-gT-vzf">
- <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="48.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5ag-N4-pUg" customClass="RelayStatusIndicatorView" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="16" y="14" width="16" height="16"/>
+ <rect key="frame" x="16" y="16.5" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="QWj-hh-I3P"/>
<constraint firstAttribute="width" constant="16" id="TFV-yi-LXG"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="y7o-0b-MUV">
- <rect key="frame" x="44" y="11" width="42" height="21.5"/>
+ <rect key="frame" x="44" y="11" width="42" height="26.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="IconTick" translatesAutoresizingMaskIntoConstraints="NO" id="e1o-Bl-zd5">
- <rect key="frame" x="12" y="10" width="24" height="24"/>
+ <rect key="frame" x="0.0" y="0.5" width="48" height="48"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KaW-bN-I51">
- <rect key="frame" x="311" y="0.0" width="64" height="43.5"/>
+ <rect key="frame" x="311" y="0.0" width="64" height="48.5"/>
<constraints>
<constraint firstAttribute="width" constant="64" id="UU3-Di-65E"/>
</constraints>
diff --git a/ios/MullvadVPN/CustomButton.swift b/ios/MullvadVPN/CustomButton.swift
index fa4c867cc9..9cecb1f144 100644
--- a/ios/MullvadVPN/CustomButton.swift
+++ b/ios/MullvadVPN/CustomButton.swift
@@ -46,6 +46,7 @@ import UIKit
setTitleColor(UIColor.white, for: .normal)
setTitleColor(UIColor.lightGray, for: .highlighted)
+ setTitleColor(UIColor.lightGray, for: .disabled)
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
diff --git a/ios/MullvadVPN/EmbeddedViewContainerView.swift b/ios/MullvadVPN/EmbeddedViewContainerView.swift
new file mode 100644
index 0000000000..e2a6a3717f
--- /dev/null
+++ b/ios/MullvadVPN/EmbeddedViewContainerView.swift
@@ -0,0 +1,60 @@
+//
+// EmbeddedViewContainerView.swift
+// MullvadVPN
+//
+// Created by pronebird on 09/12/2019.
+// Copyright © 2019 Amagicom AB. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+/// A `UIView` subclass that implements a host view for an embedded subview via an outlet.
+@IBDesignable class EmbeddedViewContainerView: UIView {
+ @IBOutlet var embeddedView: UIView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+
+ backgroundColor = .clear
+
+ embeddedView.translatesAutoresizingMaskIntoConstraints = false
+
+ addSubview(embeddedView)
+
+ NSLayoutConstraint.activate([
+ embeddedView.topAnchor.constraint(equalTo: topAnchor),
+ embeddedView.leadingAnchor.constraint(equalTo: leadingAnchor),
+ embeddedView.trailingAnchor.constraint(equalTo: trailingAnchor),
+ embeddedView.bottomAnchor.constraint(equalTo: bottomAnchor)
+ ])
+ }
+
+ #if TARGET_INTERFACE_BUILDER
+ override func draw(_ rect: CGRect) {
+ UIColor.white.withAlphaComponent(0.3).setFill()
+ UIColor.white.withAlphaComponent(0.6).setStroke()
+
+ let bezierPath = UIBezierPath(rect: rect)
+ bezierPath.lineWidth = 1
+ bezierPath.fill()
+ bezierPath.stroke()
+
+ let attributedString = NSAttributedString(
+ string: "UIView",
+ attributes: [.foregroundColor: UIColor.white]
+ )
+
+ let textSize = attributedString.size()
+
+ var textRect = rect
+ textRect.origin.x = (rect.width - textSize.width) * 0.5
+ textRect.origin.y = (rect.height - textSize.height) * 0.5
+ textRect.size = textSize
+
+ attributedString.draw(in: textRect)
+ }
+ #endif
+
+
+}
diff --git a/ios/MullvadVPN/LoginViewController.swift b/ios/MullvadVPN/LoginViewController.swift
index e44ac1e454..30877664e8 100644
--- a/ios/MullvadVPN/LoginViewController.swift
+++ b/ios/MullvadVPN/LoginViewController.swift
@@ -171,7 +171,7 @@ class LoginViewController: UIViewController, UITextFieldDelegate, RootContainmen
// controller.
switch loginState {
case .authenticating:
- activityIndicator.isAnimating = true
+ activityIndicator.startAnimating()
// Fallthrough to make sure that the settings button is disabled
// in .authenticating and .success cases.
@@ -182,7 +182,7 @@ class LoginViewController: UIViewController, UITextFieldDelegate, RootContainmen
case .default, .failure:
rootContainerController?.headerBarSettingsButton.isEnabled = true
- activityIndicator.isAnimating = false
+ activityIndicator.stopAnimating()
}
updateDisplayedMessage()
diff --git a/ios/MullvadVPN/SegueIdentifier.swift b/ios/MullvadVPN/SegueIdentifier.swift
index eaa8a10cbb..84f245fdff 100644
--- a/ios/MullvadVPN/SegueIdentifier.swift
+++ b/ios/MullvadVPN/SegueIdentifier.swift
@@ -21,6 +21,10 @@ extension SegueIdentifier {
case showConnect = "ShowConnect"
}
+ enum Settings: String, SegueConvertible {
+ case showWireguardKeys = "ShowWireguardKeys"
+ }
+
enum Connect: String, SegueConvertible {
case embedTunnelControls = "EmbedTunnelControls"
case showRelaySelector = "ShowRelaySelector"
diff --git a/ios/MullvadVPN/SelectLocationController.swift b/ios/MullvadVPN/SelectLocationController.swift
index f58d0c5c78..3a1d90d6ea 100644
--- a/ios/MullvadVPN/SelectLocationController.swift
+++ b/ios/MullvadVPN/SelectLocationController.swift
@@ -110,11 +110,11 @@ class SelectLocationController: UITableViewController {
})
.receive(on: DispatchQueue.main)
.handleEvents(receiveSubscription: { _ in
- self.activityIndicator.isAnimating = true
+ self.activityIndicator.startAnimating()
}, receiveCompletion: { _ in
- self.activityIndicator.isAnimating = false
+ self.activityIndicator.stopAnimating()
}, receiveCancel: {
- self.activityIndicator.isAnimating = false
+ self.activityIndicator.stopAnimating()
})
.sink(receiveCompletion: { (completion) in
if case .failure(let error) = completion {
diff --git a/ios/MullvadVPN/SettingsBasicCell.swift b/ios/MullvadVPN/SettingsBasicCell.swift
new file mode 100644
index 0000000000..a4e98a58eb
--- /dev/null
+++ b/ios/MullvadVPN/SettingsBasicCell.swift
@@ -0,0 +1,13 @@
+//
+// SettingsBasicCell.swift
+// MullvadVPN
+//
+// Created by pronebird on 04/12/2019.
+// Copyright © 2019 Amagicom AB. All rights reserved.
+//
+
+import UIKit
+
+class SettingsBasicCell: SettingsCell {
+ @IBOutlet var titleLabel: UILabel!
+}
diff --git a/ios/MullvadVPN/SettingsViewController.swift b/ios/MullvadVPN/SettingsViewController.swift
index fe6b11743d..4c8cc11c20 100644
--- a/ios/MullvadVPN/SettingsViewController.swift
+++ b/ios/MullvadVPN/SettingsViewController.swift
@@ -16,6 +16,7 @@ class SettingsViewController: UITableViewController {
private enum CellIdentifier: String {
case account = "Account"
case appVersion = "AppVersion"
+ case basicDisclosure = "BasicDisclosure"
}
private weak var accountRow: StaticTableViewRow?
@@ -43,9 +44,21 @@ class SettingsViewController: UITableViewController {
cell.accountExpiryDate = Account.shared.expiry
}
+ let wireguardKeyRow = StaticTableViewRow(reuseIdentifier: CellIdentifier.basicDisclosure.rawValue) { (_, cell) in
+ let cell = cell as! SettingsBasicCell
+
+ cell.titleLabel.text = NSLocalizedString("WireGuard key", comment: "")
+ }
+
+ wireguardKeyRow.actionBlock = { [weak self] (indexPath) in
+ self?.performSegue(
+ withIdentifier: SegueIdentifier.Settings.showWireguardKeys.rawValue,
+ sender: nil)
+ }
+
self.accountRow = accountRow
- topSection.addRows([accountRow])
+ topSection.addRows([accountRow, wireguardKeyRow])
staticDataSource.addSections([topSection])
}
diff --git a/ios/MullvadVPN/SpinnerActivityIndicatorView.swift b/ios/MullvadVPN/SpinnerActivityIndicatorView.swift
index ea33c64024..2e8140c97f 100644
--- a/ios/MullvadVPN/SpinnerActivityIndicatorView.swift
+++ b/ios/MullvadVPN/SpinnerActivityIndicatorView.swift
@@ -14,7 +14,7 @@ private let kAnimationDuration = 0.6
@IBDesignable class SpinnerActivityIndicatorView: UIView {
/// Thickness of the front and back circles
- var thickness: CGFloat = 6 {
+ @IBInspectable var thickness: CGFloat = 6 {
didSet {
setLayersThickness()
}
@@ -34,17 +34,7 @@ private let kAnimationDuration = 0.6
}
}
- @IBInspectable var isAnimating: Bool = false {
- didSet {
- guard oldValue != isAnimating else { return }
-
- if isAnimating {
- startAnimating()
- } else {
- stopAnimating()
- }
- }
- }
+ private(set) var isAnimating = false
fileprivate let frontCircle = CAShapeLayer()
fileprivate let backCircle = CAShapeLayer()
@@ -88,18 +78,24 @@ private let kAnimationDuration = 0.6
setFrontCircleLayerColor()
}
- // MARK: - Private
+ func startAnimating() {
+ guard !isAnimating else { return }
+ isAnimating = true
- private func startAnimating() {
isHidden = false
addAnimation()
}
- private func stopAnimating() {
+ func stopAnimating() {
+ guard isAnimating else { return }
+ isAnimating = false
+
isHidden = true
removeAnimation()
}
+ // MARK: - Private
+
private func commonInit() {
registerForAppStateNotifications()
diff --git a/ios/MullvadVPN/StaticTableViewDataSource.swift b/ios/MullvadVPN/StaticTableViewDataSource.swift
index 11145bf607..305def36a6 100644
--- a/ios/MullvadVPN/StaticTableViewDataSource.swift
+++ b/ios/MullvadVPN/StaticTableViewDataSource.swift
@@ -103,6 +103,12 @@ class StaticTableViewDataSource: NSObject, UITableViewDataSource, UITableViewDel
return cell
}
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let row = self.row(for: indexPath)
+
+ row.actionBlock?(indexPath)
+ }
+
// MARK: - Private
private func row(for indexPath: IndexPath) -> StaticTableViewRow {
diff --git a/ios/MullvadVPN/WebLinks.swift b/ios/MullvadVPN/WebLinks.swift
index 3a48f96c16..29edd6eaac 100644
--- a/ios/MullvadVPN/WebLinks.swift
+++ b/ios/MullvadVPN/WebLinks.swift
@@ -8,10 +8,13 @@
import Foundation
-struct WebLinks {
+enum WebLinks {}
+
+extension WebLinks {
static let createAccountURL = URL(string: "https://mullvad.net/account/create/")!
static let purchaseURL = URL(string: "https://mullvad.net/account/login/")!
static let faqURL = URL(string: "https://mullvad.net/faq/")!
+ static let manageKeysURL = URL(string: "https://mullvad.net/en/account/ports/")!
}
diff --git a/ios/MullvadVPN/WireguardKeysViewController.swift b/ios/MullvadVPN/WireguardKeysViewController.swift
new file mode 100644
index 0000000000..9024f82627
--- /dev/null
+++ b/ios/MullvadVPN/WireguardKeysViewController.swift
@@ -0,0 +1,284 @@
+//
+// WireguardKeysViewController.swift
+// MullvadVPN
+//
+// Created by pronebird on 04/12/2019.
+// Copyright © 2019 Amagicom AB. All rights reserved.
+//
+
+import Combine
+import Foundation
+import UIKit
+import os
+
+/// A UI refresh interval for the public key creation date (in seconds)
+private let kCreationDateRefreshInterval = TimeInterval(60)
+
+private enum WireguardKeysViewState {
+ case `default`
+ case verifyingKey
+ case verifiedKey(Bool)
+ case regeneratingKey
+}
+
+enum VerifyWireguardPublicKeyError {
+ case network(MullvadAPI.Error)
+ case server(MullvadAPI.ResponseError)
+}
+
+extension VerifyWireguardPublicKeyError: LocalizedError {
+ var errorDescription: String? {
+ return NSLocalizedString("Cannot verify the public key", comment: "")
+ }
+
+ var failureReason: String? {
+ switch self {
+ case .network(.network(let urlError)):
+ return urlError.localizedDescription
+ case .server(let serverError):
+ return serverError.errorDescription
+ default:
+ return NSLocalizedString("Internal error", comment: "")
+ }
+ }
+}
+
+class WireguardKeysViewController: UIViewController {
+
+ @IBOutlet var publicKeyLabel: UILabel!
+ @IBOutlet var creationDateLabel: UILabel!
+ @IBOutlet var regenerateKeyButton: UIButton!
+ @IBOutlet var verifyKeyButton: UIButton!
+ @IBOutlet var wireguardKeyStatusView: WireguardKeyStatusView!
+
+ private var fetchKeySubscriber: AnyCancellable?
+ private var verifyKeySubscriber: AnyCancellable?
+ private var regenerateKeySubscriber: AnyCancellable?
+ private var timerSubscriber: AnyCancellable?
+
+ private let apiClient = MullvadAPI()
+ private var publicKey: WireguardPublicKey?
+
+ private var state: WireguardKeysViewState = .default {
+ didSet {
+ updateViewState(state)
+ }
+ }
+
+ private lazy var relativeFormatter: DateComponentsFormatter = {
+ let formatter = DateComponentsFormatter()
+ formatter.unitsStyle = .full
+ formatter.allowedUnits = [.minute, .hour, .day, .month, .year]
+ formatter.maximumUnitCount = 1
+
+ return formatter
+ }()
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ // Reset Storyboard placeholders
+ publicKeyLabel.text = "-"
+ creationDateLabel.text = "-"
+
+ timerSubscriber = Timer.publish(every: kCreationDateRefreshInterval, on: .main, in: .common)
+ .autoconnect()
+ .sink { [weak self] _ in
+ guard let self = self else { return }
+
+ if let creationDate = self.publicKey?.creationDate {
+ self.updateCreationDateLabel(with: creationDate)
+ }
+ }
+
+ loadPublicKey()
+ }
+
+ // MARK: - IBActions
+
+ @IBAction func handleRegenerateKey(_ sender: Any) {
+ regeneratePrivateKey()
+ }
+
+ @IBAction func handleVerifyKey(_ sender: Any) {
+ guard let accountToken = Account.shared.token,
+ let publicKey = publicKey else { return }
+
+ verifyKey(accountToken: accountToken, publicKey: publicKey)
+ }
+
+ @IBAction func handleManageKeys(_ sender: Any) {
+ UIApplication.shared.open(WebLinks.manageKeysURL, options: [:])
+ }
+
+ // MARK: - Private
+
+ private func formatKeyGenerationElapsedTime(with creationDate: Date) -> String? {
+ let elapsedTime = Date().timeIntervalSince(creationDate)
+
+ if elapsedTime >= 60 {
+ if let formattedInterval = relativeFormatter.string(from: elapsedTime) {
+ return String.localizedStringWithFormat(
+ NSLocalizedString("%@ ago", comment: ""),
+ formattedInterval)
+ } else {
+ return nil
+ }
+ } else {
+ return NSLocalizedString("Less than a minute ago", comment: "")
+ }
+ }
+
+ private func updateCreationDateLabel(with creationDate: Date) {
+ creationDateLabel.text = formatKeyGenerationElapsedTime(with: creationDate) ?? "-"
+ }
+
+ private func loadPublicKey() {
+ fetchKeySubscriber = TunnelManager.shared.getWireguardPublicKey()
+ .receive(on: DispatchQueue.main)
+ .sink(receiveCompletion: { (completion) in
+ switch completion {
+ case .finished:
+ break
+
+ case .failure(let error):
+ os_log(.error, "Failed to receive the public key for Wireguard: %{public}s",
+ error.localizedDescription)
+
+ self.presentError(error, preferredStyle: .alert)
+ }
+ }) { [weak self] (publicKey) in
+ guard let self = self else { return }
+
+ self.publicKeyLabel.text = publicKey.rawRepresentation.base64EncodedString()
+ self.updateCreationDateLabel(with: publicKey.creationDate)
+
+ self.publicKey = publicKey
+ }
+ }
+
+ private func updateViewState(_ state: WireguardKeysViewState) {
+ switch state {
+ case .default:
+ setKeyActionButtonsEnabled(true)
+ wireguardKeyStatusView.status = .default
+
+ case .verifyingKey:
+ setKeyActionButtonsEnabled(false)
+ wireguardKeyStatusView.status = .verifying
+
+ case .verifiedKey(let isValid):
+ setKeyActionButtonsEnabled(true)
+ wireguardKeyStatusView.status = .verified(isValid)
+
+ case .regeneratingKey:
+ setKeyActionButtonsEnabled(false)
+ wireguardKeyStatusView.status = .verifying
+ }
+ }
+
+ private func setKeyActionButtonsEnabled(_ enabled: Bool) {
+ regenerateKeyButton.isEnabled = enabled
+ verifyKeyButton.isEnabled = enabled
+ }
+
+ private func verifyKey(accountToken: String, publicKey: WireguardPublicKey) {
+ verifyKeySubscriber = apiClient.checkWireguardKey(
+ accountToken: accountToken,
+ publicKey: publicKey.rawRepresentation
+ )
+ .retry(1)
+ .receive(on: DispatchQueue.main)
+ .mapError { VerifyWireguardPublicKeyError.network($0) }
+ .flatMap({ (response) in
+ response.result
+ .mapError { VerifyWireguardPublicKeyError.server($0) }
+ .publisher
+ })
+ .handleEvents(receiveSubscription: { _ in
+ self.updateViewState(.verifyingKey)
+ })
+ .sink(receiveCompletion: { (completion) in
+ switch completion {
+ case .finished:
+ break
+
+ case .failure(let error):
+ self.presentError(error, preferredStyle: .alert)
+ self.updateViewState(.default)
+ }
+ }) { (isValid) in
+ self.updateViewState(.verifiedKey(isValid))
+ }
+ }
+
+ private func regeneratePrivateKey() {
+ regenerateKeySubscriber = TunnelManager.shared.regeneratePrivateKey()
+ .receive(on: DispatchQueue.main)
+ .handleEvents(receiveSubscription: { (_) in
+ self.updateViewState(.regeneratingKey)
+ }, receiveCompletion: { (completion) in
+ self.updateViewState(.default)
+ })
+ .sink { (completion) in
+ switch completion {
+ case .finished:
+ self.loadPublicKey()
+
+ case .failure(let error):
+ os_log(.error, "Failed to re-generate the private key: %{public}s",
+ error.errorDescription ?? "")
+
+ self.presentError(error, preferredStyle: .alert)
+ }
+ }
+ }
+
+}
+
+class WireguardKeyStatusView: UIView {
+
+ enum Status {
+ case `default`, verifying, verified(Bool)
+ }
+
+ @IBOutlet var textLabel: UILabel!
+ @IBOutlet var activityIndicator: SpinnerActivityIndicatorView!
+
+ var status: Status = .default {
+ didSet {
+ updateView()
+ }
+ }
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+
+ updateView()
+ }
+
+ private func updateView() {
+ switch status {
+ case .default:
+ textLabel.isHidden = true
+ activityIndicator.stopAnimating()
+
+ case .verifying:
+ textLabel.isHidden = true
+ activityIndicator.startAnimating()
+
+ case .verified(let isValid):
+ textLabel.isHidden = false
+ activityIndicator.stopAnimating()
+
+ if isValid {
+ textLabel.textColor = .successColor
+ textLabel.text = NSLocalizedString("Key is valid", comment: "")
+ } else {
+ textLabel.textColor = .dangerColor
+ textLabel.text = NSLocalizedString("Key is invalid", comment: "")
+ }
+ }
+ }
+
+}