summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2020-07-15 18:51:48 +0200
committerAndrej Mihajlov <and@mullvad.net>2020-07-15 18:51:48 +0200
commitd68ce4e2c6fc2a458405b50a13b1dd8ded120de5 (patch)
tree9d654e401852bfe9a40df17a0dd11688f690b8ad
parent5afa504637d787ca587f9dbd987a5d6f8093a5ac (diff)
parent6cbaef80e9c57808e9ef0746edf78f5718a9dacd (diff)
downloadmullvadvpn-d68ce4e2c6fc2a458405b50a13b1dd8ded120de5.tar.xz
mullvadvpn-d68ce4e2c6fc2a458405b50a13b1dd8ded120de5.zip
Merge branch 'ios12-support'
-rw-r--r--ios/CHANGELOG.md1
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj62
-rw-r--r--ios/MullvadVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata2
-rw-r--r--ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved25
-rw-r--r--ios/MullvadVPN/AccountViewController.swift6
-rw-r--r--ios/MullvadVPN/AppButton.swift15
-rw-r--r--ios/MullvadVPN/AutoDisposableSink.swift67
-rw-r--r--ios/MullvadVPN/Base.lproj/Main.storyboard46
-rw-r--r--ios/MullvadVPN/CancellableDelayPublisher.swift32
-rw-r--r--ios/MullvadVPN/ConnectionPanelView.swift29
-rw-r--r--ios/MullvadVPN/CustomNavigationBar.swift8
-rw-r--r--ios/MullvadVPN/MutuallyExclusive.swift125
-rw-r--r--ios/MullvadVPN/ReplaceNilWithError.swift24
-rw-r--r--ios/MullvadVPN/SegueIdentifier.swift4
-rw-r--r--ios/MullvadVPN/SelectLocationController.swift116
-rw-r--r--ios/MullvadVPN/SettingsCell.swift11
-rw-r--r--ios/MullvadVPN/SettingsViewController.swift13
17 files changed, 193 insertions, 393 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md
index 45518ed1ff..fccf62bda3 100644
--- a/ios/CHANGELOG.md
+++ b/ios/CHANGELOG.md
@@ -24,6 +24,7 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
### Added
+- Add support for iOS 12.
- Ship the initial relay list with the app, and do once an hour periodic refresh in background.
- Refresh account expiry when visiting settings.
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 92961b6592..8fa3f3734e 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -42,8 +42,6 @@
581CBCE62296B97300727D7F /* ViewControllerIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581CBCE52296B97300727D7F /* ViewControllerIdentifier.swift */; };
581CBCEC2298041B00727D7F /* SettingsAppVersionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581CBCEB2298041B00727D7F /* SettingsAppVersionCell.swift */; };
581CBCEE229826FD00727D7F /* StaticTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581CBCED229826FD00727D7F /* StaticTableViewDataSource.swift */; };
- 582650862384116F00FA7A86 /* ReplaceNilWithError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582650832384102800FA7A86 /* ReplaceNilWithError.swift */; };
- 582650872384117900FA7A86 /* ReplaceNilWithError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582650832384102800FA7A86 /* ReplaceNilWithError.swift */; };
582AE3102440A6CA00E6733A /* AccountTokenInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */; };
582AE3122440CA0D00E6733A /* AccountTokenInputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE3112440CA0D00E6733A /* AccountTokenInputTests.swift */; };
582AE3132440CA2700E6733A /* AccountTokenInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */; };
@@ -57,11 +55,8 @@
5840250422B11AB700E4CFEC /* MullvadEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */; };
5840250522B11AB700E4CFEC /* MullvadEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */; };
5845F838236C466400B2D93C /* TunnelControlViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F837236C466400B2D93C /* TunnelControlViewController.swift */; };
- 5845F83A236C6A7200B2D93C /* AutoDisposableSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F839236C6A7200B2D93C /* AutoDisposableSink.swift */; };
- 5845F83C236C72E300B2D93C /* AutoDisposableSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F839236C6A7200B2D93C /* AutoDisposableSink.swift */; };
5845F842236CBACD00B2D93C /* PacketTunnelIpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */; };
5845F843236CBDAB00B2D93C /* PacketTunnelIpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */; };
- 584E96BA240D791E00D3334F /* CancellableDelayPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584E96B9240D791E00D3334F /* CancellableDelayPublisher.swift */; };
584E96BC240FD4DA00D3334F /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8623F43901009F7EA6 /* Location.swift */; };
584E96BD240FD4DA00D3334F /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8623F43901009F7EA6 /* Location.swift */; };
584E96BE240FD4DB00D3334F /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8623F43901009F7EA6 /* Location.swift */; };
@@ -94,8 +89,6 @@
5888AD83227B11080051EB06 /* SelectLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD82227B11080051EB06 /* SelectLocationCell.swift */; };
5888AD87227B17950051EB06 /* SelectLocationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD86227B17950051EB06 /* SelectLocationController.swift */; };
5888AD89227B18C40051EB06 /* RelayList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD88227B18C40051EB06 /* RelayList.swift */; };
- 588AE72F2362001F009F9F2E /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588AE72E2362001F009F9F2E /* MutuallyExclusive.swift */; };
- 588AE730236200E2009F9F2E /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588AE72E2362001F009F9F2E /* MutuallyExclusive.swift */; };
588D2FE3248AC27F00E313F7 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E973DD24850EB600096F90 /* AsyncOperation.swift */; };
58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */; };
5896AE7E246ACE65005B36CB /* KeychainAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDEB245059F000CB0F5B /* KeychainAttributes.swift */; };
@@ -172,6 +165,7 @@
58F3C09A249B9852003E76BE /* x25519.c in Sources */ = {isa = PBXBuildFile; fileRef = 58F3C098249B978C003E76BE /* x25519.c */; };
58F3C09C249B99DD003E76BE /* Curve25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F3C09B249B99DD003E76BE /* Curve25519.swift */; };
58F3C09D249B99DD003E76BE /* Curve25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F3C09B249B99DD003E76BE /* Curve25519.swift */; };
+ 58F3C0A0249BBF1E003E76BE /* DiffableDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 58F3C09F249BBF1E003E76BE /* DiffableDataSources */; };
58F3C0A2249CA1E0003E76BE /* HeaderBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58F3C0A1249CA1E0003E76BE /* HeaderBarView.xib */; };
58F3C0A4249CB069003E76BE /* HeaderBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F3C0A3249CB069003E76BE /* HeaderBarView.swift */; };
58F3C0A624A50157003E76BE /* relays.json in Resources */ = {isa = PBXBuildFile; fileRef = 58F3C0A524A50155003E76BE /* relays.json */; };
@@ -256,7 +250,6 @@
581CBCE52296B97300727D7F /* ViewControllerIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerIdentifier.swift; sourceTree = "<group>"; };
581CBCEB2298041B00727D7F /* SettingsAppVersionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsAppVersionCell.swift; sourceTree = "<group>"; };
581CBCED229826FD00727D7F /* StaticTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticTableViewDataSource.swift; sourceTree = "<group>"; };
- 582650832384102800FA7A86 /* ReplaceNilWithError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceNilWithError.swift; sourceTree = "<group>"; };
582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountTokenInput.swift; sourceTree = "<group>"; };
582AE3112440CA0D00E6733A /* AccountTokenInputTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTokenInputTests.swift; sourceTree = "<group>"; };
582BB1AE229566420055B6EF /* SettingsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsCell.swift; sourceTree = "<group>"; };
@@ -267,10 +260,8 @@
5840250022B1124600E4CFEC /* IpAddress+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IpAddress+Codable.swift"; sourceTree = "<group>"; };
5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadEndpoint.swift; sourceTree = "<group>"; };
5845F837236C466400B2D93C /* TunnelControlViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelControlViewController.swift; sourceTree = "<group>"; };
- 5845F839236C6A7200B2D93C /* AutoDisposableSink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoDisposableSink.swift; sourceTree = "<group>"; };
5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelIpc.swift; sourceTree = "<group>"; };
584B26F3237434D00073B10E /* RelaySelectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelectorTests.swift; sourceTree = "<group>"; };
- 584E96B9240D791E00D3334F /* CancellableDelayPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancellableDelayPublisher.swift; sourceTree = "<group>"; };
58561C98239A5D1500BD6B5E /* IPEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPEndpoint.swift; sourceTree = "<group>"; };
5860F1C123A785C600CEA666 /* WireguardDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireguardDevice.swift; sourceTree = "<group>"; };
5860F1C323A8D25F00CEA666 /* WireguardConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireguardConfiguration.swift; sourceTree = "<group>"; };
@@ -296,7 +287,6 @@
5888AD82227B11080051EB06 /* SelectLocationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationCell.swift; sourceTree = "<group>"; };
5888AD86227B17950051EB06 /* SelectLocationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationController.swift; sourceTree = "<group>"; };
5888AD88227B18C40051EB06 /* RelayList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayList.swift; sourceTree = "<group>"; };
- 588AE72E2362001F009F9F2E /* MutuallyExclusive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutuallyExclusive.swift; sourceTree = "<group>"; };
58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEProviderStopReason+Debug.swift"; sourceTree = "<group>"; };
5894E725236B2801008A2793 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateComponentsFormatting.swift; sourceTree = "<group>"; };
@@ -386,6 +376,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 58F3C0A0249BBF1E003E76BE /* DiffableDataSources in Frameworks */,
586BD68422B7BBE400BB7F9F /* NetworkExtension.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -455,12 +446,13 @@
58CE5E57224146200008646E = {
isa = PBXGroup;
children = (
+ 58F3C0A824A50C0E003E76BE /* Assets */,
58ECD29023F178FD004298B6 /* Configurations */,
+ 0E15C74FDCF763609B367486 /* Frameworks */,
58CE5E62224146200008646E /* MullvadVPN */,
- 58CE5E7A224146470008646E /* PacketTunnel */,
- 58B0A2A1238EE67E00BC001D /* MullvadVPNTests */,
58D0C79423F1CE7000FE9BA7 /* MullvadVPNScreenshots */,
- 0E15C74FDCF763609B367486 /* Frameworks */,
+ 58B0A2A1238EE67E00BC001D /* MullvadVPNTests */,
+ 58CE5E7A224146470008646E /* PacketTunnel */,
58CE5E61224146200008646E /* Products */,
);
sourceTree = "<group>";
@@ -493,11 +485,9 @@
58DF28A42417CB4B00E836B0 /* AppStorePaymentManager.swift */,
58FD5BE624192A2B00112C88 /* AppStoreReceipt.swift */,
58CE5E6A224146210008646E /* Assets.xcassets */,
- 5845F839236C6A7200B2D93C /* AutoDisposableSink.swift */,
588534BD246193C00018B744 /* AutomaticKeyRotationManager.swift */,
589AB4F6227B64450039131E /* BasicTableViewCell.swift */,
58EC4E6B23915325003F5C5B /* Bundle+MullvadVersion.swift */,
- 584E96B9240D791E00D3334F /* CancellableDelayPublisher.swift */,
58F840B12464491D0044E708 /* ChainedError.swift */,
58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */,
58CCA00F224249A1004F3011 /* ConnectViewController.swift */,
@@ -531,7 +521,6 @@
58ADDB3D227B1CD900FAFEA7 /* MullvadRpc.swift */,
58FBDAAA22A52DC500EB69A3 /* MullvadVPN-Bridging-Header.h */,
5866F39B2243B82D00168AE5 /* MullvadVPN.entitlements */,
- 588AE72E2362001F009F9F2E /* MutuallyExclusive.swift */,
58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */,
5811DE4F239014550011EB53 /* NEVPNStatus+Debug.swift */,
58CC40EE24A601900019D96E /* ObserverList.swift */,
@@ -543,7 +532,6 @@
5888AD88227B18C40051EB06 /* RelayList.swift */,
58781CD422AFBA39009B9D8E /* RelaySelector.swift */,
5888AD7E2279B6BF0051EB06 /* RelayStatusIndicatorView.swift */,
- 582650832384102800FA7A86 /* ReplaceNilWithError.swift */,
587425C02299833500CA2045 /* RootContainerViewController.swift */,
5867A51B2248F26A005513C0 /* SegueIdentifier.swift */,
5888AD82227B11080051EB06 /* SelectLocationCell.swift */,
@@ -675,6 +663,9 @@
58CE5E80224146470008646E /* PBXTargetDependency */,
);
name = MullvadVPN;
+ packageProductDependencies = (
+ 58F3C09F249BBF1E003E76BE /* DiffableDataSources */,
+ );
productName = MullvadVPN;
productReference = 58CE5E60224146200008646E /* MullvadVPN.app */;
productType = "com.apple.product-type.application";
@@ -768,6 +759,9 @@
Base,
);
mainGroup = 58CE5E57224146200008646E;
+ packageReferences = (
+ 58F3C09E249BBF1E003E76BE /* XCRemoteSwiftPackageReference "DiffableDataSources" */,
+ );
productRefGroup = 58CE5E61224146200008646E /* Products */;
projectDirPath = "";
projectRoot = "";
@@ -916,7 +910,6 @@
5888AD87227B17950051EB06 /* SelectLocationController.swift in Sources */,
580EE20424B321EC00F9D8A1 /* OperationObserver.swift in Sources */,
58F19E35228C15BA00C7710B /* SpinnerActivityIndicatorView.swift in Sources */,
- 584E96BA240D791E00D3334F /* CancellableDelayPublisher.swift in Sources */,
58A99ED3240014A0006599E9 /* ConsentViewController.swift in Sources */,
58FAEE0124533A9C00CB0F5B /* KeychainClass.swift in Sources */,
5845F838236C466400B2D93C /* TunnelControlViewController.swift in Sources */,
@@ -925,7 +918,6 @@
587CBFE322807F530028DED3 /* UIColor+Helpers.swift in Sources */,
581CBCEC2298041B00727D7F /* SettingsAppVersionCell.swift in Sources */,
58FAEDFD24533A5500CB0F5B /* KeychainMatchLimit.swift in Sources */,
- 5845F83A236C6A7200B2D93C /* AutoDisposableSink.swift in Sources */,
5840250422B11AB700E4CFEC /* MullvadEndpoint.swift in Sources */,
58CC40EF24A601900019D96E /* ObserverList.swift in Sources */,
58CCA01822426713004F3011 /* AccountViewController.swift in Sources */,
@@ -943,7 +935,6 @@
580EE22124B3240100F9D8A1 /* TransformOperationObserver.swift in Sources */,
582BB1AF229566420055B6EF /* SettingsCell.swift in Sources */,
5873884D239E6D7E00E96C4E /* EmbeddedViewContainerView.swift in Sources */,
- 582650862384116F00FA7A86 /* ReplaceNilWithError.swift in Sources */,
58F3C0A4249CB069003E76BE /* HeaderBarView.swift in Sources */,
58B9EB132488ED2100095626 /* AlertPresenter.swift in Sources */,
587A01FC23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift in Sources */,
@@ -975,7 +966,6 @@
587425C12299833500CA2045 /* RootContainerViewController.swift in Sources */,
580EE20124B321D500F9D8A1 /* OperationProtocol.swift in Sources */,
5896AE84246D5889005B36CB /* CustomDateComponentsFormatting.swift in Sources */,
- 588AE72F2362001F009F9F2E /* MutuallyExclusive.swift in Sources */,
5888AD89227B18C40051EB06 /* RelayList.swift in Sources */,
580EE21824B3235100F9D8A1 /* AnyOperationObserver.swift in Sources */,
587AD7C623421D7000E93A53 /* TunnelSettings.swift in Sources */,
@@ -994,7 +984,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 5845F83C236C72E300B2D93C /* AutoDisposableSink.swift in Sources */,
5860F1C423A8D25F00CEA666 /* WireguardConfiguration.swift in Sources */,
58F3C09D249B99DD003E76BE /* Curve25519.swift in Sources */,
580EE21F24B3237F00F9D8A1 /* OutputOperation.swift in Sources */,
@@ -1003,7 +992,6 @@
58FAEE0324533ABE00CB0F5B /* KeychainReturn.swift in Sources */,
58BFA5CD22A7CE1F00A6173D /* ApplicationConfiguration.swift in Sources */,
58BFA5C222A7C92900A6173D /* JsonRpc.swift in Sources */,
- 588AE730236200E2009F9F2E /* MutuallyExclusive.swift in Sources */,
580EE20724B3222400F9D8A1 /* ExclusivityController.swift in Sources */,
58F840B02464382C0044E708 /* KeychainItemRevision.swift in Sources */,
58C6B35122BB7CFD003C19AD /* IPAddressRange.swift in Sources */,
@@ -1032,7 +1020,6 @@
58AEEF6C2344A49D00C9BBD5 /* TunnelSettingsManager.swift in Sources */,
58C6B35F22BBBFE3003C19AD /* Data+HexCoding.swift in Sources */,
5840250522B11AB700E4CFEC /* MullvadEndpoint.swift in Sources */,
- 582650872384117900FA7A86 /* ReplaceNilWithError.swift in Sources */,
58BFA5C722A7C97F00A6173D /* RelayCache.swift in Sources */,
58BFA5C022A7C8A900A6173D /* MullvadRpc.swift in Sources */,
580EE21024B322E700F9D8A1 /* TransformOperation.swift in Sources */,
@@ -1275,7 +1262,7 @@
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = CKG9MXH72F;
INFOPLIST_FILE = MullvadVPN/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1303,7 +1290,7 @@
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = CKG9MXH72F;
INFOPLIST_FILE = MullvadVPN/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1330,6 +1317,7 @@
DEVELOPMENT_TEAM = CKG9MXH72F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1357,6 +1345,7 @@
DEVELOPMENT_TEAM = CKG9MXH72F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = PacketTunnel/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1502,6 +1491,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 58F3C09E249BBF1E003E76BE /* XCRemoteSwiftPackageReference "DiffableDataSources" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/ra1028/DiffableDataSources.git";
+ requirement = {
+ kind = exactVersion;
+ version = 0.4.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 58F3C09F249BBF1E003E76BE /* DiffableDataSources */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 58F3C09E249BBF1E003E76BE /* XCRemoteSwiftPackageReference "DiffableDataSources" */;
+ productName = DiffableDataSources;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = 58CE5E58224146200008646E /* Project object */;
}
diff --git a/ios/MullvadVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/MullvadVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index ca6506a98c..919434a625 100644
--- a/ios/MullvadVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/ios/MullvadVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
- location = "self:MullvadVPN.xcodeproj">
+ location = "self:">
</FileRef>
</Workspace>
diff --git a/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000000..497878448d
--- /dev/null
+++ b/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,25 @@
+{
+ "object": {
+ "pins": [
+ {
+ "package": "DiffableDataSources",
+ "repositoryURL": "https://github.com/ra1028/DiffableDataSources.git",
+ "state": {
+ "branch": null,
+ "revision": "581b4f8d1634e83c6b33caaafdc6a115a74650c3",
+ "version": "0.4.0"
+ }
+ },
+ {
+ "package": "DifferenceKit",
+ "repositoryURL": "https://github.com/ra1028/DifferenceKit.git",
+ "state": {
+ "branch": null,
+ "revision": "14c66681e12a38b81045f44c6c29724a0d4b0e72",
+ "version": "1.1.5"
+ }
+ }
+ ]
+ },
+ "version": 1
+}
diff --git a/ios/MullvadVPN/AccountViewController.swift b/ios/MullvadVPN/AccountViewController.swift
index 6d3bf1dbcf..d95adcf738 100644
--- a/ios/MullvadVPN/AccountViewController.swift
+++ b/ios/MullvadVPN/AccountViewController.swift
@@ -158,7 +158,11 @@ class AccountViewController: UIViewController, AppStorePaymentObserver {
view.isUserInteractionEnabled = enableUserInteraction
// Prevent view controller from being swiped away by user
- isModalInPresentation = !enableUserInteraction
+ if #available(iOS 13.0, *) {
+ isModalInPresentation = !enableUserInteraction
+ } else {
+ // Fallback on earlier versions
+ }
// Hide back button in navigation bar
navigationItem.setHidesBackButton(!enableUserInteraction, animated: animated)
diff --git a/ios/MullvadVPN/AppButton.swift b/ios/MullvadVPN/AppButton.swift
index b98cc9c2cc..25eed30cf8 100644
--- a/ios/MullvadVPN/AppButton.swift
+++ b/ios/MullvadVPN/AppButton.swift
@@ -212,6 +212,21 @@ private extension UIControl.State {
}
}
+ override func layoutSubviews() {
+ super.layoutSubviews()
+
+ if #available(iOS 13, *) {
+ // no-op
+ } else {
+ // Fix: on iOS 12 the image view frame is not always set, even though the `UIButton`
+ // calls `imageRect` to compute the image layout frame.
+ let imageRect = self.imageRect(forContentRect: contentRect(forBounds: bounds))
+ if imageView?.frame != imageRect {
+ imageView?.frame = imageRect
+ }
+ }
+ }
+
private func computeLayout(forContentRect contentRect: CGRect) -> (CGRect, CGRect) {
var imageRect = super.imageRect(forContentRect: contentRect)
var titleRect = super.titleRect(forContentRect: contentRect)
diff --git a/ios/MullvadVPN/AutoDisposableSink.swift b/ios/MullvadVPN/AutoDisposableSink.swift
deleted file mode 100644
index 548e343053..0000000000
--- a/ios/MullvadVPN/AutoDisposableSink.swift
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-// AutoDisposableSink.swift
-// MullvadVPN
-//
-// Created by pronebird on 01/11/2019.
-// Copyright © 2019 Mullvad VPN AB. All rights reserved.
-//
-
-import Combine
-import Foundation
-
-/// A thread safe storage for a set of `AnyCancellable` objects
-final class CancellableSet {
- private let lock = NSLock()
- private var storage = Set<AnyCancellable>()
-
- func append(_ cancellable: AnyCancellable) {
- lock.lock()
- storage.insert(cancellable)
- lock.unlock()
- }
-
- func remove(_ cancellable: AnyCancellable) {
- lock.lock()
- storage.remove(cancellable)
- lock.unlock()
- }
-}
-
-extension Publisher {
-
- /// Make a `Publishers.Sink` subscriber and put it in the given `CancellableSet`, automatically
- /// remove it upon completion.
- func autoDisposableSink(cancellableSet: CancellableSet, receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure>) -> Void), receiveValue: @escaping ((Self.Output) -> Void)) -> Void {
- var sharedCancellable: AnyCancellable?
-
- let disposeSubscriber = {
- if let sharedCancellable = sharedCancellable {
- cancellableSet.remove(sharedCancellable)
- }
- }
-
- let cancellable = handleEvents(receiveCancel: {
- disposeSubscriber()
- }).sink(receiveCompletion: { (completion) in
- receiveCompletion(completion)
-
- disposeSubscriber()
- }, receiveValue: receiveValue)
-
- sharedCancellable = cancellable
- cancellableSet.append(cancellable)
- }
-
-}
-
-extension Publisher where Output == Void, Failure: Error {
-
- func sink(receiveCompletion: @escaping ((Subscribers.Completion<Failure>) -> Void)) -> AnyCancellable {
- return sink(receiveCompletion: receiveCompletion, receiveValue: { _ in })
- }
-
- func autoDisposableSink(cancellableSet: CancellableSet, receiveCompletion: @escaping ((Subscribers.Completion<Failure>) -> Void)) -> Void {
- return autoDisposableSink(cancellableSet: cancellableSet, receiveCompletion: receiveCompletion, receiveValue: { _ in })
- }
-
-}
diff --git a/ios/MullvadVPN/Base.lproj/Main.storyboard b/ios/MullvadVPN/Base.lproj/Main.storyboard
index f1a8fcd7fb..db5a927b89 100644
--- a/ios/MullvadVPN/Base.lproj/Main.storyboard
+++ b/ios/MullvadVPN/Base.lproj/Main.storyboard
@@ -44,7 +44,7 @@
</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="127.5" y="137" width="120" height="120"/>
+ <rect key="frame" x="157.5" y="167" width="60" height="60"/>
</imageView>
<view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="V3j-Lb-fSQ" userLabel="Form">
<rect key="frame" x="0.0" y="251" width="375" height="125.5"/>
@@ -292,11 +292,11 @@
<objects>
<tableViewController id="SHd-a4-ewi" customClass="SettingsViewController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="6Gz-UM-orK">
- <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" name="Secondary"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <color key="separatorColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <color key="separatorColor" name="Secondary"/>
<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"/>
@@ -430,11 +430,11 @@
<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"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<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"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5VO-oQ-4jM" userLabel="Container">
<rect key="frame" x="0.0" y="0.0" width="375" height="295.5"/>
@@ -666,11 +666,11 @@
<objects>
<viewController id="ruh-Q2-P39" customClass="AccountViewController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Qpl-bL-ZGl">
- <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="saE-dV-AgF">
- <rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rkG-Xa-pEO" userLabel="Container">
<rect key="frame" x="0.0" y="0.0" width="375" height="349.5"/>
@@ -896,10 +896,10 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JYh-33-d0O">
- <rect key="frame" x="0.0" y="0.0" width="375" height="598"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="597"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="N9k-cQ-tlw" userLabel="Content view">
- <rect key="frame" x="0.0" y="0.0" width="375" height="568"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="558"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wnl-L9-JqG" userLabel="Logo header">
<rect key="frame" x="0.0" y="0.0" width="375" height="100"/>
@@ -935,7 +935,7 @@
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cas-Tk-gcz" customClass="LinkButton" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="20" y="516" width="36" height="32"/>
+ <rect key="frame" x="20" y="516" width="128" height="22"/>
<fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="18"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" title="Privacy Policy" image="IconExtlink"/>
@@ -971,10 +971,10 @@
</constraints>
</scrollView>
<view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="16P-Q0-ZO9" userLabel="Footer">
- <rect key="frame" x="0.0" y="598" width="375" height="69"/>
+ <rect key="frame" x="0.0" y="597" width="375" height="70"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ttw-7B-1MM" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="16" y="24" width="343" height="21"/>
+ <rect key="frame" x="16" y="24" width="343" height="22"/>
<accessibility key="accessibilityConfiguration" identifier="AgreeButton"/>
<state key="normal" title="Agree and continue" backgroundImage="DefaultButton">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -1010,7 +1010,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="87X-aX-U9x" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
- <point key="canvasLocation" x="-1207" y="27"/>
+ <point key="canvasLocation" x="-551" y="27"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="oT4-Ap-qrZ">
@@ -1063,31 +1063,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="48.5"/>
+ <rect key="frame" x="0.0" y="173" width="375" height="43.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="48.5"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.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="16.5" width="16" height="16"/>
+ <rect key="frame" x="16" y="14" 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="26.5"/>
+ <rect key="frame" x="44" y="11" width="42" 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>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="IconTick" translatesAutoresizingMaskIntoConstraints="NO" id="e1o-Bl-zd5">
- <rect key="frame" x="0.0" y="0.5" width="48" height="48"/>
+ <rect key="frame" x="12" y="10" width="24" height="24"/>
<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="48.5"/>
+ <rect key="frame" x="311" y="0.0" width="64" height="43.5"/>
<accessibility key="accessibilityConfiguration" identifier="ExpandButton"/>
<constraints>
<constraint firstAttribute="width" constant="64" id="UU3-Di-65E"/>
@@ -1131,18 +1131,10 @@
</barButtonItem>
</navigationItem>
<connections>
- <outlet property="activityIndicator" destination="Mdw-ey-b8l" id="w7b-yv-Q39"/>
<segue destination="6Lc-ZQ-E4P" kind="unwind" identifier="ReturnToConnectWithNewRelay" unwindAction="unwindFromSelectLocationWithSegue:" id="SPT-Ay-Cy3"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="EvX-LH-gOg" userLabel="First Responder" sceneMemberID="firstResponder"/>
- <view contentMode="scaleToFill" id="Mdw-ey-b8l" customClass="SpinnerActivityIndicatorView" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="0.0" y="0.0" width="48" height="48"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <viewLayoutGuide key="safeArea" id="gNQ-Ao-v6S"/>
- </view>
<exit id="6Lc-ZQ-E4P" userLabel="Exit" sceneMemberID="exit"/>
</objects>
<point key="canvasLocation" x="2649" y="841"/>
diff --git a/ios/MullvadVPN/CancellableDelayPublisher.swift b/ios/MullvadVPN/CancellableDelayPublisher.swift
deleted file mode 100644
index c177bd4251..0000000000
--- a/ios/MullvadVPN/CancellableDelayPublisher.swift
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// CancellableDelayPublisher.swift
-// MullvadVPN
-//
-// Created by pronebird on 02/03/2020.
-// Copyright © 2020 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-import Combine
-
-extension Publisher {
-
- /// This is a `delay` operator implementation that respects cancellation
- func cancellableDelay<S>(for delay: S.SchedulerTimeType.Stride, scheduler: S)
- -> Publishers.FlatMap<PassthroughSubject<Self.Output, Self.Failure>, Self>
- where S: Scheduler
- {
- return self.flatMap { (value) -> PassthroughSubject<Output, Failure> in
- let subject = PassthroughSubject<Output, Failure>()
- let date = scheduler.now.advanced(by: delay)
-
- // `PassthroughSubject` does not emit values, nor completion after cancellation
- scheduler.schedule(after: date) {
- subject.send(value)
- subject.send(completion: .finished)
- }
-
- return subject
- }
- }
-}
diff --git a/ios/MullvadVPN/ConnectionPanelView.swift b/ios/MullvadVPN/ConnectionPanelView.swift
index e9d5da93c3..fbf0ae44d3 100644
--- a/ios/MullvadVPN/ConnectionPanelView.swift
+++ b/ios/MullvadVPN/ConnectionPanelView.swift
@@ -177,7 +177,7 @@ class ConnectionPanelAddressRow: UIView {
}
}
-class ConnectionPanelCollapseButton: UIButton {
+class ConnectionPanelCollapseButton: CustomButton {
enum Style {
case up, down
@@ -200,43 +200,28 @@ class ConnectionPanelCollapseButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
-
commonInit()
- updateButtonImage()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
-
commonInit()
- updateButtonImage()
}
private func commonInit() {
setTitleColor(UIColor.white, for: .normal)
setTitleColor(UIColor.lightGray, for: .highlighted)
setTitleColor(UIColor.lightGray, for: .disabled)
- }
- private func updateButtonImage() {
- setImage(style.image, for: .normal)
- }
+ contentHorizontalAlignment = .leading
+ imageAlignment = .trailing
+ inlineImageSpacing = 0
- override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
- let titleRect = self.titleRect(forContentRect: contentRect)
- var imageRect = super.imageRect(forContentRect: contentRect)
-
- imageRect.origin.x = titleRect.maxX
-
- return imageRect
+ updateButtonImage()
}
- override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
- var titleRect = super.titleRect(forContentRect: contentRect)
-
- titleRect.origin.x = 0
-
- return titleRect
+ private func updateButtonImage() {
+ setImage(style.image, for: .normal)
}
}
diff --git a/ios/MullvadVPN/CustomNavigationBar.swift b/ios/MullvadVPN/CustomNavigationBar.swift
index 2f2072b809..4168d62bb0 100644
--- a/ios/MullvadVPN/CustomNavigationBar.swift
+++ b/ios/MullvadVPN/CustomNavigationBar.swift
@@ -27,6 +27,14 @@ class CustomNavigationBar: UINavigationBar {
margins.left = 24
margins.right = 24
layoutMargins = margins
+
+ if #available(iOS 13, *) {
+ // no-op
+ } else {
+ barTintColor = .secondaryColor
+ shadowImage = UIImage()
+ isTranslucent = false
+ }
}
}
diff --git a/ios/MullvadVPN/MutuallyExclusive.swift b/ios/MullvadVPN/MutuallyExclusive.swift
deleted file mode 100644
index fc0bd254c5..0000000000
--- a/ios/MullvadVPN/MutuallyExclusive.swift
+++ /dev/null
@@ -1,125 +0,0 @@
-//
-// MutuallyExclusive.swift
-// MullvadVPN
-//
-// Created by pronebird on 24/10/2019.
-// Copyright © 2019 Mullvad VPN AB. All rights reserved.
-//
-
-import Combine
-import Foundation
-
-extension Publishers {
-
- /// A publisher that blocks the given DispatchQueue until the produced publisher reported the
- /// completion.
- final class MutuallyExclusive<PublisherType, Context>: Publisher
- where
- PublisherType: Publisher,
- Context: Scheduler
- {
- typealias MakePublisherBlock = () -> PublisherType
-
- typealias Output = PublisherType.Output
- typealias Failure = PublisherType.Failure
-
- private let exclusivityQueue: Context
- private let executionQueue: Context
-
- private let createPublisher: MakePublisherBlock
-
- init(exclusivityQueue: Context, executionQueue: Context, createPublisher: @escaping MakePublisherBlock) {
- self.exclusivityQueue = exclusivityQueue
- self.executionQueue = executionQueue
- self.createPublisher = createPublisher
- }
-
- func receive<S>(subscriber: S) where S : Subscriber, S.Failure == Failure, S.Input == Output {
- let subscription = MutuallyExclusive.Subscription(
- subscriber: subscriber,
- createPublisher: createPublisher,
- exclusivityQueue: exclusivityQueue,
- executionQueue: executionQueue)
-
- subscriber.receive(subscription: subscription)
- }
- }
-}
-
-private extension Publishers.MutuallyExclusive {
-
- /// A subscription used by `MutuallyExclusive` publisher
- final class Subscription<SubscriberType, PublisherType, Context>: Combine.Subscription
- where
- SubscriberType: Subscriber, PublisherType: Publisher,
- PublisherType.Output == SubscriberType.Input,
- PublisherType.Failure == SubscriberType.Failure,
- Context: Scheduler
- {
- typealias MakePublisherBlock = () -> PublisherType
-
- private let subscriber: SubscriberType
- private var innerSubscriber: AnyCancellable?
- private let createPublisher: MakePublisherBlock
-
- private let exclusivityQueue: Context
- private let executionQueue: Context
- private let sema = DispatchSemaphore(value: 0)
-
- private let cancelLock = NSLock()
- private var isCancelled = false
-
- init(subscriber: SubscriberType,
- createPublisher: @escaping MakePublisherBlock,
- exclusivityQueue: Context,
- executionQueue: Context)
- {
- self.subscriber = subscriber
- self.createPublisher = createPublisher
- self.exclusivityQueue = exclusivityQueue
- self.executionQueue = executionQueue
- }
-
- func request(_ demand: Subscribers.Demand) {
- self.exclusivityQueue.schedule {
- self.executionQueue.schedule {
- self.cancelLock.withCriticalBlock {
- guard !self.isCancelled else { return }
-
- self.innerSubscriber = self.createPublisher()
- .sink(receiveCompletion: { [weak self] (completion) in
- guard let self = self else { return }
-
- self.subscriber.receive(completion: completion)
- self.signalSemaphore()
- }, receiveValue: { [weak self] (output) in
- _ = self?.subscriber.receive(output)
- })
- }
- }
- self.sema.wait()
- }
- }
-
- func cancel() {
- cancelLock.withCriticalBlock {
- guard !isCancelled else { return }
-
- isCancelled = true
-
- innerSubscriber?.cancel()
- innerSubscriber = nil
-
- signalSemaphore()
- }
- }
-
- private func signalSemaphore() {
- _ = sema.signal()
- }
-
- }
-
-}
-
-typealias MutuallyExclusive = Publishers.MutuallyExclusive
diff --git a/ios/MullvadVPN/ReplaceNilWithError.swift b/ios/MullvadVPN/ReplaceNilWithError.swift
deleted file mode 100644
index f25a8ff7bc..0000000000
--- a/ios/MullvadVPN/ReplaceNilWithError.swift
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-// ReplaceNilWithError.swift
-// MullvadVPN
-//
-// Created by pronebird on 19/11/2019.
-// Copyright © 2019 Mullvad VPN AB. All rights reserved.
-//
-
-import Combine
-import Foundation
-
-extension Publisher {
-
- /// Replace nil elements with the provided error
- func replaceNil<T>(with error: Failure) -> Publishers.FlatMap<Result<T, Failure>.Publisher, Self>
- where Output == T?, Failure: Error {
- return flatMap { (output: T?) -> Result<T, Failure>.Publisher in
- let result: Result<T, Failure> = output.flatMap { .success($0) } ?? .failure(error)
-
- return result.publisher
- }
- }
-
-}
diff --git a/ios/MullvadVPN/SegueIdentifier.swift b/ios/MullvadVPN/SegueIdentifier.swift
index 85d80ba061..fa7a3cbc33 100644
--- a/ios/MullvadVPN/SegueIdentifier.swift
+++ b/ios/MullvadVPN/SegueIdentifier.swift
@@ -13,10 +13,6 @@ enum SegueIdentifier {}
extension SegueIdentifier {
- enum Root: String, SegueConvertible {
- case showSettings = "ShowSettings"
- }
-
enum Login: String, SegueConvertible {
case showConnect = "ShowConnect"
}
diff --git a/ios/MullvadVPN/SelectLocationController.swift b/ios/MullvadVPN/SelectLocationController.swift
index 3fb22600a1..c1c8b7d938 100644
--- a/ios/MullvadVPN/SelectLocationController.swift
+++ b/ios/MullvadVPN/SelectLocationController.swift
@@ -6,27 +6,32 @@
// Copyright © 2019 Mullvad VPN AB. All rights reserved.
//
-import Combine
+import DiffableDataSources
import UIKit
import os
private let kCellIdentifier = "Cell"
-enum SelectLocationControllerError: Error {
- case loadRelayList(RelayCacheError)
- case getRelayConstraints(TunnelManagerError)
-}
+class SelectLocationController: UITableViewController, RelayCacheObserver {
-class SelectLocationController: UITableViewController {
+ private enum Error: ChainedError {
+ case loadRelayList(RelayCacheError)
+ case getRelayConstraints(TunnelManager.Error)
+
+ var errorDescription: String? {
+ switch self {
+ case .loadRelayList:
+ return "Failure to load a relay list"
+ case .getRelayConstraints:
+ return "Failure to get relay constraints"
+ }
+ }
+ }
- private let relayCache = try! RelayCache.withDefaultLocationAndEphemeralSession().get()
private var relayList: RelayList?
private var relayConstraints: RelayConstraints?
private var expandedItems = [RelayLocation]()
private var dataSource: DataSource?
- private var loadDataSubscriber: AnyCancellable?
-
- @IBOutlet var activityIndicator: SpinnerActivityIndicatorView!
var selectedLocation: RelayLocation?
@@ -58,7 +63,7 @@ class SelectLocationController: UITableViewController {
tableView.dataSource = dataSource
- addActivityIndicatorView()
+ RelayCache.shared.addObserver(self)
loadData()
}
@@ -92,38 +97,62 @@ class SelectLocationController: UITableViewController {
}
}
+ // MARK: - RelayCacheObserver
+
+ func relayCache(_ relayCache: RelayCache, didUpdateCachedRelayList cachedRelayList: CachedRelayList) {
+ self.didReceiveCachedRelays(cachedRelayList: cachedRelayList) { (result) in
+ DispatchQueue.main.async {
+ switch result {
+ case .success(let (cachedRelayList, relayConstraints)):
+ self.didReceive(relayList: cachedRelayList.relayList, relayConstraints: relayConstraints)
+
+ case .failure(let error):
+ error.logChain()
+ }
+ }
+ }
+ }
+
// MARK: - Relay list handling
private func loadData() {
- loadDataSubscriber = relayCache.read()
- .mapError { SelectLocationControllerError.loadRelayList($0) }
- .map { $0.relayList.sorted() }
- .flatMap({ (filteredRelayList) in
- TunnelManager.shared.getRelayConstraints()
- .mapError { SelectLocationControllerError.getRelayConstraints($0) }
- .map { (filteredRelayList, $0) }
- })
- .receive(on: DispatchQueue.main)
- .handleEvents(receiveSubscription: { [weak self] _ in
- self?.activityIndicator.startAnimating()
- }, receiveCompletion: { [weak self] _ in
- self?.activityIndicator.stopAnimating()
- }, receiveCancel: { [weak self] () in
- self?.activityIndicator.stopAnimating()
- })
- .sink(receiveCompletion: { (completion) in
- if case .failure(let error) = completion {
- os_log(.error, "Failed to load the SelectLocation controller: %{public}s", error.localizedDescription)
+ fetchRelays { (result) in
+ DispatchQueue.main.async {
+ switch result {
+ case .success(let (cachedRelayList, relayConstraints)):
+ self.didReceive(relayList: cachedRelayList.relayList, relayConstraints: relayConstraints)
+
+ case .failure(let error):
+ error.logChain()
}
- }) { [weak self] (result) in
- let (relayList, constraints) = result
+ }
+ }
+ }
+
+ private func fetchRelays(completionHandler: @escaping (Result<(CachedRelayList, RelayConstraints), Error>) -> Void) {
+ RelayCache.shared.read { (result) in
+ switch result {
+ case .success(let cachedRelayList):
+ self.didReceiveCachedRelays(cachedRelayList: cachedRelayList, completionHandler: completionHandler)
- self?.didReceive(relayList: relayList, relayConstraints: constraints)
+ case .failure(let error):
+ completionHandler(.failure(.loadRelayList(error)))
}
+ }
+ }
+
+ private func didReceiveCachedRelays(cachedRelayList: CachedRelayList, completionHandler: @escaping (Result<(CachedRelayList, RelayConstraints), Error>) -> Void) {
+ TunnelManager.shared.getRelayConstraints { (result) in
+ let result = result
+ .map { (cachedRelayList, $0) }
+ .mapError { Error.getRelayConstraints($0) }
+
+ completionHandler(result)
+ }
}
private func didReceive(relayList: RelayList, relayConstraints: RelayConstraints) {
- self.relayList = relayList
+ self.relayList = relayList.sorted()
self.relayConstraints = relayConstraints
let relayLocation = relayConstraints.location.value
@@ -214,21 +243,6 @@ class SelectLocationController: UITableViewController {
tableView.tableHeaderView = header
}
}
-
- // MARK: - Activity indicator
-
- private func addActivityIndicatorView() {
- view.addSubview(activityIndicator)
-
- activityIndicator.translatesAutoresizingMaskIntoConstraints = false
-
- NSLayoutConstraint.activate([
- activityIndicator.widthAnchor.constraint(equalToConstant: 48),
- activityIndicator.heightAnchor.constraint(equalToConstant: 48),
- activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
- activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -60)
- ])
- }
}
private extension RelayList {
@@ -311,10 +325,10 @@ private enum DataSourceSection {
}
/// Data source type
-private typealias DataSource = UITableViewDiffableDataSource<DataSourceSection, DataSourceItem>
+private typealias DataSource = TableViewDiffableDataSource<DataSourceSection, DataSourceItem>
/// Data source snapshot type
-private typealias DataSourceSnapshot = NSDiffableDataSourceSnapshot<DataSourceSection, DataSourceItem>
+private typealias DataSourceSnapshot = DiffableDataSourceSnapshot<DataSourceSection, DataSourceItem>
/// A wrapper type for RelayList to be able to represent it as a flat list
private enum DataSourceItem: Hashable {
diff --git a/ios/MullvadVPN/SettingsCell.swift b/ios/MullvadVPN/SettingsCell.swift
index c292ce1fcf..84967b9a1d 100644
--- a/ios/MullvadVPN/SettingsCell.swift
+++ b/ios/MullvadVPN/SettingsCell.swift
@@ -7,12 +7,11 @@
//
import UIKit
-import Combine
class SettingsCell: BasicTableViewCell {
private let preferredMargins = UIEdgeInsets(top: 16, left: 24, bottom: 16, right: 12)
- private var appDidBecomeActiveSubscriber: AnyCancellable?
+ private var appDidBecomeActiveObserver: NSObjectProtocol?
override func awakeFromNib() {
super.awakeFromNib()
@@ -21,6 +20,7 @@ class SettingsCell: BasicTableViewCell {
selectedBackgroundView?.backgroundColor = UIColor.Cell.selectedAltBackgroundColor
contentView.layoutMargins = preferredMargins
+ separatorInset = .zero
enableDisclosureViewTintColorFix()
}
@@ -28,9 +28,10 @@ class SettingsCell: BasicTableViewCell {
/// `UITableViewCell` resets the disclosure view image when the app goes in background
/// This fix ensures that the image is tinted when the app becomes active again.
private func enableDisclosureViewTintColorFix() {
- appDidBecomeActiveSubscriber = NotificationCenter.default
- .publisher(for: UIApplication.didBecomeActiveNotification, object: nil)
- .sink { [weak self] (_) in
+ appDidBecomeActiveObserver = NotificationCenter.default.addObserver(
+ forName: UIApplication.didBecomeActiveNotification,
+ object: nil,
+ queue: nil) { [weak self] (note) in
self?.updateDisclosureViewTintColor()
}
diff --git a/ios/MullvadVPN/SettingsViewController.swift b/ios/MullvadVPN/SettingsViewController.swift
index 5d66a46f0e..4fd4f82a8a 100644
--- a/ios/MullvadVPN/SettingsViewController.swift
+++ b/ios/MullvadVPN/SettingsViewController.swift
@@ -6,7 +6,6 @@
// Copyright © 2019 Mullvad VPN AB. All rights reserved.
//
-import Combine
import Foundation
import UIKit
@@ -25,18 +24,18 @@ class SettingsViewController: UITableViewController {
}
private weak var accountRow: StaticTableViewRow?
- private var accountExpirySubscriber: AnyCancellable?
+ private var accountExpiryObserver: NSObjectProtocol?
override func viewDidLoad() {
super.viewDidLoad()
- accountExpirySubscriber = NotificationCenter.default
- .publisher(for: Account.didUpdateAccountExpiryNotification, object: Account.shared)
- .receive(on: DispatchQueue.main)
- .sink { [weak self] (notification) in
+ accountExpiryObserver = NotificationCenter.default.addObserver(
+ forName: Account.didUpdateAccountExpiryNotification,
+ object: Account.shared,
+ queue: OperationQueue.main) { [weak self] (note) in
guard let accountRow = self?.accountRow else { return }
- self?.staticDataSource.reloadRows([accountRow], with: .automatic)
+ self?.staticDataSource.reloadRows([accountRow], with: .none)
}
setupDataSource()