diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2020-07-15 18:51:48 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2020-07-15 18:51:48 +0200 |
| commit | d68ce4e2c6fc2a458405b50a13b1dd8ded120de5 (patch) | |
| tree | 9d654e401852bfe9a40df17a0dd11688f690b8ad | |
| parent | 5afa504637d787ca587f9dbd987a5d6f8093a5ac (diff) | |
| parent | 6cbaef80e9c57808e9ef0746edf78f5718a9dacd (diff) | |
| download | mullvadvpn-d68ce4e2c6fc2a458405b50a13b1dd8ded120de5.tar.xz mullvadvpn-d68ce4e2c6fc2a458405b50a13b1dd8ded120de5.zip | |
Merge branch 'ios12-support'
| -rw-r--r-- | ios/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 62 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata | 2 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 25 | ||||
| -rw-r--r-- | ios/MullvadVPN/AccountViewController.swift | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/AppButton.swift | 15 | ||||
| -rw-r--r-- | ios/MullvadVPN/AutoDisposableSink.swift | 67 | ||||
| -rw-r--r-- | ios/MullvadVPN/Base.lproj/Main.storyboard | 46 | ||||
| -rw-r--r-- | ios/MullvadVPN/CancellableDelayPublisher.swift | 32 | ||||
| -rw-r--r-- | ios/MullvadVPN/ConnectionPanelView.swift | 29 | ||||
| -rw-r--r-- | ios/MullvadVPN/CustomNavigationBar.swift | 8 | ||||
| -rw-r--r-- | ios/MullvadVPN/MutuallyExclusive.swift | 125 | ||||
| -rw-r--r-- | ios/MullvadVPN/ReplaceNilWithError.swift | 24 | ||||
| -rw-r--r-- | ios/MullvadVPN/SegueIdentifier.swift | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/SelectLocationController.swift | 116 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsCell.swift | 11 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsViewController.swift | 13 |
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() |
