From a96d4c17c9c65377a0a159133b074220a5714a54 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Fri, 11 Feb 2022 15:15:41 +0100 Subject: [PATCH 001/159] Adds the functionality of in-app message for user survey and point to new commit sha on PIALibary module --- PIA VPN/AppConstants.swift | 6 +++- PIA VPN/AppPreferences.swift | 52 +++++++++++++++++++++++++-- PIA VPN/DashboardViewController.swift | 6 ++++ PIA VPN/MessagesManager.swift | 12 ++++++- PIA VPN/SwiftGen+Strings.swift | 7 ++++ PIA VPN/en.lproj/Localizable.strings | 2 ++ Podfile | 2 +- Podfile.lock | 14 ++++---- 8 files changed, 89 insertions(+), 12 deletions(-) diff --git a/PIA VPN/AppConstants.swift b/PIA VPN/AppConstants.swift index 3432a5407..b2d94f1e2 100644 --- a/PIA VPN/AppConstants.swift +++ b/PIA VPN/AppConstants.swift @@ -191,5 +191,9 @@ struct AppConstants { static let connect = "piavpn:connect" static let view = "piavpn:view" } - + + struct Survey { + static let numberOfConnections = 15 + static let formURL = URL(string: "https://privateinternetaccess.typeform.com/to/WTFcN77r")! + } } diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 9388046b4..fdef47cb7 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -102,7 +102,10 @@ class AppPreferences { // Dev static let appEnvironmentIsProduction = "AppEnvironmentIsProduction" static let stagingVersion = "StagingVersion" - + + // Survey Settings + static let consecutiveConnections = "consecutiveConnections" + static let showUserSurvey = "showUserSurvey" } static let shared = AppPreferences() @@ -518,7 +521,29 @@ class AppPreferences { defaults.set(newValue, forKey: Entries.stagingVersion) } } - + + private var consecutiveConnections: Int { + get { + return defaults.integer(forKey: Entries.consecutiveConnections) + } + set { + defaults.set(newValue, forKey: Entries.consecutiveConnections) + } + } + + private var showUserSurvey: Bool? { + get { + if let showSurvey = defaults.value(forKey: Entries.showUserSurvey) as? Bool { + return showSurvey + } else { + return nil + } + } + set { + defaults.set(newValue, forKey: Entries.showUserSurvey) + } + } + private init() { guard let defaults = UserDefaults(suiteName: AppConstants.appGroup) else { fatalError("Unable to initialize app preferences") @@ -557,6 +582,7 @@ class AppPreferences { Entries.checksDipExpirationRequest: true, Entries.stagingVersion: 0, Entries.appEnvironmentIsProduction: Client.environment == .production ? true : false, + Entries.consecutiveConnections: 0, ]) migrateDIP() @@ -783,6 +809,8 @@ class AppPreferences { dedicatedTokenIPReleation = [:] appEnvironmentIsProduction = Client.environment == .production ? true : false MessagesManager.shared.reset() + consecutiveConnections = 0 + showUserSurvey = nil } func clean() { @@ -816,6 +844,8 @@ class AppPreferences { dedicatedTokenIPReleation = [:] MessagesManager.shared.reset() appEnvironmentIsProduction = Client.environment == .production ? true : false + consecutiveConnections = 0 + showUserSurvey = nil } // + (void)eraseForTesting; @@ -858,4 +888,22 @@ class AppPreferences { } } } + + // MARK: Survey Settings + func connectionEstablished() { + consecutiveConnections = consecutiveConnections + 1 + if consecutiveConnections > AppConstants.Survey.numberOfConnections { + showUserSurvey = false + } else { + showUserSurvey = true + } + } + + func showTakeASurveryMessage() -> Bool { + guard let showUserSurvey = showUserSurvey else { + return false + } + return (showUserSurvey && consecutiveConnections >= AppConstants.Survey.numberOfConnections) ? true : false + } + } diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 75d0503bb..1e1ade9ee 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -403,6 +403,12 @@ class DashboardViewController: AutolayoutViewController { Client.configuration.connectedManually = true guard let weakSelf = self else { return } + + AppPreferences.shared.connectionEstablished() + if AppPreferences.shared.showTakeASurveryMessage() { + MessagesManager.shared.showInAppSurveyMessage() + } + if let _ = error { RatingManager.shared.logError() } diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 7a913bcf5..5092b96fe 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -126,7 +126,7 @@ extension InAppMessage { } command?.execute() - + callbackDidFinishExecution?() } func localisedMessage() -> String { @@ -199,4 +199,14 @@ extension MessagesManager { AppPreferences.shared.dedicatedTokenIPReleation[token] = ip } } + + + func showInAppSurveyMessage() { + let messageID = "take-the-survey-message-banner" + let message = InAppMessage(withMessage: ["en-US": L10n.Account.Survey.message], id: messageID, link: ["en-US": L10n.Account.Survey.messageLink], type: .link, level: .api, actions: nil, view: nil, uri: AppConstants.Survey.formURL.absoluteString) { [weak self] in + guard let weakSelf = self else { return } + weakSelf.dismiss(message: messageID) + } + MessagesManager.shared.postSystemMessage(message: message) + } } diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index 85d15c45b..cffcf86a1 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -111,6 +111,13 @@ internal enum L10n { internal static let message = L10n.tr("Localizable", "account.subscriptions.short.message") } } + internal enum Survey { + /// Want to help make PIA better? Let us know how we can improve! + /// Take The Survey ⟩ + internal static let message = L10n.tr("Localizable", "account.survey.message") + /// Take The Survey ⟩ + internal static let messageLink = L10n.tr("Localizable", "account.survey.messageLink") + } internal enum Update { internal enum Email { internal enum Require { diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index 66aa92638..74e455838 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -113,6 +113,8 @@ "account.delete.alert.title" = "Are you sure?"; "account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; "account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey ⟩"; +"account.survey.messageLink" = "Take The Survey ⟩"; // SETTINGS diff --git a/Podfile b/Podfile index 3b145c576..cdd625059 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('6d34ee34') + library_by_gitlab_by_git('b52761c3') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 4080a35a3..463002d9b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, commit `31186b1d`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :commit: 31186b1d :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 6d34ee34 + :commit: b52761c3 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 31186b1d :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 6d34ee34 + :commit: b52761c3 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 5b7ee3e88860d684f51498a697fb96c685f63397 +PODFILE CHECKSUM: a9e17a33b25182bf0b331985c9f2dc40dd614dff COCOAPODS: 1.11.2 From ae747762389c85fba8f503b37f953d32d87003b5 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Fri, 11 Feb 2022 16:19:08 +0100 Subject: [PATCH 002/159] Corrects a module rename and points to new commit --- PIA VPN/MessagesManager.swift | 2 +- Podfile | 2 +- Podfile.lock | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 5092b96fe..e0bfe74e6 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -126,7 +126,7 @@ extension InAppMessage { } command?.execute() - callbackDidFinishExecution?() + executionCompletionHandler?() } func localisedMessage() -> String { diff --git a/Podfile b/Podfile index cdd625059..90d9e05b6 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('b52761c3') + library_by_gitlab_by_git('4c13f1e6') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 463002d9b..01331a0df 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, commit `31186b1d`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b52761c3`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :commit: 31186b1d :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: b52761c3 + :commit: 4c13f1e6 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 31186b1d :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: b52761c3 + :commit: 4c13f1e6 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: a9e17a33b25182bf0b331985c9f2dc40dd614dff +PODFILE CHECKSUM: 82a23ff5a151e40eb7534ae4b69056482ad20542 COCOAPODS: 1.11.2 From 174b013c244f321235466c025c5bb3cab19de6eb Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Fri, 11 Feb 2022 16:54:51 +0100 Subject: [PATCH 003/159] Code cleanup on weak self. --- PIA VPN/MessagesManager.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index e0bfe74e6..31ecab17f 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -204,8 +204,7 @@ extension MessagesManager { func showInAppSurveyMessage() { let messageID = "take-the-survey-message-banner" let message = InAppMessage(withMessage: ["en-US": L10n.Account.Survey.message], id: messageID, link: ["en-US": L10n.Account.Survey.messageLink], type: .link, level: .api, actions: nil, view: nil, uri: AppConstants.Survey.formURL.absoluteString) { [weak self] in - guard let weakSelf = self else { return } - weakSelf.dismiss(message: messageID) + self?.dismiss(message: messageID) } MessagesManager.shared.postSystemMessage(message: message) } From 1e521df79e7a5d280080da41b3e922784040b2b7 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 14 Feb 2022 11:09:31 +0100 Subject: [PATCH 004/159] Refactors the survey control logic into a UserSurveyManager. This also changes responsibility of incrementing successConnections and moves it to AppPreferences. Small refactoring of RatingManager. --- PIA VPN.xcodeproj/project.pbxproj | 6 ++++ PIA VPN/AppConstants.swift | 2 +- PIA VPN/AppPreferences.swift | 50 +++------------------------ PIA VPN/Bootstrapper.swift | 4 ++- PIA VPN/DashboardViewController.swift | 6 ---- PIA VPN/RatingManager.swift | 3 +- PIA VPN/UserSurveyManager.swift | 28 +++++++++++++++ 7 files changed, 44 insertions(+), 55 deletions(-) create mode 100644 PIA VPN/UserSurveyManager.swift diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 1e43708e9..b1b58050b 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -149,6 +149,8 @@ 3545E98426AADC7E00B812CC /* ServerSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E97F26AAD60C00B812CC /* ServerSelectionDelegate.swift */; }; 3732E0F0C64DA8513903087D /* Pods_PIA_VPN_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02823EF3398B7E2C32FA7544 /* Pods_PIA_VPN_Tunnel.framework */; }; 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */; }; + 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; + 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; 821674F22678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; 82183D7B2500FD460033023F /* String+Substrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82183D7A2500FD460033023F /* String+Substrings.swift */; }; @@ -615,6 +617,7 @@ 59EC919445EA680B691ACC88 /* Pods-PIA VPNTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.debug.xcconfig"; sourceTree = ""; }; 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.release.xcconfig"; sourceTree = ""; }; 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_dev.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; 82183D7A2500FD460033023F /* String+Substrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Substrings.swift"; sourceTree = ""; }; @@ -958,6 +961,7 @@ DD51F8C32372E4C8009FEED3 /* PemUtil.swift */, DD522D21237D74380072F555 /* BooleanUtil.swift */, DD918575246BFA7F006B3A2B /* RatingManager.swift */, + 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */, DDD65312247E66AD00F0A897 /* CoordinatesFinder.swift */, 829EB51B2535A8FF003E74DD /* CollectionViewAutoSizeUtils.swift */, 3524670A26B431B800E3F0AC /* TrustedNetworkHelper.swift */, @@ -2148,6 +2152,7 @@ 0EFDC1E11FE4A450007C0B9B /* AppPreferences.swift in Sources */, 0E441E272055AEDF007528D5 /* ThemeStrategy+App.swift in Sources */, DDC8125021761B0B00CB290C /* SwiftGen+ScenesStoryboards.swift in Sources */, + 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */, 0E492C681FE60907007F23DF /* Flags.swift in Sources */, DD172A972254C35000071CFB /* FavoriteServersTile.swift in Sources */, 0EE14D191FF15812008D9AC2 /* ModalNavigationSegue.swift in Sources */, @@ -2234,6 +2239,7 @@ 82A1AD2F24B87CF20003DD02 /* PIACard.swift in Sources */, 8272C6392657F54400D846A8 /* DevelopmentSettingsViewController.swift in Sources */, 0EFB839020209CF200980F69 /* VPNPermissionViewController.swift in Sources */, + 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */, 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */, 0EFDC1D71FE46177007C0B9B /* SensitiveOperation.swift in Sources */, 82C0071325231F2800F21AF2 /* String+VPNType.swift in Sources */, diff --git a/PIA VPN/AppConstants.swift b/PIA VPN/AppConstants.swift index b2d94f1e2..49a940b15 100644 --- a/PIA VPN/AppConstants.swift +++ b/PIA VPN/AppConstants.swift @@ -193,7 +193,7 @@ struct AppConstants { } struct Survey { - static let numberOfConnections = 15 + static let numberOfConnectionsUntilPrompt = 15 static let formURL = URL(string: "https://privateinternetaccess.typeform.com/to/WTFcN77r")! } } diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index fdef47cb7..663c1dec7 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -103,9 +103,6 @@ class AppPreferences { static let appEnvironmentIsProduction = "AppEnvironmentIsProduction" static let stagingVersion = "StagingVersion" - // Survey Settings - static let consecutiveConnections = "consecutiveConnections" - static let showUserSurvey = "showUserSurvey" } static let shared = AppPreferences() @@ -522,28 +519,6 @@ class AppPreferences { } } - private var consecutiveConnections: Int { - get { - return defaults.integer(forKey: Entries.consecutiveConnections) - } - set { - defaults.set(newValue, forKey: Entries.consecutiveConnections) - } - } - - private var showUserSurvey: Bool? { - get { - if let showSurvey = defaults.value(forKey: Entries.showUserSurvey) as? Bool { - return showSurvey - } else { - return nil - } - } - set { - defaults.set(newValue, forKey: Entries.showUserSurvey) - } - } - private init() { guard let defaults = UserDefaults(suiteName: AppConstants.appGroup) else { fatalError("Unable to initialize app preferences") @@ -582,7 +557,6 @@ class AppPreferences { Entries.checksDipExpirationRequest: true, Entries.stagingVersion: 0, Entries.appEnvironmentIsProduction: Client.environment == .production ? true : false, - Entries.consecutiveConnections: 0, ]) migrateDIP() @@ -803,14 +777,13 @@ class AppPreferences { todayWidgetVpnSocket = "UDP" todayWidgetTrustedNetwork = false Client.resetServers(completionBlock: {_ in }) + successConnections = 0 failureConnections = 0 showGeoServers = true stopInAppMessages = false dedicatedTokenIPReleation = [:] appEnvironmentIsProduction = Client.environment == .production ? true : false MessagesManager.shared.reset() - consecutiveConnections = 0 - showUserSurvey = nil } func clean() { @@ -837,6 +810,7 @@ class AppPreferences { let preferences = Client.preferences.editable().reset() preferences.commit() Client.resetServers(completionBlock: {_ in }) + successConnections = 0 failureConnections = 0 showGeoServers = true stopInAppMessages = false @@ -844,8 +818,6 @@ class AppPreferences { dedicatedTokenIPReleation = [:] MessagesManager.shared.reset() appEnvironmentIsProduction = Client.environment == .production ? true : false - consecutiveConnections = 0 - showUserSurvey = nil } // + (void)eraseForTesting; @@ -889,21 +861,9 @@ class AppPreferences { } } - // MARK: Survey Settings - func connectionEstablished() { - consecutiveConnections = consecutiveConnections + 1 - if consecutiveConnections > AppConstants.Survey.numberOfConnections { - showUserSurvey = false - } else { - showUserSurvey = true - } - } - - func showTakeASurveryMessage() -> Bool { - guard let showUserSurvey = showUserSurvey else { - return false - } - return (showUserSurvey && consecutiveConnections >= AppConstants.Survey.numberOfConnections) ? true : false + // MARK: Connections + func incrementSuccessConnections() { + self.successConnections += 1 } } diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index a1243066a..ab466efa0 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -259,7 +259,9 @@ class Bootstrapper { guard (Client.providers.vpnProvider.vpnStatus == .connected) else { return } - RatingManager.shared.logSuccessConnection() + AppPreferences.shared.incrementSuccessConnections() + RatingManager.shared.showRating() + UserSurveyManager.shared.showUserSurveyIfPossible() } @objc private func internetReachable(notification: Notification) { diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 1e1ade9ee..75d0503bb 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -403,12 +403,6 @@ class DashboardViewController: AutolayoutViewController { Client.configuration.connectedManually = true guard let weakSelf = self else { return } - - AppPreferences.shared.connectionEstablished() - if AppPreferences.shared.showTakeASurveryMessage() { - MessagesManager.shared.showInAppSurveyMessage() - } - if let _ = error { RatingManager.shared.logError() } diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 5546985db..8923f36b0 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -45,9 +45,8 @@ class RatingManager { self.timeIntervalUntilPromptAgain = AppConfiguration.Rating.timeIntervalUntilPromptAgain } - func logSuccessConnection() { + func showRating() { - AppPreferences.shared.successConnections += 1 if AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt { log.debug("Show rating") reviewApp() diff --git a/PIA VPN/UserSurveyManager.swift b/PIA VPN/UserSurveyManager.swift new file mode 100644 index 000000000..a093a4c9f --- /dev/null +++ b/PIA VPN/UserSurveyManager.swift @@ -0,0 +1,28 @@ +// +// UserSurveyManager.swift +// PIA VPN +// +// Created by Waleed Mahmood on 11.02.22. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation + +class UserSurveyManager { + static let shared = UserSurveyManager() + + private init() { + + } + + func showUserSurveyIfPossible() { + if shouldShowSurveryMessage() { + MessagesManager.shared.showInAppSurveyMessage() + } + } + + // MARK: Survey Settings + private func shouldShowSurveryMessage() -> Bool { + return AppPreferences.shared.successConnections == AppConstants.Survey.numberOfConnectionsUntilPrompt ? true : false + } +} From b29d71ef179a19ec9b58c84be13c34f138fac62e Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Feb 2022 11:29:01 +0100 Subject: [PATCH 005/159] Update for KPI module and new commit sha for client-library-apple. --- Podfile | 4 ++-- Podfile.lock | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Podfile b/Podfile index 3b145c576..541052695 100644 --- a/Podfile +++ b/Podfile @@ -76,12 +76,12 @@ def shared_main_pods pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.1' #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :commit => 'b62d1bab' - pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :commit => '31186b1d' + pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('6d34ee34') + library_by_gitlab_by_git('70a3c462') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 4080a35a3..f768b90f4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -75,11 +75,11 @@ PODS: - PIACSIModule/gradle (= 1.0.1) - PIACSIModule/csi (1.0.1) - PIACSIModule/gradle (1.0.1) - - PIAKPIModule (1.0.1): - - PIAKPIModule/gradle (= 1.0.1) - - PIAKPIModule/kpi (= 1.0.1) - - PIAKPIModule/gradle (1.0.1) - - PIAKPIModule/kpi (1.0.1) + - PIAKPIModule (1.1.0): + - PIAKPIModule/gradle (= 1.1.0) + - PIAKPIModule/kpi (= 1.1.0) + - PIAKPIModule/gradle (1.1.0) + - PIAKPIModule/kpi (1.1.0) - PIALibrary/Core (2.14.0): - PIAAccountModule - PIALibrary/Library (2.14.0): @@ -159,11 +159,11 @@ DEPENDENCIES: - OpenSSL-Apple (from `https://github.com/keeshux/openssl-apple`) - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, commit `31186b1d`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `6d34ee34`)" + - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -214,10 +214,10 @@ EXTERNAL SOURCES: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :commit: 31186b1d + :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 6d34ee34 + :commit: 70a3c462 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -240,10 +240,10 @@ CHECKOUT OPTIONS: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :commit: 31186b1d + :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 6d34ee34 + :commit: 70a3c462 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -277,7 +277,7 @@ SPEC CHECKSUMS: OpenSSL-Apple: bb7c9715a259404de040f5359ed3b3170cedf8d6 PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea - PIAKPIModule: ec04bedfc7e6eccc7dfe4bc630a99c104ffbb7ea + PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d PIALibrary: d52e06ca2995dd5692dcadb678475acbb0000cd2 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 5b7ee3e88860d684f51498a697fb96c685f63397 +PODFILE CHECKSUM: e05eadad7dd5b54da2538b8cc1254116f39daa7b COCOAPODS: 1.11.2 From be16060cd391f2ed58bdcd57a603ac3225447d07 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Feb 2022 18:20:32 +0100 Subject: [PATCH 006/159] Refactors showRating method --- PIA VPN/Bootstrapper.swift | 3 ++- PIA VPN/RatingManager.swift | 28 +++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index ab466efa0..ef7588f15 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -260,7 +260,8 @@ class Bootstrapper { return } AppPreferences.shared.incrementSuccessConnections() - RatingManager.shared.showRating() + let ratingManager = RatingManager.shared + ratingManager.showRating(of: ratingManager.ratingType()) UserSurveyManager.shared.showUserSurveyIfPossible() } diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 8923f36b0..28dcf48b8 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -37,7 +37,13 @@ class RatingManager { private var successConnectionsUntilPromptAgain: Int private var timeIntervalUntilPromptAgain: Double private var errorInConnectionsUntilPrompt: Int - + + enum RatingType { + case promopt + case withoutPrompt + case none + } + init() { self.successConnectionsUntilPrompt = AppConfiguration.Rating.successConnectionsUntilPrompt self.successConnectionsUntilPromptAgain = AppConfiguration.Rating.successConnectionsUntilPromptAgain @@ -45,13 +51,23 @@ class RatingManager { self.timeIntervalUntilPromptAgain = AppConfiguration.Rating.timeIntervalUntilPromptAgain } - func showRating() { - + func ratingType() -> RatingType { if AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt { + return .promopt + } else if AppPreferences.shared.canAskAgainForReview { + return .withoutPrompt + } else { + return .none + } + } + + func showRating(of type: RatingType) { + + switch type { + case .promopt: log.debug("Show rating") reviewApp() - } else if AppPreferences.shared.canAskAgainForReview { - + case .withoutPrompt: let now = Date() if AppPreferences.shared.successConnections >= self.successConnectionsUntilPromptAgain, @@ -60,6 +76,8 @@ class RatingManager { log.debug("Show rating") reviewAppWithoutPrompt() } + case .none: + log.debug("No rating to be shown") } } From ebe29a2e08d6328ccf45bc12755d248c6a2e6c42 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Feb 2022 19:22:40 +0100 Subject: [PATCH 007/159] Fixes the > issue with take a survey CTA text --- PIA VPN.xcodeproj/project.pbxproj | 6 ++++++ PIA VPN/MessagesManager.swift | 2 +- PIA VPN/String+AddDetailSymbol.swift | 17 +++++++++++++++++ PIA VPN/SwiftGen+Strings.swift | 19 +++++++++++++------ PIA VPN/en.lproj/Localizable.strings | 4 ++-- 5 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 PIA VPN/String+AddDetailSymbol.swift diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index b1b58050b..3752a3b19 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -151,6 +151,8 @@ 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */; }; 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; + 7ECBB8E027BC229600C0C774 /* String+AddDetailSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */; }; + 7ECBB8E127BC247A00C0C774 /* String+AddDetailSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; 821674F22678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; 82183D7B2500FD460033023F /* String+Substrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82183D7A2500FD460033023F /* String+Substrings.swift */; }; @@ -618,6 +620,7 @@ 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.release.xcconfig"; sourceTree = ""; }; 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_dev.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; + 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+AddDetailSymbol.swift"; sourceTree = ""; }; 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; 82183D7A2500FD460033023F /* String+Substrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Substrings.swift"; sourceTree = ""; }; @@ -934,6 +937,7 @@ DDE432DE2498EADA0095B197 /* Array+Group.swift */, 82183D7A2500FD460033023F /* String+Substrings.swift */, 82C0071225231F2800F21AF2 /* String+VPNType.swift */, + 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */, ); name = Extensions; sourceTree = ""; @@ -2113,6 +2117,7 @@ 827570D924DAB6E4008F9800 /* NetworkFooterCollectionViewCell.swift in Sources */, 0EFDC1C71FE35B78007C0B9B /* Macros+App.swift in Sources */, 0ECC1E421FDB45100039891D /* AboutViewController.swift in Sources */, + 7ECBB8E127BC247A00C0C774 /* String+AddDetailSymbol.swift in Sources */, 82C9F3CD25C43863005039F9 /* ActiveDedicatedIpHeaderViewCell.swift in Sources */, DD125DCD21E772B6004ECCB6 /* QuickConnectTileCollectionViewCell.swift in Sources */, 0E8DCA06204D94E800B086DE /* ContentBlockerViewController.swift in Sources */, @@ -2242,6 +2247,7 @@ 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */, 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */, 0EFDC1D71FE46177007C0B9B /* SensitiveOperation.swift in Sources */, + 7ECBB8E027BC229600C0C774 /* String+AddDetailSymbol.swift in Sources */, 82C0071325231F2800F21AF2 /* String+VPNType.swift in Sources */, 826BE8EE253861BE002339F3 /* DedicatedRegionCell.swift in Sources */, 0E3A35281FD9A960000B0F99 /* DashboardViewController.swift in Sources */, diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 31ecab17f..48a29b61f 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -203,7 +203,7 @@ extension MessagesManager { func showInAppSurveyMessage() { let messageID = "take-the-survey-message-banner" - let message = InAppMessage(withMessage: ["en-US": L10n.Account.Survey.message], id: messageID, link: ["en-US": L10n.Account.Survey.messageLink], type: .link, level: .api, actions: nil, view: nil, uri: AppConstants.Survey.formURL.absoluteString) { [weak self] in + let message = InAppMessage(withMessage: ["en-US": L10n.Account.Survey.message.appendDetailSymbol()], id: messageID, link: ["en-US": L10n.Account.Survey.messageLink.appendDetailSymbol()], type: .link, level: .api, actions: nil, view: nil, uri: AppConstants.Survey.formURL.absoluteString) { [weak self] in self?.dismiss(message: messageID) } MessagesManager.shared.postSystemMessage(message: message) diff --git a/PIA VPN/String+AddDetailSymbol.swift b/PIA VPN/String+AddDetailSymbol.swift new file mode 100644 index 000000000..465e3fc92 --- /dev/null +++ b/PIA VPN/String+AddDetailSymbol.swift @@ -0,0 +1,17 @@ +// +// String+AddDetailSymbol.swift +// PIA VPN +// +// Created by Waleed Mahmood on 15.02.22. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import UIKit + +public extension String { + func appendDetailSymbol() -> String { + let symbol = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft ? "⟨ " : " ⟩" + return "\(self)\(symbol)" + } +} diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index cffcf86a1..f7a1c6739 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -113,9 +113,9 @@ internal enum L10n { } internal enum Survey { /// Want to help make PIA better? Let us know how we can improve! - /// Take The Survey ⟩ + /// Take The Survey internal static let message = L10n.tr("Localizable", "account.survey.message") - /// Take The Survey ⟩ + /// Take The Survey internal static let messageLink = L10n.tr("Localizable", "account.survey.messageLink") } internal enum Update { @@ -187,7 +187,9 @@ internal enum L10n { } internal enum ContentBlocker { internal enum Intro { - /// This version replaces MACE with our Safari Content Blocker.\n\nCheck it out in the 'Settings' section. + /// This version replaces MACE with our Safari Content Blocker. + /// + /// Check it out in the 'Settings' section. internal static let message = L10n.tr("Localizable", "dashboard.content_blocker.intro.message") } } @@ -409,7 +411,9 @@ internal enum L10n { } internal enum Collect { internal enum Data { - /// E-mail Address for the purposes of account management and protection from abuse.\n\nE-mail address is used to send subscription information, payment confirmations, customer correspondence, and Private Internet Access promotional offers only. + /// E-mail Address for the purposes of account management and protection from abuse. + /// + /// E-mail address is used to send subscription information, payment confirmations, customer correspondence, and Private Internet Access promotional offers only. internal static let description = L10n.tr("Localizable", "gdpr.collect.data.description") /// Personal information we collect internal static let title = L10n.tr("Localizable", "gdpr.collect.data.title") @@ -766,7 +770,9 @@ internal enum L10n { internal static let title = L10n.tr("Localizable", "settings.application_information.debug.failure.title") } internal enum Success { - /// Debug information successfully submitted.\nID: %@\nPlease note this ID, as our support team will require this to locate your submission. + /// Debug information successfully submitted. + /// ID: %@ + /// Please note this ID, as our support team will require this to locate your submission. internal static func message(_ p1: Any) -> String { return L10n.tr("Localizable", "settings.application_information.debug.success.message", String(describing: p1)) } @@ -1231,7 +1237,8 @@ internal enum L10n { internal enum Body { /// We don’t monitor, filter or log any network activity. internal static let footer = L10n.tr("Localizable", "vpn_permission.body.footer") - /// You’ll see a prompt for PIA VPN and need to allow access to VPN configurations.\nTo proceed tap on “%@”. + /// You’ll see a prompt for PIA VPN and need to allow access to VPN configurations. + /// To proceed tap on “%@”. internal static func subtitle(_ p1: Any) -> String { return L10n.tr("Localizable", "vpn_permission.body.subtitle", String(describing: p1)) } diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index 74e455838..f145f2b91 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -113,8 +113,8 @@ "account.delete.alert.title" = "Are you sure?"; "account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; "account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; -"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey ⟩"; -"account.survey.messageLink" = "Take The Survey ⟩"; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS From 5c23c4afb863675e7b65a04831358fd835a8cc43 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Feb 2022 20:01:33 +0100 Subject: [PATCH 008/159] Code refactor for handling connection success on rating and survey managers --- PIA VPN/Bootstrapper.swift | 5 ++--- PIA VPN/DashboardViewController.swift | 4 ++-- PIA VPN/RatingManager.swift | 27 ++++----------------------- PIA VPN/UserSurveyManager.swift | 2 +- 4 files changed, 9 insertions(+), 29 deletions(-) diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index ef7588f15..a76e6337a 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -260,9 +260,8 @@ class Bootstrapper { return } AppPreferences.shared.incrementSuccessConnections() - let ratingManager = RatingManager.shared - ratingManager.showRating(of: ratingManager.ratingType()) - UserSurveyManager.shared.showUserSurveyIfPossible() + RatingManager.shared.handleConnectionSuccess() + UserSurveyManager.shared.handleConnectionSuccess() } @objc private func internetReachable(notification: Notification) { diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 75d0503bb..a66a775d1 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -404,7 +404,7 @@ class DashboardViewController: AutolayoutViewController { guard let weakSelf = self else { return } if let _ = error { - RatingManager.shared.logError() + RatingManager.shared.handleConnectionError() } let preferences = Client.preferences.editable() @@ -601,7 +601,7 @@ class DashboardViewController: AutolayoutViewController { if !isDisconnecting { isDisconnecting = true Client.providers.vpnProvider.disconnect { _ in - RatingManager.shared.logError() + RatingManager.shared.handleConnectionError() self.isDisconnecting = false self.connectingStatus = .none } diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 28dcf48b8..77707b317 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -38,12 +38,6 @@ class RatingManager { private var timeIntervalUntilPromptAgain: Double private var errorInConnectionsUntilPrompt: Int - enum RatingType { - case promopt - case withoutPrompt - case none - } - init() { self.successConnectionsUntilPrompt = AppConfiguration.Rating.successConnectionsUntilPrompt self.successConnectionsUntilPromptAgain = AppConfiguration.Rating.successConnectionsUntilPromptAgain @@ -51,23 +45,12 @@ class RatingManager { self.timeIntervalUntilPromptAgain = AppConfiguration.Rating.timeIntervalUntilPromptAgain } - func ratingType() -> RatingType { - if AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt { - return .promopt - } else if AppPreferences.shared.canAskAgainForReview { - return .withoutPrompt - } else { - return .none - } - } - - func showRating(of type: RatingType) { + func handleConnectionSuccess() { - switch type { - case .promopt: + if AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt { log.debug("Show rating") reviewApp() - case .withoutPrompt: + } else if AppPreferences.shared.canAskAgainForReview { let now = Date() if AppPreferences.shared.successConnections >= self.successConnectionsUntilPromptAgain, @@ -76,13 +59,11 @@ class RatingManager { log.debug("Show rating") reviewAppWithoutPrompt() } - case .none: - log.debug("No rating to be shown") } } - func logError() { + func handleConnectionError() { if Client.daemons.isNetworkReachable { if AppPreferences.shared.failureConnections == self.errorInConnectionsUntilPrompt { askForConnectionIssuesFeedback() diff --git a/PIA VPN/UserSurveyManager.swift b/PIA VPN/UserSurveyManager.swift index a093a4c9f..b4d5c888e 100644 --- a/PIA VPN/UserSurveyManager.swift +++ b/PIA VPN/UserSurveyManager.swift @@ -15,7 +15,7 @@ class UserSurveyManager { } - func showUserSurveyIfPossible() { + func handleConnectionSuccess() { if shouldShowSurveryMessage() { MessagesManager.shared.showInAppSurveyMessage() } From da236fb755cbddf23c4b572f4462b3b603aa10c0 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Feb 2022 20:06:58 +0100 Subject: [PATCH 009/159] Revert to accidentally committing swiftgen file --- PIA VPN/SwiftGen+Strings.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index f7a1c6739..d7ebc2860 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -411,9 +411,7 @@ internal enum L10n { } internal enum Collect { internal enum Data { - /// E-mail Address for the purposes of account management and protection from abuse. - /// - /// E-mail address is used to send subscription information, payment confirmations, customer correspondence, and Private Internet Access promotional offers only. + /// E-mail Address for the purposes of account management and protection from abuse.\n\nE-mail address is used to send subscription information, payment confirmations, customer correspondence, and Private Internet Access promotional offers only. internal static let description = L10n.tr("Localizable", "gdpr.collect.data.description") /// Personal information we collect internal static let title = L10n.tr("Localizable", "gdpr.collect.data.title") @@ -770,9 +768,7 @@ internal enum L10n { internal static let title = L10n.tr("Localizable", "settings.application_information.debug.failure.title") } internal enum Success { - /// Debug information successfully submitted. - /// ID: %@ - /// Please note this ID, as our support team will require this to locate your submission. + /// Debug information successfully submitted.\nID: %@\nPlease note this ID, as our support team will require this to locate your submission. internal static func message(_ p1: Any) -> String { return L10n.tr("Localizable", "settings.application_information.debug.success.message", String(describing: p1)) } @@ -1237,8 +1233,7 @@ internal enum L10n { internal enum Body { /// We don’t monitor, filter or log any network activity. internal static let footer = L10n.tr("Localizable", "vpn_permission.body.footer") - /// You’ll see a prompt for PIA VPN and need to allow access to VPN configurations. - /// To proceed tap on “%@”. + /// You’ll see a prompt for PIA VPN and need to allow access to VPN configurations.\nTo proceed tap on “%@”. internal static func subtitle(_ p1: Any) -> String { return L10n.tr("Localizable", "vpn_permission.body.subtitle", String(describing: p1)) } From 7c2ecaf5045b1c34a4a3428853eecc4a6e13841e Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Feb 2022 20:09:31 +0100 Subject: [PATCH 010/159] Removes more unwanted swiftgen changes --- PIA VPN/SwiftGen+Strings.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index d7ebc2860..8cdcd42ff 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -187,9 +187,7 @@ internal enum L10n { } internal enum ContentBlocker { internal enum Intro { - /// This version replaces MACE with our Safari Content Blocker. - /// - /// Check it out in the 'Settings' section. + /// This version replaces MACE with our Safari Content Blocker.\n\nCheck it out in the 'Settings' section. internal static let message = L10n.tr("Localizable", "dashboard.content_blocker.intro.message") } } @@ -411,7 +409,7 @@ internal enum L10n { } internal enum Collect { internal enum Data { - /// E-mail Address for the purposes of account management and protection from abuse.\n\nE-mail address is used to send subscription information, payment confirmations, customer correspondence, and Private Internet Access promotional offers only. + /// E-mail Address for the purposes of account management and protection from abuse.\n\nE-mail address is used to send subscription information, payment confirmations, customer correspondence, and Private Internet Access promotional offers only. internal static let description = L10n.tr("Localizable", "gdpr.collect.data.description") /// Personal information we collect internal static let title = L10n.tr("Localizable", "gdpr.collect.data.title") From 708cc076e3f3c5a85e1563e605e3d75d5fdfc2a8 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 16 Feb 2022 12:53:28 +0100 Subject: [PATCH 011/159] Moves the string extension to private scope and applies a code refactor --- PIA VPN.xcodeproj/project.pbxproj | 6 ------ PIA VPN/MessagesManager.swift | 7 +++++++ PIA VPN/String+AddDetailSymbol.swift | 17 ----------------- PIA VPN/UserSurveyManager.swift | 2 +- 4 files changed, 8 insertions(+), 24 deletions(-) delete mode 100644 PIA VPN/String+AddDetailSymbol.swift diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 3752a3b19..b1b58050b 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -151,8 +151,6 @@ 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */; }; 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; - 7ECBB8E027BC229600C0C774 /* String+AddDetailSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */; }; - 7ECBB8E127BC247A00C0C774 /* String+AddDetailSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; 821674F22678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; 82183D7B2500FD460033023F /* String+Substrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82183D7A2500FD460033023F /* String+Substrings.swift */; }; @@ -620,7 +618,6 @@ 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.release.xcconfig"; sourceTree = ""; }; 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_dev.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; - 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+AddDetailSymbol.swift"; sourceTree = ""; }; 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; 82183D7A2500FD460033023F /* String+Substrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Substrings.swift"; sourceTree = ""; }; @@ -937,7 +934,6 @@ DDE432DE2498EADA0095B197 /* Array+Group.swift */, 82183D7A2500FD460033023F /* String+Substrings.swift */, 82C0071225231F2800F21AF2 /* String+VPNType.swift */, - 7ECBB8DF27BC229600C0C774 /* String+AddDetailSymbol.swift */, ); name = Extensions; sourceTree = ""; @@ -2117,7 +2113,6 @@ 827570D924DAB6E4008F9800 /* NetworkFooterCollectionViewCell.swift in Sources */, 0EFDC1C71FE35B78007C0B9B /* Macros+App.swift in Sources */, 0ECC1E421FDB45100039891D /* AboutViewController.swift in Sources */, - 7ECBB8E127BC247A00C0C774 /* String+AddDetailSymbol.swift in Sources */, 82C9F3CD25C43863005039F9 /* ActiveDedicatedIpHeaderViewCell.swift in Sources */, DD125DCD21E772B6004ECCB6 /* QuickConnectTileCollectionViewCell.swift in Sources */, 0E8DCA06204D94E800B086DE /* ContentBlockerViewController.swift in Sources */, @@ -2247,7 +2242,6 @@ 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */, 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */, 0EFDC1D71FE46177007C0B9B /* SensitiveOperation.swift in Sources */, - 7ECBB8E027BC229600C0C774 /* String+AddDetailSymbol.swift in Sources */, 82C0071325231F2800F21AF2 /* String+VPNType.swift in Sources */, 826BE8EE253861BE002339F3 /* DedicatedRegionCell.swift in Sources */, 0E3A35281FD9A960000B0F99 /* DashboardViewController.swift in Sources */, diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 48a29b61f..57543bbfe 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -209,3 +209,10 @@ extension MessagesManager { MessagesManager.shared.postSystemMessage(message: message) } } + +private extension String { + func appendDetailSymbol() -> String { + let symbol = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft ? "⟨ " : " ⟩" + return "\(self)\(symbol)" + } +} diff --git a/PIA VPN/String+AddDetailSymbol.swift b/PIA VPN/String+AddDetailSymbol.swift deleted file mode 100644 index 465e3fc92..000000000 --- a/PIA VPN/String+AddDetailSymbol.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// String+AddDetailSymbol.swift -// PIA VPN -// -// Created by Waleed Mahmood on 15.02.22. -// Copyright © 2022 Private Internet Access Inc. All rights reserved. -// - -import Foundation -import UIKit - -public extension String { - func appendDetailSymbol() -> String { - let symbol = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft ? "⟨ " : " ⟩" - return "\(self)\(symbol)" - } -} diff --git a/PIA VPN/UserSurveyManager.swift b/PIA VPN/UserSurveyManager.swift index b4d5c888e..094cf0f63 100644 --- a/PIA VPN/UserSurveyManager.swift +++ b/PIA VPN/UserSurveyManager.swift @@ -23,6 +23,6 @@ class UserSurveyManager { // MARK: Survey Settings private func shouldShowSurveryMessage() -> Bool { - return AppPreferences.shared.successConnections == AppConstants.Survey.numberOfConnectionsUntilPrompt ? true : false + return AppPreferences.shared.successConnections == AppConstants.Survey.numberOfConnectionsUntilPrompt } } From c2d6aaa2e04732dd8d79270454644bb09926164f Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 16 Feb 2022 15:49:59 +0100 Subject: [PATCH 012/159] Update for an edge case where user has to interact with the survey message --- PIA VPN/AppPreferences.swift | 15 +++++++++++++++ PIA VPN/MessagesManager.swift | 12 ++++++++---- PIA VPN/UserSurveyManager.swift | 7 ++++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 663c1dec7..4e947dbf2 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -99,6 +99,9 @@ class AppPreferences { static let checksDipExpirationRequest = "checksDipExpirationRequest" static let showNewInitialScreen = "showNewInitialScreen" + // Survey + static let userInteractedWithSurvey = "userInteractedWithSurvey" + // Dev static let appEnvironmentIsProduction = "AppEnvironmentIsProduction" static let stagingVersion = "StagingVersion" @@ -519,6 +522,15 @@ class AppPreferences { } } + var userInteractedWithSurvey: Bool { + get { + return defaults.bool(forKey: Entries.userInteractedWithSurvey) + } + set { + defaults.set(newValue, forKey: Entries.userInteractedWithSurvey) + } + } + private init() { guard let defaults = UserDefaults(suiteName: AppConstants.appGroup) else { fatalError("Unable to initialize app preferences") @@ -555,6 +567,7 @@ class AppPreferences { Entries.showsDedicatedIPView: true, Entries.disablesMultiDipTokens: true, Entries.checksDipExpirationRequest: true, + Entries.userInteractedWithSurvey: false, Entries.stagingVersion: 0, Entries.appEnvironmentIsProduction: Client.environment == .production ? true : false, ]) @@ -784,6 +797,7 @@ class AppPreferences { dedicatedTokenIPReleation = [:] appEnvironmentIsProduction = Client.environment == .production ? true : false MessagesManager.shared.reset() + userInteractedWithSurvey = false } func clean() { @@ -818,6 +832,7 @@ class AppPreferences { dedicatedTokenIPReleation = [:] MessagesManager.shared.reset() appEnvironmentIsProduction = Client.environment == .production ? true : false + userInteractedWithSurvey = false } // + (void)eraseForTesting; diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 57543bbfe..9a72f9818 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -28,7 +28,8 @@ public class MessagesManager: NSObject { public static let shared = MessagesManager() private var apiMessage: InAppMessage! private var systemMessage: InAppMessage! - + private static let surveyMessageID = "take-the-survey-message-banner" + public override init() { super.init() NotificationCenter.default.addObserver(self, selector: #selector(presentExpiringDIPRegionSystemMessage(notification:)), name: .PIADIPRegionExpiring, object: nil) @@ -71,6 +72,10 @@ public class MessagesManager: NSObject { } func dismiss(message id: String) { + if id == MessagesManager.surveyMessageID { + AppPreferences.shared.userInteractedWithSurvey = true + } + AppPreferences.shared.dismissedMessages.append(id) if apiMessage != nil, id == apiMessage.id { apiMessage = nil @@ -202,9 +207,8 @@ extension MessagesManager { func showInAppSurveyMessage() { - let messageID = "take-the-survey-message-banner" - let message = InAppMessage(withMessage: ["en-US": L10n.Account.Survey.message.appendDetailSymbol()], id: messageID, link: ["en-US": L10n.Account.Survey.messageLink.appendDetailSymbol()], type: .link, level: .api, actions: nil, view: nil, uri: AppConstants.Survey.formURL.absoluteString) { [weak self] in - self?.dismiss(message: messageID) + let message = InAppMessage(withMessage: ["en-US": L10n.Account.Survey.message.appendDetailSymbol()], id: MessagesManager.surveyMessageID, link: ["en-US": L10n.Account.Survey.messageLink.appendDetailSymbol()], type: .link, level: .api, actions: nil, view: nil, uri: AppConstants.Survey.formURL.absoluteString) { [weak self] in + self?.dismiss(message: MessagesManager.surveyMessageID) } MessagesManager.shared.postSystemMessage(message: message) } diff --git a/PIA VPN/UserSurveyManager.swift b/PIA VPN/UserSurveyManager.swift index 094cf0f63..ec0b22649 100644 --- a/PIA VPN/UserSurveyManager.swift +++ b/PIA VPN/UserSurveyManager.swift @@ -16,13 +16,14 @@ class UserSurveyManager { } func handleConnectionSuccess() { - if shouldShowSurveryMessage() { + if shouldShowSurveyMessage() { MessagesManager.shared.showInAppSurveyMessage() } } // MARK: Survey Settings - private func shouldShowSurveryMessage() -> Bool { - return AppPreferences.shared.successConnections == AppConstants.Survey.numberOfConnectionsUntilPrompt + private func shouldShowSurveyMessage() -> Bool { + let appPreferences = AppPreferences.shared + return !appPreferences.userInteractedWithSurvey && appPreferences.successConnections >= AppConstants.Survey.numberOfConnectionsUntilPrompt } } From 72dfb9ac18c01099ca4e8fc3426e2821ec309b64 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 16 Feb 2022 16:18:21 +0100 Subject: [PATCH 013/159] Points to correct commit sha for PIAKPI and PIALibrary modules --- Podfile | 4 ++-- Podfile.lock | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Podfile b/Podfile index 90d9e05b6..47f096a9f 100644 --- a/Podfile +++ b/Podfile @@ -76,12 +76,12 @@ def shared_main_pods pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.1' #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :commit => 'b62d1bab' - pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :commit => '31186b1d' + pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('4c13f1e6') + library_by_gitlab_by_git('703113f4') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 01331a0df..2bf5fbfa2 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -75,11 +75,11 @@ PODS: - PIACSIModule/gradle (= 1.0.1) - PIACSIModule/csi (1.0.1) - PIACSIModule/gradle (1.0.1) - - PIAKPIModule (1.0.1): - - PIAKPIModule/gradle (= 1.0.1) - - PIAKPIModule/kpi (= 1.0.1) - - PIAKPIModule/gradle (1.0.1) - - PIAKPIModule/kpi (1.0.1) + - PIAKPIModule (1.1.0): + - PIAKPIModule/gradle (= 1.1.0) + - PIAKPIModule/kpi (= 1.1.0) + - PIAKPIModule/gradle (1.1.0) + - PIAKPIModule/kpi (1.1.0) - PIALibrary/Core (2.14.0): - PIAAccountModule - PIALibrary/Library (2.14.0): @@ -159,11 +159,11 @@ DEPENDENCIES: - OpenSSL-Apple (from `https://github.com/keeshux/openssl-apple`) - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, commit `31186b1d`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4c13f1e6`)" + - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -214,10 +214,10 @@ EXTERNAL SOURCES: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :commit: 31186b1d + :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 4c13f1e6 + :commit: 703113f4 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -240,10 +240,10 @@ CHECKOUT OPTIONS: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :commit: 31186b1d + :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 4c13f1e6 + :commit: 703113f4 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -277,7 +277,7 @@ SPEC CHECKSUMS: OpenSSL-Apple: bb7c9715a259404de040f5359ed3b3170cedf8d6 PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea - PIAKPIModule: ec04bedfc7e6eccc7dfe4bc630a99c104ffbb7ea + PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d PIALibrary: d52e06ca2995dd5692dcadb678475acbb0000cd2 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 82a23ff5a151e40eb7534ae4b69056482ad20542 +PODFILE CHECKSUM: fbe608d4e51826ba47ee0dd2639c4847fcf22089 COCOAPODS: 1.11.2 From eb7171d6f56bf97f5c034b203806b1859ba499ad Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 16 Feb 2022 18:29:10 +0100 Subject: [PATCH 014/159] Updates PIALibrary commit sha --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 541052695..a670a78bf 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('70a3c462') + library_by_gitlab_by_git('913ef511') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index f768b90f4..f9c35bea6 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `70a3c462`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 70a3c462 + :commit: 913ef511 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 70a3c462 + :commit: 913ef511 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: e05eadad7dd5b54da2538b8cc1254116f39daa7b +PODFILE CHECKSUM: 8baa871d22552efb1b330f906a83ec2d30d1164d COCOAPODS: 1.11.2 From cb4997301b007e8b2bf645d0ded47726bbea900e Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 17 Feb 2022 11:31:52 +0100 Subject: [PATCH 015/159] Updates commit sha --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index a670a78bf..08d7e13c2 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('913ef511') + library_by_gitlab_by_git('90f24b0b') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index f9c35bea6..423394d98 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `913ef511`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 913ef511 + :commit: 90f24b0b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 913ef511 + :commit: 90f24b0b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 8baa871d22552efb1b330f906a83ec2d30d1164d +PODFILE CHECKSUM: e59a64421071f34d640851918162f0e8c46bfd40 COCOAPODS: 1.11.2 From 26c1bc6fcd20d05441b8b1eba42b9e6e3fac494d Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 17 Feb 2022 13:19:09 +0100 Subject: [PATCH 016/159] Adds functionality to have a separate connections-counter for UserSurvey message banner logic --- PIA VPN/AppPreferences.swift | 17 +++++++++++++++++ PIA VPN/Bootstrapper.swift | 1 + PIA VPN/DashboardViewController.swift | 2 ++ PIA VPN/UserSurveyManager.swift | 2 +- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 4e947dbf2..30120af31 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -101,6 +101,7 @@ class AppPreferences { // Survey static let userInteractedWithSurvey = "userInteractedWithSurvey" + static let consecutiveSuccessConnections = "consecutiveSuccessConnections" // Dev static let appEnvironmentIsProduction = "AppEnvironmentIsProduction" @@ -531,6 +532,15 @@ class AppPreferences { } } + private(set) var consecutiveSuccessConnections: Int { + get { + return defaults.integer(forKey: Entries.consecutiveSuccessConnections) + } + set { + defaults.set(newValue, forKey: Entries.consecutiveSuccessConnections) + } + } + private init() { guard let defaults = UserDefaults(suiteName: AppConstants.appGroup) else { fatalError("Unable to initialize app preferences") @@ -568,6 +578,7 @@ class AppPreferences { Entries.disablesMultiDipTokens: true, Entries.checksDipExpirationRequest: true, Entries.userInteractedWithSurvey: false, + Entries.consecutiveSuccessConnections: 0, Entries.stagingVersion: 0, Entries.appEnvironmentIsProduction: Client.environment == .production ? true : false, ]) @@ -798,6 +809,7 @@ class AppPreferences { appEnvironmentIsProduction = Client.environment == .production ? true : false MessagesManager.shared.reset() userInteractedWithSurvey = false + consecutiveSuccessConnections = 0 } func clean() { @@ -833,6 +845,7 @@ class AppPreferences { MessagesManager.shared.reset() appEnvironmentIsProduction = Client.environment == .production ? true : false userInteractedWithSurvey = false + consecutiveSuccessConnections = 0 } // + (void)eraseForTesting; @@ -881,4 +894,8 @@ class AppPreferences { self.successConnections += 1 } + func incrementConsecutiveSuccessConnections() { + self.consecutiveSuccessConnections += 1 + } + } diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index a76e6337a..052801737 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -260,6 +260,7 @@ class Bootstrapper { return } AppPreferences.shared.incrementSuccessConnections() + AppPreferences.shared.incrementConsecutiveSuccessConnections() RatingManager.shared.handleConnectionSuccess() UserSurveyManager.shared.handleConnectionSuccess() } diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index a66a775d1..e9bd2af89 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -155,6 +155,8 @@ class DashboardViewController: AutolayoutViewController { updateCurrentStatus() setupCallingCards() + // Checks if survey needs to be shown + UserSurveyManager.shared.handleConnectionSuccess() } override func viewDidAppear(_ animated: Bool) { diff --git a/PIA VPN/UserSurveyManager.swift b/PIA VPN/UserSurveyManager.swift index ec0b22649..aa5acede4 100644 --- a/PIA VPN/UserSurveyManager.swift +++ b/PIA VPN/UserSurveyManager.swift @@ -24,6 +24,6 @@ class UserSurveyManager { // MARK: Survey Settings private func shouldShowSurveyMessage() -> Bool { let appPreferences = AppPreferences.shared - return !appPreferences.userInteractedWithSurvey && appPreferences.successConnections >= AppConstants.Survey.numberOfConnectionsUntilPrompt + return !appPreferences.userInteractedWithSurvey && appPreferences.consecutiveSuccessConnections >= AppConstants.Survey.numberOfConnectionsUntilPrompt } } From 0c026ee8953a59bd33fe7d608c48ecbccc55fb7d Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 17 Feb 2022 16:26:39 +0100 Subject: [PATCH 017/159] Code refactor for User survey's separate counter --- PIA VPN/AppPreferences.swift | 22 ++++++++++++---------- PIA VPN/Bootstrapper.swift | 1 - PIA VPN/DashboardViewController.swift | 4 +++- PIA VPN/UserSurveyManager.swift | 9 ++++++--- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 30120af31..8e5cca3fe 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -101,7 +101,7 @@ class AppPreferences { // Survey static let userInteractedWithSurvey = "userInteractedWithSurvey" - static let consecutiveSuccessConnections = "consecutiveSuccessConnections" + static let successConnectionsUntilSurvey = "successConnectionsUntilSurvey" // Dev static let appEnvironmentIsProduction = "AppEnvironmentIsProduction" @@ -532,12 +532,12 @@ class AppPreferences { } } - private(set) var consecutiveSuccessConnections: Int { + private(set) var successConnectionsUntilSurvey: Int? { get { - return defaults.integer(forKey: Entries.consecutiveSuccessConnections) + return defaults.value(forKey: Entries.successConnectionsUntilSurvey) as? Int ?? nil } set { - defaults.set(newValue, forKey: Entries.consecutiveSuccessConnections) + defaults.set(newValue, forKey: Entries.successConnectionsUntilSurvey) } } @@ -578,7 +578,6 @@ class AppPreferences { Entries.disablesMultiDipTokens: true, Entries.checksDipExpirationRequest: true, Entries.userInteractedWithSurvey: false, - Entries.consecutiveSuccessConnections: 0, Entries.stagingVersion: 0, Entries.appEnvironmentIsProduction: Client.environment == .production ? true : false, ]) @@ -809,7 +808,7 @@ class AppPreferences { appEnvironmentIsProduction = Client.environment == .production ? true : false MessagesManager.shared.reset() userInteractedWithSurvey = false - consecutiveSuccessConnections = 0 + successConnectionsUntilSurvey = nil } func clean() { @@ -845,7 +844,7 @@ class AppPreferences { MessagesManager.shared.reset() appEnvironmentIsProduction = Client.environment == .production ? true : false userInteractedWithSurvey = false - consecutiveSuccessConnections = 0 + successConnectionsUntilSurvey = nil } // + (void)eraseForTesting; @@ -891,11 +890,14 @@ class AppPreferences { // MARK: Connections func incrementSuccessConnections() { + updateSuccessConnectionsUntilSurvey() self.successConnections += 1 } - func incrementConsecutiveSuccessConnections() { - self.consecutiveSuccessConnections += 1 + private func updateSuccessConnectionsUntilSurvey() { + if let _ = successConnectionsUntilSurvey { + return + } + self.successConnectionsUntilSurvey = successConnections + AppConstants.Survey.numberOfConnectionsUntilPrompt } - } diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index 052801737..a76e6337a 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -260,7 +260,6 @@ class Bootstrapper { return } AppPreferences.shared.incrementSuccessConnections() - AppPreferences.shared.incrementConsecutiveSuccessConnections() RatingManager.shared.handleConnectionSuccess() UserSurveyManager.shared.handleConnectionSuccess() } diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index e9bd2af89..f61c3b9cc 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -156,7 +156,9 @@ class DashboardViewController: AutolayoutViewController { setupCallingCards() // Checks if survey needs to be shown - UserSurveyManager.shared.handleConnectionSuccess() + if UserSurveyManager.shouldShowSurveyMessage() { + MessagesManager.shared.showInAppSurveyMessage() + } } override func viewDidAppear(_ animated: Bool) { diff --git a/PIA VPN/UserSurveyManager.swift b/PIA VPN/UserSurveyManager.swift index aa5acede4..d372e4df1 100644 --- a/PIA VPN/UserSurveyManager.swift +++ b/PIA VPN/UserSurveyManager.swift @@ -16,14 +16,17 @@ class UserSurveyManager { } func handleConnectionSuccess() { - if shouldShowSurveyMessage() { + if UserSurveyManager.shouldShowSurveyMessage() { MessagesManager.shared.showInAppSurveyMessage() } } // MARK: Survey Settings - private func shouldShowSurveyMessage() -> Bool { + static func shouldShowSurveyMessage() -> Bool { let appPreferences = AppPreferences.shared - return !appPreferences.userInteractedWithSurvey && appPreferences.consecutiveSuccessConnections >= AppConstants.Survey.numberOfConnectionsUntilPrompt + guard let successConnectionUntilSurvey = appPreferences.successConnectionsUntilSurvey else { + return false + } + return !appPreferences.userInteractedWithSurvey && appPreferences.successConnections >= successConnectionUntilSurvey } } From 3261f612aae77dbf6a200ee5deb058e608935893 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Fri, 18 Feb 2022 10:46:59 +0100 Subject: [PATCH 018/159] Refactor on successConnectionUntilSurvey --- PIA VPN/AppPreferences.swift | 13 +++---------- PIA VPN/UserSurveyManager.swift | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 8e5cca3fe..1db7ad708 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -532,9 +532,9 @@ class AppPreferences { } } - private(set) var successConnectionsUntilSurvey: Int? { + var successConnectionsUntilSurvey: Int? { get { - return defaults.value(forKey: Entries.successConnectionsUntilSurvey) as? Int ?? nil + return defaults.value(forKey: Entries.successConnectionsUntilSurvey) as? Int } set { defaults.set(newValue, forKey: Entries.successConnectionsUntilSurvey) @@ -890,14 +890,7 @@ class AppPreferences { // MARK: Connections func incrementSuccessConnections() { - updateSuccessConnectionsUntilSurvey() - self.successConnections += 1 + successConnections += 1 } - private func updateSuccessConnectionsUntilSurvey() { - if let _ = successConnectionsUntilSurvey { - return - } - self.successConnectionsUntilSurvey = successConnections + AppConstants.Survey.numberOfConnectionsUntilPrompt - } } diff --git a/PIA VPN/UserSurveyManager.swift b/PIA VPN/UserSurveyManager.swift index d372e4df1..bcbdbf6a8 100644 --- a/PIA VPN/UserSurveyManager.swift +++ b/PIA VPN/UserSurveyManager.swift @@ -7,12 +7,28 @@ // import Foundation +import PIALibrary class UserSurveyManager { static let shared = UserSurveyManager() private init() { + let nc = NotificationCenter.default + nc.addObserver(self, selector: #selector(setupConnectionCounters), name: .PIAAccountDidRefresh, object: nil) + setupConnectionCounters() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc private func setupConnectionCounters() { + let appPreferences = AppPreferences.shared + if let _ = appPreferences.successConnectionsUntilSurvey { + return + } + appPreferences.successConnectionsUntilSurvey = appPreferences.successConnections + AppConstants.Survey.numberOfConnectionsUntilPrompt } func handleConnectionSuccess() { From 72669a00c57a790336a962f35cd8948d0947b4f8 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 21 Feb 2022 15:57:53 +0100 Subject: [PATCH 019/159] Updates commit sha and time_to_connect collection logic --- PIA VPN/Settings/HelpSettingsViewController.swift | 1 + Podfile | 2 +- Podfile.lock | 14 +++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/PIA VPN/Settings/HelpSettingsViewController.swift b/PIA VPN/Settings/HelpSettingsViewController.swift index c02b7109c..2ecee6591 100644 --- a/PIA VPN/Settings/HelpSettingsViewController.swift +++ b/PIA VPN/Settings/HelpSettingsViewController.swift @@ -79,6 +79,7 @@ class HelpSettingsViewController: PIABaseSettingsViewController { @objc private func toggleShareServiceQualityData(_ sender: UISwitch) { let preferences = Client.preferences.editable() preferences.shareServiceQualityData = sender.isOn + preferences.versionServiceQualityOpted = Macros.versionString() preferences.commit() if sender.isOn { diff --git a/Podfile b/Podfile index 08d7e13c2..03e16a83a 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('90f24b0b') + library_by_gitlab_by_git('a578a59b') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 423394d98..8f0bcb7cb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `90f24b0b`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 90f24b0b + :commit: a578a59b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 90f24b0b + :commit: a578a59b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: e59a64421071f34d640851918162f0e8c46bfd40 +PODFILE CHECKSUM: b4dbaaa97c9ddc0e986f1afb2de46394f9d76d1e COCOAPODS: 1.11.2 From 0ed12b1b81d25dd657e7ab95c0f7a966d121f8aa Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 21 Feb 2022 17:30:23 +0100 Subject: [PATCH 020/159] Code refactor and updates commit sha --- PIA VPN/Settings/HelpSettingsViewController.swift | 5 +++-- Podfile | 2 +- Podfile.lock | 14 +++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/PIA VPN/Settings/HelpSettingsViewController.swift b/PIA VPN/Settings/HelpSettingsViewController.swift index 2ecee6591..506a9f4b2 100644 --- a/PIA VPN/Settings/HelpSettingsViewController.swift +++ b/PIA VPN/Settings/HelpSettingsViewController.swift @@ -79,15 +79,16 @@ class HelpSettingsViewController: PIABaseSettingsViewController { @objc private func toggleShareServiceQualityData(_ sender: UISwitch) { let preferences = Client.preferences.editable() preferences.shareServiceQualityData = sender.isOn - preferences.versionServiceQualityOpted = Macros.versionString() - preferences.commit() if sender.isOn { + preferences.versionServiceQualityOpted = Macros.versionString() ServiceQualityManager.shared.start() } else { + preferences.versionServiceQualityOpted = nil ServiceQualityManager.shared.stop() } + preferences.commit() reloadSettings() } diff --git a/Podfile b/Podfile index 03e16a83a..5c98d691a 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('a578a59b') + library_by_gitlab_by_git('79086e91') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 8f0bcb7cb..a57d59381 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `a578a59b`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: a578a59b + :commit: '79086e91' :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: a578a59b + :commit: '79086e91' :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: b4dbaaa97c9ddc0e986f1afb2de46394f9d76d1e +PODFILE CHECKSUM: e55b879e7e0a5a41ef20990c324d5287b9a827c3 COCOAPODS: 1.11.2 From f7523abc8c8a0bc0664bdccc57e37d902c3868fb Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 21 Feb 2022 17:42:40 +0100 Subject: [PATCH 021/159] Code refactor --- PIA VPN/Settings/HelpSettingsViewController.swift | 4 ++-- Podfile | 2 +- Podfile.lock | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/PIA VPN/Settings/HelpSettingsViewController.swift b/PIA VPN/Settings/HelpSettingsViewController.swift index 506a9f4b2..1af28ff6a 100644 --- a/PIA VPN/Settings/HelpSettingsViewController.swift +++ b/PIA VPN/Settings/HelpSettingsViewController.swift @@ -81,10 +81,10 @@ class HelpSettingsViewController: PIABaseSettingsViewController { preferences.shareServiceQualityData = sender.isOn if sender.isOn { - preferences.versionServiceQualityOpted = Macros.versionString() + preferences.versionWhenServiceQualityOpted = Macros.versionString() ServiceQualityManager.shared.start() } else { - preferences.versionServiceQualityOpted = nil + preferences.versionWhenServiceQualityOpted = nil ServiceQualityManager.shared.stop() } diff --git a/Podfile b/Podfile index 5c98d691a..3617da915 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('79086e91') + library_by_gitlab_by_git('5549b5fb') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index a57d59381..bf59abe8e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `79086e91`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: '79086e91' + :commit: 5549b5fb :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: '79086e91' + :commit: 5549b5fb :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: e55b879e7e0a5a41ef20990c324d5287b9a827c3 +PODFILE CHECKSUM: fdc70de5d7ef2d231f0deb3da0e213b9149a37e7 COCOAPODS: 1.11.2 From 424b4f4319c8a9a25c3fb3df94df755a7600dff8 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 21 Feb 2022 17:52:19 +0100 Subject: [PATCH 022/159] Updates commit sha --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 3617da915..5e133cf13 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('5549b5fb') + library_by_gitlab_by_git('e1dd40bb') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index bf59abe8e..d5059d10e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5549b5fb`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 5549b5fb + :commit: e1dd40bb :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 5549b5fb + :commit: e1dd40bb :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: fdc70de5d7ef2d231f0deb3da0e213b9149a37e7 +PODFILE CHECKSUM: 87418e08fa6aecc18ea01a5b3255d32d52d28c62 COCOAPODS: 1.11.2 From 6d7d1f3c1db2e3216f03f4ac98d5b80b456f7cb7 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 21 Feb 2022 19:12:32 +0100 Subject: [PATCH 023/159] Updates to correct commit sha for KPI library --- Podfile | 2 +- Podfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index 5e133cf13..aed90ae8e 100644 --- a/Podfile +++ b/Podfile @@ -76,7 +76,7 @@ def shared_main_pods pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.1' #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :commit => 'b62d1bab' - pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' + pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :commit => '403d8f2' #library_by_path('~/Repositories') #library_by_git('') diff --git a/Podfile.lock b/Podfile.lock index d5059d10e..3c039a8fd 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -159,7 +159,7 @@ DEPENDENCIES: - OpenSSL-Apple (from `https://github.com/keeshux/openssl-apple`) - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" + - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, commit `403d8f2`)" - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" @@ -214,7 +214,7 @@ EXTERNAL SOURCES: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :branch: release/1.1.0 + :commit: 403d8f2 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: :commit: e1dd40bb @@ -240,7 +240,7 @@ CHECKOUT OPTIONS: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f + :commit: 403d8f2 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: :commit: e1dd40bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 87418e08fa6aecc18ea01a5b3255d32d52d28c62 +PODFILE CHECKSUM: 7cd6917e7c5aac321b0648498340ddf171b3230f COCOAPODS: 1.11.2 From f9f8634386d0e1a67907f91d9599613832cd5989 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 22 Feb 2022 13:50:40 +0100 Subject: [PATCH 024/159] Updates KPI commit sha --- Podfile | 2 +- Podfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index aed90ae8e..5e133cf13 100644 --- a/Podfile +++ b/Podfile @@ -76,7 +76,7 @@ def shared_main_pods pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.1' #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :commit => 'b62d1bab' - pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :commit => '403d8f2' + pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' #library_by_path('~/Repositories') #library_by_git('') diff --git a/Podfile.lock b/Podfile.lock index 3c039a8fd..b68f86a1a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -159,7 +159,7 @@ DEPENDENCIES: - OpenSSL-Apple (from `https://github.com/keeshux/openssl-apple`) - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, commit `403d8f2`)" + - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" @@ -214,7 +214,7 @@ EXTERNAL SOURCES: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :commit: 403d8f2 + :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: :commit: e1dd40bb @@ -240,7 +240,7 @@ CHECKOUT OPTIONS: :commit: b62d1bab :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: - :commit: 403d8f2 + :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: :commit: e1dd40bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 7cd6917e7c5aac321b0648498340ddf171b3230f +PODFILE CHECKSUM: 87418e08fa6aecc18ea01a5b3255d32d52d28c62 COCOAPODS: 1.11.2 From f0c5fe6dd3af6609dc0cdbdcf2dbe9d1e677caed Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 24 Feb 2022 16:46:37 +0100 Subject: [PATCH 025/159] Updates the commit for wrong ip fix --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 47f096a9f..df91ae8e6 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('703113f4') + library_by_gitlab_by_git('aca720b4') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 2bf5fbfa2..4248e4b8d 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 703113f4 + :commit: aca720b4 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 703113f4 + :commit: aca720b4 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: fbe608d4e51826ba47ee0dd2639c4847fcf22089 +PODFILE CHECKSUM: 550cd625da7289a3e72c629a5309558f17f979c6 COCOAPODS: 1.11.2 From 966cda59af4685b8d86e97d2fc967f27147381f6 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 1 Mar 2022 12:02:50 +0100 Subject: [PATCH 026/159] Adds UI Test target --- PIA VPN UITests/PIALaunchTests.swift | 33 ++++ PIA VPN UITests/PIAUITests.swift | 43 +++++ PIA VPN.xcodeproj/project.pbxproj | 154 +++++++++++++++++- .../xcschemes/PIA VPN WG Tunnel.xcscheme | 10 ++ .../xcshareddata/xcschemes/PIA VPN.xcscheme | 10 ++ 5 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 PIA VPN UITests/PIALaunchTests.swift create mode 100644 PIA VPN UITests/PIAUITests.swift diff --git a/PIA VPN UITests/PIALaunchTests.swift b/PIA VPN UITests/PIALaunchTests.swift new file mode 100644 index 000000000..fc904bb62 --- /dev/null +++ b/PIA VPN UITests/PIALaunchTests.swift @@ -0,0 +1,33 @@ +// +// PIALaunchTests.swift +// PIA VPN UITests +// +// Created by Waleed Mahmood on 01.03.22. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import XCTest + +class PIALaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/PIA VPN UITests/PIAUITests.swift b/PIA VPN UITests/PIAUITests.swift new file mode 100644 index 000000000..c220431f0 --- /dev/null +++ b/PIA VPN UITests/PIAUITests.swift @@ -0,0 +1,43 @@ +// +// PIAUITests.swift +// PIA VPN UITests +// +// Created by Waleed Mahmood on 01.03.22. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import XCTest + +class PIAUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index b1b58050b..351535697 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -149,6 +149,8 @@ 3545E98426AADC7E00B812CC /* ServerSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E97F26AAD60C00B812CC /* ServerSelectionDelegate.swift */; }; 3732E0F0C64DA8513903087D /* Pods_PIA_VPN_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02823EF3398B7E2C32FA7544 /* Pods_PIA_VPN_Tunnel.framework */; }; 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */; }; + 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; + 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; @@ -433,6 +435,13 @@ remoteGlobalIDString = 0EFB606F203D7A2C0095398C; remoteInfo = "PIA VPN AdBlocker"; }; + 7EB8D11927CCF4C20030B060 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 291C6374183EBC210039EC03 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 291C637B183EBC210039EC03; + remoteInfo = "PIA VPN"; + }; 8269A6E1251CB5E3000B4DBF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 291C6374183EBC210039EC03 /* Project object */; @@ -617,6 +626,9 @@ 59EC919445EA680B691ACC88 /* Pods-PIA VPNTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.debug.xcconfig"; sourceTree = ""; }; 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.release.xcconfig"; sourceTree = ""; }; 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_dev.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; + 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; @@ -819,6 +831,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 7EB8D11027CCF4C20030B060 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8269A6D2251CB5E0000B4DBF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1049,6 +1068,7 @@ 0EEE1C191E4F719E00397DE2 /* Resources */, DDC8F5ED23EC1070005D19C6 /* PIA VPN WG Tunnel */, 8269A6D8251CB5E0000B4DBF /* PIAWidget */, + 7EB8D11427CCF4C20030B060 /* PIA VPN UITests */, 291C637E183EBC210039EC03 /* Frameworks */, 291C637D183EBC210039EC03 /* Products */, 90B2842B07AEDA4CF8A2035B /* Pods */, @@ -1065,6 +1085,7 @@ 0EFB6070203D7A2C0095398C /* PIA VPN AdBlocker.appex */, DDC8F5EC23EC106F005D19C6 /* PIA VPN WG Tunnel.appex */, 8269A6D5251CB5E0000B4DBF /* PIAWidgetExtension.appex */, + 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */, ); name = Products; sourceTree = ""; @@ -1165,6 +1186,15 @@ name = UI; sourceTree = ""; }; + 7EB8D11427CCF4C20030B060 /* PIA VPN UITests */ = { + isa = PBXGroup; + children = ( + 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */, + 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */, + ); + path = "PIA VPN UITests"; + sourceTree = ""; + }; 82183D8425014F940033023F /* Menu */ = { isa = PBXGroup; children = ( @@ -1510,6 +1540,24 @@ productReference = 291C637C183EBC210039EC03 /* PIA VPN.app */; productType = "com.apple.product-type.application"; }; + 7EB8D11227CCF4C20030B060 /* PIA VPN UITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7EB8D11D27CCF4C20030B060 /* Build configuration list for PBXNativeTarget "PIA VPN UITests" */; + buildPhases = ( + 7EB8D10F27CCF4C20030B060 /* Sources */, + 7EB8D11027CCF4C20030B060 /* Frameworks */, + 7EB8D11127CCF4C20030B060 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7EB8D11A27CCF4C20030B060 /* PBXTargetDependency */, + ); + name = "PIA VPN UITests"; + productName = "PIA VPN UITests"; + productReference = 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; 8269A6D4251CB5E0000B4DBF /* PIAWidgetExtension */ = { isa = PBXNativeTarget; buildConfigurationList = 8269A6E4251CB5E3000B4DBF /* Build configuration list for PBXNativeTarget "PIAWidgetExtension" */; @@ -1552,7 +1600,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = PIA; - LastSwiftUpdateCheck = 1200; + LastSwiftUpdateCheck = 1300; LastUpgradeCheck = 0930; ORGANIZATIONNAME = "Private Internet Access Inc."; TargetAttributes = { @@ -1624,6 +1672,12 @@ }; }; }; + 7EB8D11227CCF4C20030B060 = { + CreatedOnToolsVersion = 13.0; + DevelopmentTeam = 5357M5NW9W; + ProvisioningStyle = Automatic; + TestTargetID = 291C637B183EBC210039EC03; + }; 8269A6D4251CB5E0000B4DBF = { CreatedOnToolsVersion = 12.0; DevelopmentTeam = 5357M5NW9W; @@ -1673,6 +1727,7 @@ DDC8F5EB23EC106F005D19C6 /* PIA VPN WG Tunnel */, 8269A6D4251CB5E0000B4DBF /* PIAWidgetExtension */, 0EEE1BE61E4F6EF400397DE2 /* PIA VPNTests */, + 7EB8D11227CCF4C20030B060 /* PIA VPN UITests */, ); }; /* End PBXProject section */ @@ -1798,6 +1853,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 7EB8D11127CCF4C20030B060 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8269A6D3251CB5E0000B4DBF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2325,6 +2387,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 7EB8D10F27CCF4C20030B060 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */, + 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8269A6D1251CB5E0000B4DBF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2377,6 +2448,11 @@ target = 0EFB606F203D7A2C0095398C /* PIA VPN AdBlocker */; targetProxy = 0EFB607E203D893E0095398C /* PBXContainerItemProxy */; }; + 7EB8D11A27CCF4C20030B060 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 291C637B183EBC210039EC03 /* PIA VPN */; + targetProxy = 7EB8D11927CCF4C20030B060 /* PBXContainerItemProxy */; + }; 8269A6E2251CB5E3000B4DBF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8269A6D4251CB5E0000B4DBF /* PIAWidgetExtension */; @@ -2908,6 +2984,73 @@ }; name = Release; }; + 7EB8D11B27CCF4C20030B060 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 5357M5NW9W; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.cyberghostsrl.PIA-VPN-UITests"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "PIA VPN"; + }; + name = Debug; + }; + 7EB8D11C27CCF4C20030B060 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 5357M5NW9W; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.cyberghostsrl.PIA-VPN-UITests"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "PIA VPN"; + }; + name = Release; + }; 8269A6E5251CB5E3000B4DBF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3107,6 +3250,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 7EB8D11D27CCF4C20030B060 /* Build configuration list for PBXNativeTarget "PIA VPN UITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7EB8D11B27CCF4C20030B060 /* Debug */, + 7EB8D11C27CCF4C20030B060 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 8269A6E4251CB5E3000B4DBF /* Build configuration list for PBXNativeTarget "PIAWidgetExtension" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme index 117e38cf1..7af92a33e 100644 --- a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme +++ b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme @@ -43,6 +43,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + Date: Wed, 2 Mar 2022 18:32:47 +0100 Subject: [PATCH 027/159] Updates commit sha after merging client-library-apple for timeToConnect MR --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 5e133cf13..eeafef265 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('e1dd40bb') + library_by_gitlab_by_git('b6b6b365') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index b68f86a1a..773e22024 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `e1dd40bb`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b6b6b365`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b6b6b365`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b6b6b365`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `b6b6b365`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: e1dd40bb + :commit: b6b6b365 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: e1dd40bb + :commit: b6b6b365 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 87418e08fa6aecc18ea01a5b3255d32d52d28c62 +PODFILE CHECKSUM: f6de171d7e2e4ea4a2ff25c170614dd5fda599d1 COCOAPODS: 1.11.2 From f61375a5c156ee2cfe41a79fffa9c73566ce5f3b Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 7 Mar 2022 09:21:29 +0100 Subject: [PATCH 028/159] Adds a UI test for invalid user --- PIA VPN UITests/PIALoginTests.swift | 89 +++++++++++++++++++++++++++++ PIA VPN.xcodeproj/project.pbxproj | 4 ++ Podfile | 2 +- Podfile.lock | 14 ++--- 4 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 PIA VPN UITests/PIALoginTests.swift diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift new file mode 100644 index 000000000..9a12cda14 --- /dev/null +++ b/PIA VPN UITests/PIALoginTests.swift @@ -0,0 +1,89 @@ +// +// PIALoginTests.swift +// PIA VPN UITests +// +// Created by Waleed Mahmood on 01.03.22. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import XCTest + +class PIALoginTests: XCTestCase { + + static let requestTimeout = 10 + + override func setUpWithError() throws { + continueAfterFailure = false + XCUIApplication().launch() + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + private func assertfalse(with message: String) { + XCTAssert(false, message) + } + + func testInvalideUserLogin() throws { + let app = XCUIApplication() + + // wait for feature flags + sleep(6) + + let loginButtonGetStartedView = app.buttons["uitests.login.submit"] + let newLoginButtonGetStartedView = app.buttons["uitests.login.newSubmit"] + + if loginButtonGetStartedView.exists || newLoginButtonGetStartedView.exists { + if loginButtonGetStartedView.exists { + loginButtonGetStartedView.tap() + } else { + newLoginButtonGetStartedView.tap() + } + + let usernameTextField = app.textFields["uitests.login.username"] + let passwordTextField = app.secureTextFields["uitests.login.password"] + let loginButton = app.buttons["uitests.login.submit"] + + if usernameTextField.exists && passwordTextField.exists { + usernameTextField.tap() + + // Issue with custome fields: + // https://stackoverflow.com/questions/32184837/ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-se + + // Type username + for str in Array("randomusername") { + app.keys[String(str)].tap() + } + + passwordTextField.tap() + for str in Array("randompassword") { + app.keys[String(str)].tap() + } + + let expectation = XCTestExpectation(description: "Perform login") + loginButton.tap() + DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(5)) { + XCTAssert(loginButton.exists, "testInvalideUserLogin failed") + expectation.fulfill() + } + wait(for: [expectation], timeout: TimeInterval(PIALoginTests.requestTimeout)) + } else { + assertfalse(with: "Username and Password text fields did not exist or are moved") + } + } else { + assertfalse(with: "Login button did not exist or is moved") + } + } + + func testActiveUserLogin() throws { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testInactiveUserLogin() throws { + + } +} diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 351535697..4e5c5bbc3 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -151,6 +151,7 @@ 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */; }; 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; + 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */; }; 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; @@ -629,6 +630,7 @@ 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; + 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALoginTests.swift; sourceTree = ""; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; @@ -1189,6 +1191,7 @@ 7EB8D11427CCF4C20030B060 /* PIA VPN UITests */ = { isa = PBXGroup; children = ( + 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */, 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */, 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */, ); @@ -2391,6 +2394,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */, 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */, 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */, ); diff --git a/Podfile b/Podfile index 47f096a9f..18b25706f 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('703113f4') + library_by_gitlab_by_git('d34a4b7a') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 2bf5fbfa2..637af9d7a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `703113f4`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 703113f4 + :commit: d34a4b7a :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 703113f4 + :commit: d34a4b7a :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: fbe608d4e51826ba47ee0dd2639c4847fcf22089 +PODFILE CHECKSUM: 878cfcb551638f1e3162ff77ec51fef1008709d5 COCOAPODS: 1.11.2 From 669b3271cb02ff77c570e805c26858729828b50d Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 7 Mar 2022 11:42:59 +0100 Subject: [PATCH 029/159] Updates commit sha --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index df91ae8e6..50d4fdd6f 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('aca720b4') + library_by_gitlab_by_git('f3211dd7') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 4248e4b8d..3b0449dd4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `aca720b4`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: aca720b4 + :commit: f3211dd7 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: aca720b4 + :commit: f3211dd7 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 550cd625da7289a3e72c629a5309558f17f979c6 +PODFILE CHECKSUM: 76db4e8bffd4312e75aa82deb57065219d1d12ce COCOAPODS: 1.11.2 From 2a9cf6e58fe46c289ded19af428d33e5897ce77d Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 8 Mar 2022 13:18:12 +0100 Subject: [PATCH 030/159] Code refactor and adds PIALibrary to UITests target for accessibility identifiers --- PIA VPN UITests/PIALoginTests.swift | 75 +++++++++++++++++++---------- PIA VPN.xcodeproj/project.pbxproj | 49 +++++++++++++++++++ Podfile | 6 ++- Podfile.lock | 14 +++--- 4 files changed, 110 insertions(+), 34 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index 9a12cda14..85913914d 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -7,14 +7,17 @@ // import XCTest +import PIALibrary +import Pods_PIA_VPN_dev class PIALoginTests: XCTestCase { - static let requestTimeout = 10 + static let timeoutUIOps: TimeInterval = 10.0 + let app = XCUIApplication() override func setUpWithError() throws { continueAfterFailure = false - XCUIApplication().launch() + app.launch() // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. } @@ -27,41 +30,41 @@ class PIALoginTests: XCTestCase { XCTAssert(false, message) } - func testInvalideUserLogin() throws { - let app = XCUIApplication() + + func testInvalidUserLogin() throws { + + var isNewButtonUsed = false // wait for feature flags - sleep(6) + var submitButtonExists = app.buttons[Accessibility.UITests.Login.submit].waitForExistence(timeout: PIALoginTests.timeoutUIOps) - let loginButtonGetStartedView = app.buttons["uitests.login.submit"] - let newLoginButtonGetStartedView = app.buttons["uitests.login.newSubmit"] + // check if new button should be used + if !submitButtonExists { + submitButtonExists = app.buttons[Accessibility.UITests.Login.submitNew].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + isNewButtonUsed = true + } - if loginButtonGetStartedView.exists || newLoginButtonGetStartedView.exists { - if loginButtonGetStartedView.exists { - loginButtonGetStartedView.tap() + app.switchEnvironmentToStaging() + + if submitButtonExists { + if submitButtonExists && !isNewButtonUsed { + app.buttons[Accessibility.UITests.Login.submit].tap() } else { - newLoginButtonGetStartedView.tap() + app.buttons[Accessibility.UITests.Login.submitNew].tap() } - let usernameTextField = app.textFields["uitests.login.username"] - let passwordTextField = app.secureTextFields["uitests.login.password"] - let loginButton = app.buttons["uitests.login.submit"] + let usernameTextField = app.textFields[Accessibility.UITests.Login.username] + let passwordTextField = app.secureTextFields[Accessibility.UITests.Login.password] + let loginButton = app.buttons[Accessibility.UITests.Login.submit] if usernameTextField.exists && passwordTextField.exists { - usernameTextField.tap() - - // Issue with custome fields: - // https://stackoverflow.com/questions/32184837/ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-se - // Type username - for str in Array("randomusername") { - app.keys[String(str)].tap() - } + usernameTextField.tap() + app.typeString(with: "randomusername") + // Type password passwordTextField.tap() - for str in Array("randompassword") { - app.keys[String(str)].tap() - } + app.typeString(with: "randompassword") let expectation = XCTestExpectation(description: "Perform login") loginButton.tap() @@ -69,7 +72,7 @@ class PIALoginTests: XCTestCase { XCTAssert(loginButton.exists, "testInvalideUserLogin failed") expectation.fulfill() } - wait(for: [expectation], timeout: TimeInterval(PIALoginTests.requestTimeout)) + wait(for: [expectation], timeout: PIALoginTests.timeoutUIOps) } else { assertfalse(with: "Username and Password text fields did not exist or are moved") } @@ -87,3 +90,23 @@ class PIALoginTests: XCTestCase { } } + +private extension XCUIApplication { + func typeString(with text: String) { + // Issue with custome fields: + // https://stackoverflow.com/questions/32184837/ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-se + for str in Array(text) { + self.keys[String(str)].tap() + } + } + + func switchEnvironmentToStaging() { + if Client.environment == .production { + // wait until the button is available + _ = self.buttons[Accessibility.UITests.Welcome.environment].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + + // then click it to switch environment + self.buttons[Accessibility.UITests.Welcome.environment].tap() + } + } +} diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 4e5c5bbc3..afc69e18d 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -148,6 +148,7 @@ 3545E98326AADC7C00B812CC /* ServerSelectingTileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */; }; 3545E98426AADC7E00B812CC /* ServerSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E97F26AAD60C00B812CC /* ServerSelectionDelegate.swift */; }; 3732E0F0C64DA8513903087D /* Pods_PIA_VPN_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02823EF3398B7E2C32FA7544 /* Pods_PIA_VPN_Tunnel.framework */; }; + 520C1753B3AD005B8609F080 /* Pods_PIA_VPN_UITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EB292786A3E16F34DFADE7D /* Pods_PIA_VPN_UITests.framework */; }; 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */; }; 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; @@ -627,6 +628,7 @@ 59EC919445EA680B691ACC88 /* Pods-PIA VPNTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.debug.xcconfig"; sourceTree = ""; }; 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.release.xcconfig"; sourceTree = ""; }; 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_dev.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6EB292786A3E16F34DFADE7D /* Pods_PIA_VPN_UITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_UITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; @@ -698,6 +700,7 @@ 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9F453DF1F58B8C2676041EDF /* Pods_PIA_VPNTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPNTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A6454C66DD80FE480E48BFA8 /* Pods-PIA VPN WG Tunnel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN WG Tunnel.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN WG Tunnel/Pods-PIA VPN WG Tunnel.debug.xcconfig"; sourceTree = ""; }; + A8AE4350B939233D4C2A176D /* Pods-PIA VPN UITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN UITests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN UITests/Pods-PIA VPN UITests.release.xcconfig"; sourceTree = ""; }; AC26C61F8D1F24341AD67A4A /* Pods-PIA VPN Tunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN Tunnel.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN Tunnel/Pods-PIA VPN Tunnel.release.xcconfig"; sourceTree = ""; }; B5E30A2B8E23D816328C690E /* Pods-PIA VPN WG Tunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN WG Tunnel.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN WG Tunnel/Pods-PIA VPN WG Tunnel.release.xcconfig"; sourceTree = ""; }; DD052AC22419003300AD3A24 /* ProtocolTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolTableViewCell.swift; sourceTree = ""; }; @@ -772,6 +775,7 @@ E65358880CC5204F785ADE10 /* Pods-PIA VPN Tunnel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN Tunnel.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN Tunnel/Pods-PIA VPN Tunnel.debug.xcconfig"; sourceTree = ""; }; EFF4BBB886BD153CAD50F8E2 /* Pods-PIA VPN dev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.release.xcconfig"; sourceTree = ""; }; F489C6FC39A7285DDE2A26F7 /* Pods-PIA VPN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN/Pods-PIA VPN.debug.xcconfig"; sourceTree = ""; }; + FA8D0BE3D6E8FE3AF571EE1C /* Pods-PIA VPN UITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN UITests.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN UITests/Pods-PIA VPN UITests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -837,6 +841,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 520C1753B3AD005B8609F080 /* Pods_PIA_VPN_UITests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1117,6 +1122,7 @@ 28DFD7434ECA3047EB4A2C75 /* Pods_PIA_VPN_WG_Tunnel.framework */, 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */, 9F453DF1F58B8C2676041EDF /* Pods_PIA_VPNTests.framework */, + 6EB292786A3E16F34DFADE7D /* Pods_PIA_VPN_UITests.framework */, ); name = Frameworks; sourceTree = ""; @@ -1293,6 +1299,8 @@ EFF4BBB886BD153CAD50F8E2 /* Pods-PIA VPN dev.release.xcconfig */, 59EC919445EA680B691ACC88 /* Pods-PIA VPNTests.debug.xcconfig */, 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */, + FA8D0BE3D6E8FE3AF571EE1C /* Pods-PIA VPN UITests.debug.xcconfig */, + A8AE4350B939233D4C2A176D /* Pods-PIA VPN UITests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -1547,9 +1555,11 @@ isa = PBXNativeTarget; buildConfigurationList = 7EB8D11D27CCF4C20030B060 /* Build configuration list for PBXNativeTarget "PIA VPN UITests" */; buildPhases = ( + 5B1D3ED823A958D1046A4FB6 /* [CP] Check Pods Manifest.lock */, 7EB8D10F27CCF4C20030B060 /* Sources */, 7EB8D11027CCF4C20030B060 /* Frameworks */, 7EB8D11127CCF4C20030B060 /* Resources */, + CDF5626F60268FA0C4FBC24A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1967,6 +1977,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 5B1D3ED823A958D1046A4FB6 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PIA VPN UITests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 5FDBE92B797C696CA4F5D6E3 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2063,6 +2095,21 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + CDF5626F60268FA0C4FBC24A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PIA VPN UITests/Pods-PIA VPN UITests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; DA22F2CE535A37D07F2DE296 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2990,6 +3037,7 @@ }; 7EB8D11B27CCF4C20030B060 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = FA8D0BE3D6E8FE3AF571EE1C /* Pods-PIA VPN UITests.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3024,6 +3072,7 @@ }; 7EB8D11C27CCF4C20030B060 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A8AE4350B939233D4C2A176D /* Pods-PIA VPN UITests.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/Podfile b/Podfile index 18b25706f..d5835a106 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('d34a4b7a') + library_by_gitlab_by_git('d45c4b3b') #library_by_version('~> 1.1.3') end @@ -139,6 +139,10 @@ target 'PIA VPNTests' do pod 'Fabric' end +target 'PIA VPN UITests' do + app_pods +end + post_install do |installer| installer.pods_project.targets.each do |target| if ['PopupDialog'].include? target.name diff --git a/Podfile.lock b/Podfile.lock index 637af9d7a..5af03e822 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d34a4b7a`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: d34a4b7a + :commit: d45c4b3b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: d34a4b7a + :commit: d45c4b3b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 878cfcb551638f1e3162ff77ec51fef1008709d5 +PODFILE CHECKSUM: 2463544b3d59fdfd9c8868989bcf2ba80fdf5d2b COCOAPODS: 1.11.2 From 2d68004ffe18b9788bf1efe873ea711d2ebb62b0 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 8 Mar 2022 14:25:34 +0100 Subject: [PATCH 031/159] Fixes a keyboard typing issue --- PIA VPN UITests/PIALoginTests.swift | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index 85913914d..9728ed506 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -60,11 +60,11 @@ class PIALoginTests: XCTestCase { if usernameTextField.exists && passwordTextField.exists { // Type username usernameTextField.tap() - app.typeString(with: "randomusername") + usernameTextField.typeText("random.username") // Type password passwordTextField.tap() - app.typeString(with: "randompassword") + passwordTextField.typeText("passWord@342") let expectation = XCTestExpectation(description: "Perform login") loginButton.tap() @@ -92,13 +92,6 @@ class PIALoginTests: XCTestCase { } private extension XCUIApplication { - func typeString(with text: String) { - // Issue with custome fields: - // https://stackoverflow.com/questions/32184837/ui-testing-failure-neither-element-nor-any-descendant-has-keyboard-focus-on-se - for str in Array(text) { - self.keys[String(str)].tap() - } - } func switchEnvironmentToStaging() { if Client.environment == .production { From 10dc78221fd3b336e4c7e2bd16150a0b78782d0b Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 8 Mar 2022 14:29:07 +0100 Subject: [PATCH 032/159] Adds a user credentials json file for testing different users --- PIA VPN UITests/user.credentials | 0 PIA VPN.xcodeproj/project.pbxproj | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 PIA VPN UITests/user.credentials diff --git a/PIA VPN UITests/user.credentials b/PIA VPN UITests/user.credentials new file mode 100644 index 000000000..e69de29bb diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index afc69e18d..cbd6b27af 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -153,6 +153,7 @@ 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */; }; + 7EC2971C27D791530061C56A /* user.credentials in Resources */ = {isa = PBXBuildFile; fileRef = 7EC2971B27D791530061C56A /* user.credentials */; }; 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; @@ -633,6 +634,7 @@ 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALoginTests.swift; sourceTree = ""; }; + 7EC2971B27D791530061C56A /* user.credentials */ = {isa = PBXFileReference; lastKnownFileType = text; path = user.credentials; sourceTree = ""; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; @@ -1200,6 +1202,7 @@ 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */, 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */, 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */, + 7EC2971B27D791530061C56A /* user.credentials */, ); path = "PIA VPN UITests"; sourceTree = ""; @@ -1870,6 +1873,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7EC2971C27D791530061C56A /* user.credentials in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 8ff312ba2e5ec5d99658281c0cc2fb664fe04ee1 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 8 Mar 2022 17:09:37 +0100 Subject: [PATCH 033/159] Point to PIALibrary release/2.15.0 --- Podfile | 2 +- Podfile.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Podfile b/Podfile index 50d4fdd6f..5079b43ed 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('f3211dd7') + library_by_gitlab_by_git('2b430fce') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 8d1c87ac3..f4d2903c1 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -80,9 +80,9 @@ PODS: - PIAKPIModule/kpi (= 1.1.0) - PIAKPIModule/gradle (1.1.0) - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.14.0): + - PIALibrary/Core (2.15.0): - PIAAccountModule - - PIALibrary/Library (2.14.0): + - PIALibrary/Library (2.15.0): - Alamofire (~> 4) - Gloss (~> 2) - PIAAccountModule @@ -94,18 +94,18 @@ PODS: - PopupDialog - ReachabilitySwift - SwiftyBeaver - - PIALibrary/Mock (2.14.0): + - PIALibrary/Mock (2.15.0): - PIALibrary/Library - - PIALibrary/UI (2.14.0): + - PIALibrary/UI (2.15.0): - FXPageControl - lottie-ios - PIALibrary/Library - SwiftEntryKit (= 0.7.2) - SwiftyBeaver - TPKeyboardAvoiding - - PIALibrary/Util (2.14.0): + - PIALibrary/Util (2.15.0): - PIALibrary/Core - - PIALibrary/VPN (2.14.0): + - PIALibrary/VPN (2.15.0): - PIALibrary/Library - PIAWireguard - TunnelKit @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f3211dd7`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: f3211dd7 + :commit: 2b430fce :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: f3211dd7 + :commit: 2b430fce :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -278,7 +278,7 @@ SPEC CHECKSUMS: PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: d52e06ca2995dd5692dcadb678475acbb0000cd2 + PIALibrary: 4ac21b0958f5f77186297c302df26ae99fb27fb7 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 76db4e8bffd4312e75aa82deb57065219d1d12ce +PODFILE CHECKSUM: 24686ecce7e3733cd5f30e1aa0e7ce0d618b15bb COCOAPODS: 1.11.2 From 9b6ccc1e3e3aae492c80f4ee48944d6b3b33cc5a Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 8 Mar 2022 17:10:59 +0100 Subject: [PATCH 034/159] Bump version to 3.15.0 --- PIA VPN.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index b1b58050b..18d3450dd 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2476,7 +2476,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2507,7 +2507,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2546,7 +2546,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2588,7 +2588,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2682,7 +2682,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2715,7 +2715,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2861,7 +2861,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -2898,7 +2898,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -2931,7 +2931,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -2969,7 +2969,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3002,7 +3002,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3038,7 +3038,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.14.0; + MARKETING_VERSION = 3.15.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From d0401a3e950c95b400f26f79c423e08b843ca365 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 9 Mar 2022 12:43:58 +0100 Subject: [PATCH 035/159] Use credentials from plist --- PIA VPN UITests/Credentials.plist | 17 +++++++ PIA VPN UITests/CredentialsUtil.swift | 47 +++++++++++++++++++ PIA VPN UITests/PIALoginTests.swift | 5 +- PIA VPN UITests/user.credentials | 0 PIA VPN.xcodeproj/project.pbxproj | 31 +++++++++--- .../xcschemes/PIA VPN dev.xcscheme | 10 ++++ 6 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 PIA VPN UITests/Credentials.plist create mode 100644 PIA VPN UITests/CredentialsUtil.swift delete mode 100644 PIA VPN UITests/user.credentials diff --git a/PIA VPN UITests/Credentials.plist b/PIA VPN UITests/Credentials.plist new file mode 100644 index 000000000..e256b0454 --- /dev/null +++ b/PIA VPN UITests/Credentials.plist @@ -0,0 +1,17 @@ + + + + + expired + + valid + + invalid + + username + random.username + password + passWord@533 + + + diff --git a/PIA VPN UITests/CredentialsUtil.swift b/PIA VPN UITests/CredentialsUtil.swift new file mode 100644 index 000000000..82d372ea4 --- /dev/null +++ b/PIA VPN UITests/CredentialsUtil.swift @@ -0,0 +1,47 @@ +// +// CredentialsUtil.swift +// PIA VPN +// +// Created by Waleed Mahmood on 08.03.22. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation + +public enum CredentialsType: String { + case valid = "valid" + case expired = "expired" + case invalid = "invalid" +} + +public struct Credentials: Codable { + let username: String + let password: String + + init(from dictionary: Any) throws { + let data = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) + let decoder = JSONDecoder() + self = try decoder.decode(Self.self, from: data) + } +} + +public class CredentialsUtil { + public static func credentials(type: CredentialsType) -> Credentials { + let bundle = Bundle(for: CredentialsUtil.self) + guard let filePath = bundle.path(forResource: "Credentials", ofType: "plist") else { + fatalError("Couldn't find file 'Credentials.plist'") + } + + let plist = NSDictionary(contentsOfFile: filePath) + guard let dictionary = plist?.object(forKey: type.rawValue) as? [String: String] else { + fatalError("Couldn't find key '\(type.rawValue)' in 'Credentials.plist'") + } + + do { + return try Credentials(from: dictionary) + } + catch { +  fatalError("Credential file does not contain required information") + } + } +} diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index 9728ed506..c2657943f 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -33,6 +33,7 @@ class PIALoginTests: XCTestCase { func testInvalidUserLogin() throws { + let credentials = CredentialsUtil.credentials(type: .invalid) var isNewButtonUsed = false // wait for feature flags @@ -60,11 +61,11 @@ class PIALoginTests: XCTestCase { if usernameTextField.exists && passwordTextField.exists { // Type username usernameTextField.tap() - usernameTextField.typeText("random.username") + usernameTextField.typeText(credentials.username) // Type password passwordTextField.tap() - passwordTextField.typeText("passWord@342") + passwordTextField.typeText(credentials.password) let expectation = XCTestExpectation(description: "Perform login") loginButton.tap() diff --git a/PIA VPN UITests/user.credentials b/PIA VPN UITests/user.credentials deleted file mode 100644 index e69de29bb..000000000 diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index cbd6b27af..d33d11866 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -153,7 +153,8 @@ 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */; }; - 7EC2971C27D791530061C56A /* user.credentials in Resources */ = {isa = PBXBuildFile; fileRef = 7EC2971B27D791530061C56A /* user.credentials */; }; + 7EC2972F27D8B8580061C56A /* CredentialsUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EC2972D27D8B8580061C56A /* CredentialsUtil.swift */; }; + 7EC2973027D8B8580061C56A /* Credentials.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7EC2972E27D8B8580061C56A /* Credentials.plist */; }; 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; @@ -445,6 +446,13 @@ remoteGlobalIDString = 291C637B183EBC210039EC03; remoteInfo = "PIA VPN"; }; + 7EC2973127D8BCB60061C56A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 291C6374183EBC210039EC03 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0EE2200B1F4EF307002805AE; + remoteInfo = "PIA VPN dev"; + }; 8269A6E1251CB5E3000B4DBF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 291C6374183EBC210039EC03 /* Project object */; @@ -634,7 +642,8 @@ 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALoginTests.swift; sourceTree = ""; }; - 7EC2971B27D791530061C56A /* user.credentials */ = {isa = PBXFileReference; lastKnownFileType = text; path = user.credentials; sourceTree = ""; }; + 7EC2972D27D8B8580061C56A /* CredentialsUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsUtil.swift; sourceTree = ""; }; + 7EC2972E27D8B8580061C56A /* Credentials.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Credentials.plist; sourceTree = ""; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; @@ -1199,10 +1208,11 @@ 7EB8D11427CCF4C20030B060 /* PIA VPN UITests */ = { isa = PBXGroup; children = ( + 7EC2972E27D8B8580061C56A /* Credentials.plist */, + 7EC2972D27D8B8580061C56A /* CredentialsUtil.swift */, 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */, 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */, 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */, - 7EC2971B27D791530061C56A /* user.credentials */, ); path = "PIA VPN UITests"; sourceTree = ""; @@ -1568,6 +1578,7 @@ ); dependencies = ( 7EB8D11A27CCF4C20030B060 /* PBXTargetDependency */, + 7EC2973227D8BCB60061C56A /* PBXTargetDependency */, ); name = "PIA VPN UITests"; productName = "PIA VPN UITests"; @@ -1692,7 +1703,7 @@ CreatedOnToolsVersion = 13.0; DevelopmentTeam = 5357M5NW9W; ProvisioningStyle = Automatic; - TestTargetID = 291C637B183EBC210039EC03; + TestTargetID = 0EE2200B1F4EF307002805AE; }; 8269A6D4251CB5E0000B4DBF = { CreatedOnToolsVersion = 12.0; @@ -1873,7 +1884,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7EC2971C27D791530061C56A /* user.credentials in Resources */, + 7EC2973027D8B8580061C56A /* Credentials.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2445,6 +2456,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7EC2972F27D8B8580061C56A /* CredentialsUtil.swift in Sources */, 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */, 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */, 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */, @@ -2508,6 +2520,11 @@ target = 291C637B183EBC210039EC03 /* PIA VPN */; targetProxy = 7EB8D11927CCF4C20030B060 /* PBXContainerItemProxy */; }; + 7EC2973227D8BCB60061C56A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0EE2200B1F4EF307002805AE /* PIA VPN dev */; + targetProxy = 7EC2973127D8BCB60061C56A /* PBXContainerItemProxy */; + }; 8269A6E2251CB5E3000B4DBF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8269A6D4251CB5E0000B4DBF /* PIAWidgetExtension */; @@ -3070,7 +3087,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = "PIA VPN"; + TEST_TARGET_NAME = "PIA VPN dev"; }; name = Debug; }; @@ -3104,7 +3121,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = "PIA VPN"; + TEST_TARGET_NAME = "PIA VPN dev"; }; name = Release; }; diff --git a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN dev.xcscheme b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN dev.xcscheme index 8baec6c39..a41ce8df3 100644 --- a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN dev.xcscheme +++ b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN dev.xcscheme @@ -75,6 +75,16 @@ ReferencedContainer = "container:PIA VPN.xcodeproj"> + + + + Date: Wed, 9 Mar 2022 13:22:21 +0100 Subject: [PATCH 036/159] Refactor invalid user test to reuse it other tests --- PIA VPN UITests/PIALoginTests.swift | 72 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index c2657943f..d87f90a7d 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -30,10 +30,7 @@ class PIALoginTests: XCTestCase { XCTAssert(false, message) } - - func testInvalidUserLogin() throws { - - let credentials = CredentialsUtil.credentials(type: .invalid) + private func navigateToLoginViewController() { var isNewButtonUsed = false // wait for feature flags @@ -45,8 +42,6 @@ class PIALoginTests: XCTestCase { isNewButtonUsed = true } - app.switchEnvironmentToStaging() - if submitButtonExists { if submitButtonExists && !isNewButtonUsed { app.buttons[Accessibility.UITests.Login.submit].tap() @@ -54,42 +49,53 @@ class PIALoginTests: XCTestCase { app.buttons[Accessibility.UITests.Login.submitNew].tap() } - let usernameTextField = app.textFields[Accessibility.UITests.Login.username] - let passwordTextField = app.secureTextFields[Accessibility.UITests.Login.password] - let loginButton = app.buttons[Accessibility.UITests.Login.submit] + } else { + assertfalse(with: "One of the Login buttons on GetStartedViewController is either not identifiable or have been moved") + } + } + + private func fillLoginScreen(with credentials: Credentials) { + let usernameTextField = app.textFields[Accessibility.UITests.Login.username] + let passwordTextField = app.secureTextFields[Accessibility.UITests.Login.password] + + if usernameTextField.exists && passwordTextField.exists { + // Type username + usernameTextField.tap() + usernameTextField.typeText(credentials.username) - if usernameTextField.exists && passwordTextField.exists { - // Type username - usernameTextField.tap() - usernameTextField.typeText(credentials.username) - - // Type password - passwordTextField.tap() - passwordTextField.typeText(credentials.password) - - let expectation = XCTestExpectation(description: "Perform login") - loginButton.tap() - DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(5)) { - XCTAssert(loginButton.exists, "testInvalideUserLogin failed") - expectation.fulfill() - } - wait(for: [expectation], timeout: PIALoginTests.timeoutUIOps) - } else { - assertfalse(with: "Username and Password text fields did not exist or are moved") - } + // Type password + passwordTextField.tap() + passwordTextField.typeText(credentials.password) } else { - assertfalse(with: "Login button did not exist or is moved") + assertfalse(with: "Username and Password text fields on LoginViewController are either not identifiable or are moved") } } - func testActiveUserLogin() throws { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func testInvalidUserLogin() throws { + + app.switchEnvironmentToStaging() + navigateToLoginViewController() + fillLoginScreen(with: CredentialsUtil.credentials(type: .invalid)) + + let loginButton = app.buttons[Accessibility.UITests.Login.submit] + let expectation = XCTestExpectation(description: "Perform login") + loginButton.tap() + + DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(5)) { + XCTAssert(loginButton.exists, "testInvalideUserLogin failed") + expectation.fulfill() + } + wait(for: [expectation], timeout: PIALoginTests.timeoutUIOps) } - func testInactiveUserLogin() throws { + func testExpiredUserLogin() throws { } + + func testActiveUserLogin() throws { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } } private extension XCUIApplication { From 95001f9cc45cc1c211ee726f5659a4527f288ae5 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 9 Mar 2022 18:49:41 +0100 Subject: [PATCH 037/159] Update PIALibrary commit, refactor and add valid user UI Test with updated accessibility identifiers --- PIA VPN UITests/PIALoginTests.swift | 67 +++++++++++++++++------ PIA VPN/DashboardViewController.swift | 2 + PIA VPN/MenuViewController.swift | 2 +- PIA VPN/VPNPermissionViewController.swift | 1 + Podfile | 2 +- Podfile.lock | 14 ++--- 6 files changed, 62 insertions(+), 26 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index d87f90a7d..16113a52e 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -18,8 +18,8 @@ class PIALoginTests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false app.launch() - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + //app.switchEnvironmentToStaging() + navigateToGetStartedViewController() } override func tearDownWithError() throws { @@ -30,6 +30,31 @@ class PIALoginTests: XCTestCase { XCTAssert(false, message) } + private func navigateToGetStartedViewController() { + // wait 5 second for Dashboard to settle down + sleep(5) + + // check if we have a side menu + if app.navigationBars.buttons[Accessibility.UITests.Dashboard.menu].exists { + openSideMenuAndTapLogout() + } + } + + private func openSideMenuAndTapLogout() { + app.navigationBars.buttons[Accessibility.UITests.Dashboard.menu].tap() + + if app.cells[Accessibility.UITests.Menu.logout].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { + app.cells[Accessibility.UITests.Menu.logout].tap() + } else { + assertfalse(with: "PIALoginTests:: A side menu is found but no logout cell is found") + } + if app.buttons[Accessibility.UITests.Dialog.destructive].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { + app.buttons[Accessibility.UITests.Dialog.destructive].tap() + } else { + assertfalse(with: "PIALoginTests:: Logout alert destructive button is not found") + } + } + private func navigateToLoginViewController() { var isNewButtonUsed = false @@ -50,7 +75,7 @@ class PIALoginTests: XCTestCase { } } else { - assertfalse(with: "One of the Login buttons on GetStartedViewController is either not identifiable or have been moved") + assertfalse(with: "PIALoginTests:: One of the Login buttons on GetStartedViewController is either not identifiable or have been moved") } } @@ -67,34 +92,42 @@ class PIALoginTests: XCTestCase { passwordTextField.tap() passwordTextField.typeText(credentials.password) } else { - assertfalse(with: "Username and Password text fields on LoginViewController are either not identifiable or are moved") + assertfalse(with: "PIALoginTests:: Username and Password text fields on LoginViewController are either not identifiable or are moved") } } - func testInvalidUserLogin() throws { - - app.switchEnvironmentToStaging() + private func loginUser(ofType: CredentialsType) { navigateToLoginViewController() - fillLoginScreen(with: CredentialsUtil.credentials(type: .invalid)) + switch ofType { + case .valid: + fillLoginScreen(with: CredentialsUtil.credentials(type: .valid)) + case .expired: + fillLoginScreen(with: CredentialsUtil.credentials(type: .expired)) + case .invalid: + fillLoginScreen(with: CredentialsUtil.credentials(type: .invalid)) + } let loginButton = app.buttons[Accessibility.UITests.Login.submit] - let expectation = XCTestExpectation(description: "Perform login") loginButton.tap() + } + + func testInvalidUserLogin() throws { + loginUser(ofType: .invalid) - DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(5)) { - XCTAssert(loginButton.exists, "testInvalideUserLogin failed") - expectation.fulfill() - } - wait(for: [expectation], timeout: PIALoginTests.timeoutUIOps) + let bannerViewExists = app.staticTexts["Your username or password is incorrect."].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + XCTAssertTrue(bannerViewExists, "PIALoginTests::testInvalidUserLogin() failed") } func testExpiredUserLogin() throws { } - func testActiveUserLogin() throws { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func testValidUserLogin() throws { + loginUser(ofType: .valid) + + let viewTitleExists = app.staticTexts["PIA needs access to your VPN profiles to secure your traffic"].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + let okButtonExist = app.buttons[Accessibility.UITests.Permissions.submit].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + XCTAssertTrue(viewTitleExists && okButtonExist, "PIALoginTests::testActiveUserLogin() failed") } } diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index a66a775d1..187277fa1 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -248,6 +248,7 @@ class DashboardViewController: AutolayoutViewController { ) } navigationItem.leftBarButtonItem?.accessibilityLabel = L10n.Menu.Accessibility.item + navigationItem.leftBarButtonItem?.accessibilityIdentifier = Accessibility.UITests.Dashboard.menu if navigationItem.rightBarButtonItem == nil { navigationItem.rightBarButtonItem = UIBarButtonItem( @@ -267,6 +268,7 @@ class DashboardViewController: AutolayoutViewController { action: #selector(closeTileEditingMode(_:)) ) navigationItem.leftBarButtonItem?.accessibilityLabel = L10n.Global.cancel + navigationItem.leftBarButtonItem?.accessibilityIdentifier = nil navigationItem.rightBarButtonItem = nil } diff --git a/PIA VPN/MenuViewController.swift b/PIA VPN/MenuViewController.swift index cf3cda55f..bf722bbcb 100644 --- a/PIA VPN/MenuViewController.swift +++ b/PIA VPN/MenuViewController.swift @@ -460,7 +460,7 @@ extension MenuViewController: UITableViewDataSource, UITableViewDelegate { cell.accessibilityIdentifier = "uitests.menu.account" case .logout: - cell.accessibilityIdentifier = "uitests.menu.logout" + cell.accessibilityIdentifier = Accessibility.UITests.Menu.logout default: break diff --git a/PIA VPN/VPNPermissionViewController.swift b/PIA VPN/VPNPermissionViewController.swift index 4e805a78a..64306ecfb 100644 --- a/PIA VPN/VPNPermissionViewController.swift +++ b/PIA VPN/VPNPermissionViewController.swift @@ -112,6 +112,7 @@ class VPNPermissionViewController: AutolayoutViewController { buttonSubmit.style(style: TextStyle.Buttons.piaGreenButton) buttonSubmit.setTitle(L10n.Global.ok.uppercased(), for: []) + buttonSubmit.accessibilityIdentifier = Accessibility.UITests.Permissions.submit } } diff --git a/Podfile b/Podfile index d5835a106..20fda6195 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('d45c4b3b') + library_by_gitlab_by_git('87072ac6') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 5af03e822..e3820d374 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `d45c4b3b`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: d45c4b3b + :commit: 87072ac6 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: d45c4b3b + :commit: 87072ac6 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 2463544b3d59fdfd9c8868989bcf2ba80fdf5d2b +PODFILE CHECKSUM: df0619c2417fa1b0651096bc4c76e7359d31dd1a COCOAPODS: 1.11.2 From ed236906a55e4966c6761988bc86867801305691 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 10 Mar 2022 17:19:17 +0100 Subject: [PATCH 038/159] Fix the environment and UI test target selection issues --- PIA VPN UITests/PIALoginTests.swift | 18 ++++++++++++------ PIA VPN.xcodeproj/project.pbxproj | 13 ------------- .../xcshareddata/WorkspaceSettings.xcsettings | 8 ++++++++ 3 files changed, 20 insertions(+), 19 deletions(-) create mode 100644 PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index 16113a52e..dff82518a 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -18,7 +18,6 @@ class PIALoginTests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false app.launch() - //app.switchEnvironmentToStaging() navigateToGetStartedViewController() } @@ -119,10 +118,15 @@ class PIALoginTests: XCTestCase { } func testExpiredUserLogin() throws { + app.switchEnvironment(to: .staging) + loginUser(ofType: .expired) + let bannerViewExists = app.staticTexts["Your username or password is incorrect."].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + XCTAssertTrue(bannerViewExists, "PIALoginTests::testExpiredUserLogin() failed") } func testValidUserLogin() throws { + app.switchEnvironment(to: .production) loginUser(ofType: .valid) let viewTitleExists = app.staticTexts["PIA needs access to your VPN profiles to secure your traffic"].waitForExistence(timeout: PIALoginTests.timeoutUIOps) @@ -133,13 +137,15 @@ class PIALoginTests: XCTestCase { private extension XCUIApplication { - func switchEnvironmentToStaging() { - if Client.environment == .production { - // wait until the button is available - _ = self.buttons[Accessibility.UITests.Welcome.environment].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + func switchEnvironment(to environment: Client.Environment) { + // wait until the button is available + _ = self.buttons[Accessibility.UITests.Welcome.environment].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + let environmentButton = self.buttons[Accessibility.UITests.Welcome.environment] + + if environmentButton.label.lowercased() != environment.rawValue.lowercased() { // then click it to switch environment - self.buttons[Accessibility.UITests.Welcome.environment].tap() + environmentButton.tap() } } } diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index d33d11866..0c77d2aba 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -439,13 +439,6 @@ remoteGlobalIDString = 0EFB606F203D7A2C0095398C; remoteInfo = "PIA VPN AdBlocker"; }; - 7EB8D11927CCF4C20030B060 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 291C6374183EBC210039EC03 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 291C637B183EBC210039EC03; - remoteInfo = "PIA VPN"; - }; 7EC2973127D8BCB60061C56A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 291C6374183EBC210039EC03 /* Project object */; @@ -1577,7 +1570,6 @@ buildRules = ( ); dependencies = ( - 7EB8D11A27CCF4C20030B060 /* PBXTargetDependency */, 7EC2973227D8BCB60061C56A /* PBXTargetDependency */, ); name = "PIA VPN UITests"; @@ -2515,11 +2507,6 @@ target = 0EFB606F203D7A2C0095398C /* PIA VPN AdBlocker */; targetProxy = 0EFB607E203D893E0095398C /* PBXContainerItemProxy */; }; - 7EB8D11A27CCF4C20030B060 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 291C637B183EBC210039EC03 /* PIA VPN */; - targetProxy = 7EB8D11927CCF4C20030B060 /* PBXContainerItemProxy */; - }; 7EC2973227D8BCB60061C56A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 0EE2200B1F4EF307002805AE /* PIA VPN dev */; diff --git a/PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + From bdf9b37dcbb0e24e1d3acbe3a06181c4c7816f6c Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 10 Mar 2022 17:30:32 +0100 Subject: [PATCH 039/159] Remove XCTAssert as a function --- PIA VPN UITests/PIALoginTests.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index dff82518a..e36eff3fd 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -25,10 +25,6 @@ class PIALoginTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - private func assertfalse(with message: String) { - XCTAssert(false, message) - } - private func navigateToGetStartedViewController() { // wait 5 second for Dashboard to settle down sleep(5) @@ -45,12 +41,12 @@ class PIALoginTests: XCTestCase { if app.cells[Accessibility.UITests.Menu.logout].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { app.cells[Accessibility.UITests.Menu.logout].tap() } else { - assertfalse(with: "PIALoginTests:: A side menu is found but no logout cell is found") + XCTAssert(false, "PIALoginTests:: A side menu is found but no logout cell is found") } if app.buttons[Accessibility.UITests.Dialog.destructive].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { app.buttons[Accessibility.UITests.Dialog.destructive].tap() } else { - assertfalse(with: "PIALoginTests:: Logout alert destructive button is not found") + XCTAssert(false, "PIALoginTests:: Logout alert destructive button is not found") } } @@ -74,7 +70,7 @@ class PIALoginTests: XCTestCase { } } else { - assertfalse(with: "PIALoginTests:: One of the Login buttons on GetStartedViewController is either not identifiable or have been moved") + XCTAssert(false, "PIALoginTests:: One of the Login buttons on GetStartedViewController is either not identifiable or have been moved") } } @@ -91,7 +87,7 @@ class PIALoginTests: XCTestCase { passwordTextField.tap() passwordTextField.typeText(credentials.password) } else { - assertfalse(with: "PIALoginTests:: Username and Password text fields on LoginViewController are either not identifiable or are moved") + XCTAssert(false, "PIALoginTests:: Username and Password text fields on LoginViewController are either not identifiable or are moved") } } From bc63d92d92bf44084f8643aa0cca8eb9a4176837 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 10 Mar 2022 17:41:09 +0100 Subject: [PATCH 040/159] Remove unnecessary sleeps --- PIA VPN UITests/PIALoginTests.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index e36eff3fd..44620816e 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -22,13 +22,11 @@ class PIALoginTests: XCTestCase { } override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. + // when test finishes logout + navigateToGetStartedViewController() } private func navigateToGetStartedViewController() { - // wait 5 second for Dashboard to settle down - sleep(5) - // check if we have a side menu if app.navigationBars.buttons[Accessibility.UITests.Dashboard.menu].exists { openSideMenuAndTapLogout() From 622fe532a130323d19c682a71164623c3aa90b4a Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 10 Mar 2022 18:00:15 +0100 Subject: [PATCH 041/159] Update commit sha PIALibrary --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 20fda6195..36ab1c6a4 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('87072ac6') + library_by_gitlab_by_git('f1f3551c') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index e3820d374..b3511041d 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `87072ac6`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 87072ac6 + :commit: f1f3551c :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: 4ffb43cc4bb9d66da9b0b966d98934507b2cb04f :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 87072ac6 + :commit: f1f3551c :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: df0619c2417fa1b0651096bc4c76e7359d31dd1a +PODFILE CHECKSUM: d7a3374cafb61866e90c7b331ac581b51aaee222 COCOAPODS: 1.11.2 From 73f755f129c0ef7af602d095fca5f93fb64a6b9c Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Fri, 11 Mar 2022 18:32:31 +0100 Subject: [PATCH 042/159] Update PIA Library commit & accessibility strings and code improvements --- PIA VPN UITests/PIALoginTests.swift | 38 +++++++++++------------ PIA VPN/DashboardViewController.swift | 2 +- PIA VPN/MenuViewController.swift | 2 +- PIA VPN/VPNPermissionViewController.swift | 2 +- Podfile | 2 +- Podfile.lock | 14 ++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index 44620816e..99a464087 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -28,43 +28,43 @@ class PIALoginTests: XCTestCase { private func navigateToGetStartedViewController() { // check if we have a side menu - if app.navigationBars.buttons[Accessibility.UITests.Dashboard.menu].exists { + if app.navigationBars.buttons[Accessibility.Id.Dashboard.menu].exists { openSideMenuAndTapLogout() } } private func openSideMenuAndTapLogout() { - app.navigationBars.buttons[Accessibility.UITests.Dashboard.menu].tap() + app.navigationBars.buttons[Accessibility.Id.Dashboard.menu].tap() - if app.cells[Accessibility.UITests.Menu.logout].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { - app.cells[Accessibility.UITests.Menu.logout].tap() + if app.cells[Accessibility.Id.Menu.logout].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { + app.cells[Accessibility.Id.Menu.logout].tap() } else { XCTAssert(false, "PIALoginTests:: A side menu is found but no logout cell is found") } - if app.buttons[Accessibility.UITests.Dialog.destructive].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { - app.buttons[Accessibility.UITests.Dialog.destructive].tap() + if app.buttons[Accessibility.Id.Dialog.destructive].waitForExistence(timeout: PIALoginTests.timeoutUIOps) { + app.buttons[Accessibility.Id.Dialog.destructive].tap() } else { XCTAssert(false, "PIALoginTests:: Logout alert destructive button is not found") } } private func navigateToLoginViewController() { - var isNewButtonUsed = false + var loginUsingFeatureFlags = false // wait for feature flags - var submitButtonExists = app.buttons[Accessibility.UITests.Login.submit].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + var submitButtonExists = app.buttons[Accessibility.Id.Login.submit].waitForExistence(timeout: PIALoginTests.timeoutUIOps) // check if new button should be used if !submitButtonExists { - submitButtonExists = app.buttons[Accessibility.UITests.Login.submitNew].waitForExistence(timeout: PIALoginTests.timeoutUIOps) - isNewButtonUsed = true + submitButtonExists = app.buttons[Accessibility.Id.Login.submitNew].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + loginUsingFeatureFlags = true } if submitButtonExists { - if submitButtonExists && !isNewButtonUsed { - app.buttons[Accessibility.UITests.Login.submit].tap() + if submitButtonExists && !loginUsingFeatureFlags { + app.buttons[Accessibility.Id.Login.submit].tap() } else { - app.buttons[Accessibility.UITests.Login.submitNew].tap() + app.buttons[Accessibility.Id.Login.submitNew].tap() } } else { @@ -73,8 +73,8 @@ class PIALoginTests: XCTestCase { } private func fillLoginScreen(with credentials: Credentials) { - let usernameTextField = app.textFields[Accessibility.UITests.Login.username] - let passwordTextField = app.secureTextFields[Accessibility.UITests.Login.password] + let usernameTextField = app.textFields[Accessibility.Id.Login.username] + let passwordTextField = app.secureTextFields[Accessibility.Id.Login.password] if usernameTextField.exists && passwordTextField.exists { // Type username @@ -100,7 +100,7 @@ class PIALoginTests: XCTestCase { fillLoginScreen(with: CredentialsUtil.credentials(type: .invalid)) } - let loginButton = app.buttons[Accessibility.UITests.Login.submit] + let loginButton = app.buttons[Accessibility.Id.Login.submit] loginButton.tap() } @@ -124,7 +124,7 @@ class PIALoginTests: XCTestCase { loginUser(ofType: .valid) let viewTitleExists = app.staticTexts["PIA needs access to your VPN profiles to secure your traffic"].waitForExistence(timeout: PIALoginTests.timeoutUIOps) - let okButtonExist = app.buttons[Accessibility.UITests.Permissions.submit].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + let okButtonExist = app.buttons[Accessibility.Id.Permissions.submit].waitForExistence(timeout: PIALoginTests.timeoutUIOps) XCTAssertTrue(viewTitleExists && okButtonExist, "PIALoginTests::testActiveUserLogin() failed") } } @@ -133,8 +133,8 @@ private extension XCUIApplication { func switchEnvironment(to environment: Client.Environment) { // wait until the button is available - _ = self.buttons[Accessibility.UITests.Welcome.environment].waitForExistence(timeout: PIALoginTests.timeoutUIOps) - let environmentButton = self.buttons[Accessibility.UITests.Welcome.environment] + _ = self.buttons[Accessibility.Id.Welcome.environment].waitForExistence(timeout: PIALoginTests.timeoutUIOps) + let environmentButton = self.buttons[Accessibility.Id.Welcome.environment] if environmentButton.label.lowercased() != environment.rawValue.lowercased() { diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 092b76362..9e9b0e8a8 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -252,7 +252,7 @@ class DashboardViewController: AutolayoutViewController { ) } navigationItem.leftBarButtonItem?.accessibilityLabel = L10n.Menu.Accessibility.item - navigationItem.leftBarButtonItem?.accessibilityIdentifier = Accessibility.UITests.Dashboard.menu + navigationItem.leftBarButtonItem?.accessibilityIdentifier = Accessibility.Id.Dashboard.menu if navigationItem.rightBarButtonItem == nil { navigationItem.rightBarButtonItem = UIBarButtonItem( diff --git a/PIA VPN/MenuViewController.swift b/PIA VPN/MenuViewController.swift index bf722bbcb..170ee4077 100644 --- a/PIA VPN/MenuViewController.swift +++ b/PIA VPN/MenuViewController.swift @@ -460,7 +460,7 @@ extension MenuViewController: UITableViewDataSource, UITableViewDelegate { cell.accessibilityIdentifier = "uitests.menu.account" case .logout: - cell.accessibilityIdentifier = Accessibility.UITests.Menu.logout + cell.accessibilityIdentifier = Accessibility.Id.Menu.logout default: break diff --git a/PIA VPN/VPNPermissionViewController.swift b/PIA VPN/VPNPermissionViewController.swift index 64306ecfb..922be68f7 100644 --- a/PIA VPN/VPNPermissionViewController.swift +++ b/PIA VPN/VPNPermissionViewController.swift @@ -112,7 +112,7 @@ class VPNPermissionViewController: AutolayoutViewController { buttonSubmit.style(style: TextStyle.Buttons.piaGreenButton) buttonSubmit.setTitle(L10n.Global.ok.uppercased(), for: []) - buttonSubmit.accessibilityIdentifier = Accessibility.UITests.Permissions.submit + buttonSubmit.accessibilityIdentifier = Accessibility.Id.Permissions.submit } } diff --git a/Podfile b/Podfile index 36ab1c6a4..488d65799 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('f1f3551c') + library_by_gitlab_by_git('c786f8a8') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index d26361ef5..a547db24a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `f1f3551c`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: f1f3551c + :commit: c786f8a8 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: f1f3551c + :commit: c786f8a8 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: d7a3374cafb61866e90c7b331ac581b51aaee222 +PODFILE CHECKSUM: 52320f6c9dd74ff82124a1b0d2f44076ce9e7795 COCOAPODS: 1.11.2 From 92251d08b76d6b4c821fa7324923ab79ea363669 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 14 Mar 2022 11:34:23 +0100 Subject: [PATCH 043/159] Rename conversion bool --- PIA VPN UITests/PIALoginTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PIA VPN UITests/PIALoginTests.swift b/PIA VPN UITests/PIALoginTests.swift index 99a464087..af87077ab 100644 --- a/PIA VPN UITests/PIALoginTests.swift +++ b/PIA VPN UITests/PIALoginTests.swift @@ -49,7 +49,7 @@ class PIALoginTests: XCTestCase { } private func navigateToLoginViewController() { - var loginUsingFeatureFlags = false + var conversionSubviewVisible = false // wait for feature flags var submitButtonExists = app.buttons[Accessibility.Id.Login.submit].waitForExistence(timeout: PIALoginTests.timeoutUIOps) @@ -57,11 +57,11 @@ class PIALoginTests: XCTestCase { // check if new button should be used if !submitButtonExists { submitButtonExists = app.buttons[Accessibility.Id.Login.submitNew].waitForExistence(timeout: PIALoginTests.timeoutUIOps) - loginUsingFeatureFlags = true + conversionSubviewVisible = true } if submitButtonExists { - if submitButtonExists && !loginUsingFeatureFlags { + if submitButtonExists && !conversionSubviewVisible { app.buttons[Accessibility.Id.Login.submit].tap() } else { app.buttons[Accessibility.Id.Login.submitNew].tap() From 2d6ddfd176bc08d14e2b07bc71071632b8a74e50 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Mar 2022 09:53:48 +0100 Subject: [PATCH 044/159] Download translations and new en placeholders --- PIA VPN/ar.lproj/Localizable.strings | 11 +++++++---- PIA VPN/da.lproj/Localizable.strings | 11 +++++++---- PIA VPN/de.lproj/Localizable.strings | 11 +++++++---- PIA VPN/es-MX.lproj/Localizable.strings | 11 +++++++---- PIA VPN/fr.lproj/Localizable.strings | 11 +++++++---- PIA VPN/it.lproj/Localizable.strings | 11 +++++++---- PIA VPN/ja.lproj/Localizable.strings | 11 +++++++---- PIA VPN/ko.lproj/Localizable.strings | 11 +++++++---- PIA VPN/nb.lproj/Localizable.strings | 11 +++++++---- PIA VPN/nl.lproj/Localizable.strings | 11 +++++++---- PIA VPN/pl.lproj/Localizable.strings | 11 +++++++---- PIA VPN/pt-BR.lproj/Localizable.strings | 11 +++++++---- PIA VPN/ru.lproj/Localizable.strings | 11 +++++++---- PIA VPN/th.lproj/Localizable.strings | 11 +++++++---- PIA VPN/zh-Hans.lproj/Localizable.strings | 11 +++++++---- PIA VPN/zh-Hant.lproj/Localizable.strings | 11 +++++++---- 16 files changed, 112 insertions(+), 64 deletions(-) diff --git a/PIA VPN/ar.lproj/Localizable.strings b/PIA VPN/ar.lproj/Localizable.strings index 29f7826f7..b8211a1eb 100644 --- a/PIA VPN/ar.lproj/Localizable.strings +++ b/PIA VPN/ar.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "خطة شهرية"; "account.subscriptions.trial" = "خطة تجريبية"; "account.unauthorized" = "حدث خطأ. يرجى محاولة تسجيل الدخول مرة أخرى"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "حذف الحساب"; +"account.delete.alert.title" = "هل أنت متأكد؟"; +"account.delete.alert.message" = "يعد حذف حساب PIA الخاص بك نهائي ولا رجعة فيه. لن تتمكن من استرجاع بيانات اعتماد PIA الخاصة بك بعد تنفيذ هذا الإجراء. يرجى الملاحظة بأن هذا الإجراء يعني حذف حسابك من قاعدة بياناتنا فقط، ولا يعني حذف اشتراكك. ستحتاج إلى زيارة حساب Apple الخاص بك وحذف اشتراك Private Internet Access الخاص بك من هناك. خلافاً لذلك، ستستمر في دفع رسوم الاشتراك رغم أن حسابك لن يكون نشطاً."; +"account.delete.alert.failureMessage" = "حدث خطأ أثناء حذف حسابك. يرجى المحاولة مرة أخرى."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "تم تنشيط عنوان IP المخصّص بنجاح. سيكون متاحًا في قائمة اختيار المنطقة."; "dedicated.ip.message.expired.token" = "انتهت صلاحية الرمز. يرجى إنشاء رمز جديد من صفحة حسابك على الموقع الإلكتروني."; "dedicated.ip.message.error.token" = "انتهت صلاحية الرمز. يرجى إنشاء رمز جديد من صفحة حسابك على الموقع الإلكتروني."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "ستنتهي صلاحية عنوان IP المخصّص قريبًا. احصل على واحد جديد"; "dedicated.ip.message.token.willexpire.link" = "احصل على واحد جديد"; "dedicated.ip.message.ip.updated" = "تم تحديث عنوان IP المخصّص"; diff --git a/PIA VPN/da.lproj/Localizable.strings b/PIA VPN/da.lproj/Localizable.strings index 5190ecf99..c35153a3c 100644 --- a/PIA VPN/da.lproj/Localizable.strings +++ b/PIA VPN/da.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Månedsabonnement"; "account.subscriptions.trial" = "Prøveabonnement"; "account.unauthorized" = "Noget gik galt. Prøv at logge på igen."; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Slet konto"; +"account.delete.alert.title" = "Er du sikker?"; +"account.delete.alert.message" = "Hvis du sletter din PIA-konto, er det permanent og uigenkaldeligt. Du vil ikke kunne gendanne dine PIA-legitimationsoplysninger, når du har udført denne handling. Bemærk, at denne handling kun sletter din PIA-konto fra vores database, men IKKE sletter dit aabonnement. Du er nødt til at gå til din Apple-konto og annullere dit Private Internet Access-abonnement derfra. Ellers vil du fortsat blive faktureret, også selv om din PIA-konto ikke længere er aktiv."; +"account.delete.alert.failureMessage" = "Noget gik galt, da du slettede din konto, prøv igen senere."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Din dedikerede IP er blevet aktiveret. Den vil være tilgængelig på din liste over Valg af regioner."; "dedicated.ip.message.expired.token" = "Dit token er udløbet. Generer et nyt fra din kontoside på websiden."; "dedicated.ip.message.error.token" = "Dit token er udløbet. Generer et nyt fra din kontoside på websiden."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Din dedikerede IP udløber snart. Få en ny"; "dedicated.ip.message.token.willexpire.link" = "Få en ny"; "dedicated.ip.message.ip.updated" = "Din dedikerede IP blev opdateret"; diff --git a/PIA VPN/de.lproj/Localizable.strings b/PIA VPN/de.lproj/Localizable.strings index a97ddde8f..228b0a923 100644 --- a/PIA VPN/de.lproj/Localizable.strings +++ b/PIA VPN/de.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Monatsabo"; "account.subscriptions.trial" = "Testversion"; "account.unauthorized" = "Etwas ging schief. Bitte versuchen, erneut anzumelden"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Konto löschen"; +"account.delete.alert.title" = "Sind Sie sicher?"; +"account.delete.alert.message" = "Die Löschung Ihres PIA-Kontos ist dauerhaft und unwiderruflich. Sie können Ihre PIA-Anmeldedaten nach dieser Aktion nicht wiederherstellen. Bitte beachten Sie, dass diese Aktion nur Ihr PIA-Konto aus unserer Datenbank löscht, aber NICHT Ihr Abonnement. Sie müssen zu Ihrem Apple-Konto gehen und das Private Internet Access-Abonnement von dort aus kündigen. Anderenfalls werden Sie weiterhin belastet, auch wenn Ihr PIA-Konto nicht mehr aktiv ist."; +"account.delete.alert.failureMessage" = "Etwas schlug beim Löschen Ihres Kontos fehl. Versuchen Sie es bitte später noch einmal."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Ihre Dedizierte IP wurde erfolgreich aktiviert. Sie wird in der Auswahlliste Ihrer Region verfügbar sein."; "dedicated.ip.message.expired.token" = "Ihr Token ist abgelaufen. Bitte generieren Sie von Ihrer Konto-Seite auf der Webseite ein neues."; "dedicated.ip.message.error.token" = "Ihr Token ist abgelaufen. Bitte generieren Sie ein neues auf Ihrer Konto-Seite auf der Webseite."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Ihre dedizierte IP wird bald ablaufen. Eine neue abrufen"; "dedicated.ip.message.token.willexpire.link" = "Eine neue abrufen"; "dedicated.ip.message.ip.updated" = "Ihre dedizierte IP wurde aktualisiert"; diff --git a/PIA VPN/es-MX.lproj/Localizable.strings b/PIA VPN/es-MX.lproj/Localizable.strings index 472abb401..048ed3674 100644 --- a/PIA VPN/es-MX.lproj/Localizable.strings +++ b/PIA VPN/es-MX.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Plan mensual"; "account.subscriptions.trial" = "Plan de prueba"; "account.unauthorized" = "Se ha producido un error. Intenta volver a iniciar sesión."; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Eliminar cuenta"; +"account.delete.alert.title" = "¿Estás seguro?"; +"account.delete.alert.message" = "Eliminar tu cuenta de PIA es permanente e irreversible. Una vez hecho, no podrás recuperar tus credenciales de PIA. Ten en cuenta que esta acción solo elimina tu cuenta de PIA de nuestra base de datos, pero NO cancela tu suscripción. Deberás ir a tu cuenta de Apple y cancelar allí la suscripción a Private Internet Access. De lo contrario, se te seguirá cobrando, aunque la cuenta de PIA ya no estará activa."; +"account.delete.alert.failureMessage" = "Algo ha fallado durante la eliminación de la cuenta. Inténtalo más tarde, por favor."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Tu IP dedicada se activó correctamente. Estará disponible en tu lista de selección de región."; "dedicated.ip.message.expired.token" = "Tu token caducó. Genera uno nuevo en la página de tu cuenta en el sitio web."; "dedicated.ip.message.error.token" = "Tu token caducó. Genera uno nuevo en la página de tu cuenta en el sitio web."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Tu IP dedicada caducará pronto. Obtén una nueva."; "dedicated.ip.message.token.willexpire.link" = "Obtén una nueva"; "dedicated.ip.message.ip.updated" = "Tu IP dedicada perfil se actualizó."; diff --git a/PIA VPN/fr.lproj/Localizable.strings b/PIA VPN/fr.lproj/Localizable.strings index 287bf89b9..7c53fa66c 100644 --- a/PIA VPN/fr.lproj/Localizable.strings +++ b/PIA VPN/fr.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Forfait mensuel"; "account.subscriptions.trial" = "Forfait d'essai"; "account.unauthorized" = "Un problème est survenu. Veuillez réessayer de vous connecter"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Supprimer le compte"; +"account.delete.alert.title" = "Êtes-vous sûr ?"; +"account.delete.alert.message" = "La suppression de votre compte PIA est permanente et irréversible. Vous ne pourrez plus récupérer vos identifiants PIA après avoir effectué cette opération. Veuillez noter que cette opération supprime uniquement votre compte PIA de notre base de données, mais qu’elle ne supprime PAS votre abonnement. Vous devrez ensuite accéder à votre compte Apple et annuler l’abonnement à Private Internet Access à cet endroit. Vous risquez sinon de continuer à payer, même si votre compte PIA n’est plus actif."; +"account.delete.alert.failureMessage" = "Un problème est survenu lors de la suppression de votre compte. Veuillez réessayer plus tard."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Votre adresse IP dédiée a été activée avec succès. Elle sera disponible dans votre liste de sélection de région."; "dedicated.ip.message.expired.token" = "Votre jeton est expiré. Veuillez en générer un nouveau à partir de la page de votre compte sur le site Web."; "dedicated.ip.message.error.token" = "Votre jeton est expiré. Veuillez en générer un nouveau à partir de la page de votre compte sur le site Web."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Votre adresse IP dédiée expirera bientôt. Obtenez-en une nouvelle"; "dedicated.ip.message.token.willexpire.link" = "Obtenez-en une nouvelle"; "dedicated.ip.message.ip.updated" = "Votre IP dédiée a été mise à jour"; diff --git a/PIA VPN/it.lproj/Localizable.strings b/PIA VPN/it.lproj/Localizable.strings index f74c2389e..60e804f31 100644 --- a/PIA VPN/it.lproj/Localizable.strings +++ b/PIA VPN/it.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Piano mensile"; "account.subscriptions.trial" = "Piano di prova"; "account.unauthorized" = "Qualcosa è andato storto. Prova ad accedere di nuovo"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Elimina account"; +"account.delete.alert.title" = "Continuare?"; +"account.delete.alert.message" = "L’eliminazione del tuo account PIA è un’operazione definitiva e irreversibile, al termine della quale non potrai più recuperare le tue credenziali PIA. Tieni presente che questa azione elimina solo il tuo account PIA dal nostro database, NON l’abbonamento. Per cancellare l’abbonamento a Private Internet Access, dovrai accedere al tuo account Apple e cancellarlo da lì. Altrimenti continuerai a pagarlo anche se il tuo account PIA non sarà più attivo."; +"account.delete.alert.failureMessage" = "Qualcosa non ha funzionato durante l’eliminazione dell’account. Riprova più tardi."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Il tuo IP dedicato è stato attivato. Sarà disponibile nell'elenco di selezione del paese."; "dedicated.ip.message.expired.token" = "Token scaduto. Generane uno nuovo dalla pagina del tuo account sul sito web."; "dedicated.ip.message.error.token" = "Token scaduto. Generane uno nuovo dalla pagina del tuo account sul sito web."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Il tuo IP dedicato scade a breve. Ottieni uno nuovo"; "dedicated.ip.message.token.willexpire.link" = "Ottieni uno nuovo"; "dedicated.ip.message.ip.updated" = "Il tuo IP dedicato è aggiornato"; diff --git a/PIA VPN/ja.lproj/Localizable.strings b/PIA VPN/ja.lproj/Localizable.strings index b328b549f..ab81ea23a 100644 --- a/PIA VPN/ja.lproj/Localizable.strings +++ b/PIA VPN/ja.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "月間プラン"; "account.subscriptions.trial" = "トライアルプラン"; "account.unauthorized" = "問題が発生しました。もう一度ログインしてみてください"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "アカウントを削除"; +"account.delete.alert.title" = "本当によろしいですか?"; +"account.delete.alert.message" = "PIA アカウントの削除は永久的な操作であり、元に戻すことはできません。この操作を実行した後に、お持ちのPIA資格情報を取得することはできません。この操作ではお持ちのPIAアカウントが削除されるだけで、ご契約が削除されるわけではないことにご留意ください。お持ちのAppleアカウントにアクセスし、そこでPrivate Internet Access契約をキャンセルしない限り、お持ちのPIAアカウントが無効になった後も請求が行われます。"; +"account.delete.alert.failureMessage" = "お持ちのアカウントを削除中に問題が発生しました。後でもう一度試してください。"; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "専用IPが正常にアクティベートされました。地域選択リストで利用できるようになります。"; "dedicated.ip.message.expired.token" = "トークンの有効期限が切れています。ウェブサイトのアカウントページから新しいトークンを生成してください。"; "dedicated.ip.message.error.token" = "トークンの有効期限が切れています。ウェブサイトのアカウントページから新しいトークンを生成してください。"; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "あなたの専用IPは間もなく期限が切れます。新しい専用IPを入手してください"; "dedicated.ip.message.token.willexpire.link" = "新しい専用IPを入手してください"; "dedicated.ip.message.ip.updated" = "あなたの専用IPが更新されました"; diff --git a/PIA VPN/ko.lproj/Localizable.strings b/PIA VPN/ko.lproj/Localizable.strings index 1aa9b6a92..5531de845 100644 --- a/PIA VPN/ko.lproj/Localizable.strings +++ b/PIA VPN/ko.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "월간 플랜"; "account.subscriptions.trial" = "체험 플랜"; "account.unauthorized" = "문제가 발생했습니다. 다시 로그인해 보세요"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "계정 삭제"; +"account.delete.alert.title" = "계속하시겠습니까?"; +"account.delete.alert.message" = "PIA 계정 삭제는 영구적이고 되돌릴 수 없습니다. 이 작업을 수행한 후에는 PIA 인증 정보를 복구할 수 없습니다. 이 작업은 본사 데이터베이스에서 사용자의 PIA 계정만 삭제합니다. 사용자의 구독은 삭제되지 않습니다. 따라서 자신의 Apple 계정으로 이동해서 Private Internet Access 구독을 취소해야 합니다. 그렇지 않으면 PIA를 더 이상 사용할 수 없더라도 요금이 부과됩니다."; +"account.delete.alert.failureMessage" = "계정을 삭제하는 중 문제가 발생했습니다. 나중에 다시 시도하십시오."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "회원님의 전용 IP가 성공적으로 활성화되었습니다. 지역 선택 목록에서 사용하실 수 있습니다."; "dedicated.ip.message.expired.token" = "회원님의 토큰은 만료되었습니다. 웹사이트의 계정 페이지에서 새로운 토큰을 생성하세요."; "dedicated.ip.message.error.token" = "회원님의 토큰은 만료되었습니다. 웹사이트의 계정 페이지에서 새로운 토큰을 생성하세요."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "회원님의 전용 IP가 곧 만료됩니다. 새로 획득하세요"; "dedicated.ip.message.token.willexpire.link" = "새로 획득하세요"; "dedicated.ip.message.ip.updated" = "회원님의 전용 IP가 업데이트되었습니다"; diff --git a/PIA VPN/nb.lproj/Localizable.strings b/PIA VPN/nb.lproj/Localizable.strings index ed4416b06..5d586a676 100644 --- a/PIA VPN/nb.lproj/Localizable.strings +++ b/PIA VPN/nb.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Månedsplan"; "account.subscriptions.trial" = "Prøveplan"; "account.unauthorized" = "Noe gikk galt. Prøv å logge inn igjen."; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Slett konto"; +"account.delete.alert.title" = "Er du sikker?"; +"account.delete.alert.message" = "Sletting av PIA-kontoen er permanent og kan ikke reverseres. Du vil ikke kunne hente PIA-legitimasjonen etter at denne handlingen er utført. Vær oppmerksom på at denne handlingen bare sletter PIA-kontoen fra databasen, og sletter IKKE abonnementet. Du må gå til Apple-kontoen og kansellere abonnementet på Private Internet Access derfra. Ellers blir du fortsatt belastet, selv om PIA-kontoen ikke lenger er aktiv."; +"account.delete.alert.failureMessage" = "Noe gikk galt ved sletting av kontoen. Prøv på nytt senere."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Den dedikerte IP-en din ble aktivert. Den blir tilgjengelig i regionvalg-listen."; "dedicated.ip.message.expired.token" = "Tokenet er utløpt. Generer et nytt et fra kontosiden din på nettstedet."; "dedicated.ip.message.error.token" = "Tokenet er utløpt. Generer et nytt et fra kontosiden din på nettstedet."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Den dedikerte IP-en din utløper snart. Skaff deg en ny en"; "dedicated.ip.message.token.willexpire.link" = "Skaff deg en ny en"; "dedicated.ip.message.ip.updated" = "Den dedikerte IP-en din ble oppdatert"; diff --git a/PIA VPN/nl.lproj/Localizable.strings b/PIA VPN/nl.lproj/Localizable.strings index 20d904003..25937b02d 100644 --- a/PIA VPN/nl.lproj/Localizable.strings +++ b/PIA VPN/nl.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Maandelijks abonnement"; "account.subscriptions.trial" = "Proefabonnement"; "account.unauthorized" = "Er is iets fout gegaan. Probeer opnieuw in te loggen."; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Verwijder account"; +"account.delete.alert.title" = "Weet u het zeker?"; +"account.delete.alert.message" = "Het verwijderen van uw PIA-account is definitief en onomkeerbaar. U zult uw PIA-inloggegevens niet kunnen ophalen na het uitvoeren van deze actie. Houd ermee rekening dat deze actie alleen uw PIA-account verwijdert uit onze database, maar NIET uw abonnement. Hiervoor moet u naar uw Apple-account gaan en daar het Private Internet Access-abonnement annuleren. Anders worden u nog steeds kosten aangerekend, hoewel uw PIA-account niet langer actief is."; +"account.delete.alert.failureMessage" = "Er ging iets fout bij het verwijderen van uw account. Probeer het later nog een keer."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Uw dedicated IP is geactiveerd. Het is beschikbaar in uw regioselectielijst."; "dedicated.ip.message.expired.token" = "Uw code is verlopen. Genereer een nieuwe via uw accountpagina op de website."; "dedicated.ip.message.error.token" = "Uw code is verlopen. Genereer een nieuwe via uw accountpagina op de website."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Uw dedicated IP verloopt binnenkort. Haal een nieuwe"; "dedicated.ip.message.token.willexpire.link" = "Haal een nieuwe"; "dedicated.ip.message.ip.updated" = "Uw dedicated IP is bijgewerkt"; diff --git a/PIA VPN/pl.lproj/Localizable.strings b/PIA VPN/pl.lproj/Localizable.strings index 0866f2e5a..8b19f7c2c 100644 --- a/PIA VPN/pl.lproj/Localizable.strings +++ b/PIA VPN/pl.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Plan miesięczny"; "account.subscriptions.trial" = "Plan próbny"; "account.unauthorized" = "Coś poszło nie tak. zalogować się ponownie."; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Usuń konto"; +"account.delete.alert.title" = "Czy na pewno?"; +"account.delete.alert.message" = "Usunięcie Twojego konta PIA będzie trwałe i nieodwracalne. Po tej akcji nie będziesz mógł pobrać swoich danych logowania PIA. Ta akcja powoduje tylko usunięcie konta PIA z naszej bazy danych, ale NIE powoduje usunięcia subskrypcji. W tym celu należy przejść do swojego konta Apple i anulować tam subskrypcję Private Internet Access. W przeciwnym razie nadal będą pobierane opłaty, mimo że konto PIA nie będzie już aktywne."; +"account.delete.alert.failureMessage" = "Coś poszło nie tak podczas usuwania konta, spróbuj jeszcze raz później."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Twój dedykowany adres IP został aktywowany. Będzie dostępny na liście wyboru regionu."; "dedicated.ip.message.expired.token" = "Twój token stracił ważność. Wygeneruj nowy na stronie swojego konta w serwisie."; "dedicated.ip.message.error.token" = "Twój token stracił ważność. Wygeneruj nowy na stronie swojego konta w serwisie."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Twój dedykowany adres IP wkrótce wygasa. Kup nowy adres."; "dedicated.ip.message.token.willexpire.link" = "Kup nowy adres."; "dedicated.ip.message.ip.updated" = "Zaktualizowano Twój dedykowany adres IP"; diff --git a/PIA VPN/pt-BR.lproj/Localizable.strings b/PIA VPN/pt-BR.lproj/Localizable.strings index fc79a486e..d27f6aa3d 100644 --- a/PIA VPN/pt-BR.lproj/Localizable.strings +++ b/PIA VPN/pt-BR.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Plano mensal"; "account.subscriptions.trial" = "Plano de avaliação"; "account.unauthorized" = "Ocorreu um erro. Tente fazer login novamente."; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Excluir conta"; +"account.delete.alert.title" = "Tem certeza?"; +"account.delete.alert.message" = "A exclusão da sua conta da PIA é permanente e irreversível. Você não poderá recuperar suas credenciais da PIA após executar essa ação. Observe que essa ação exclui apenas sua conta da PIA do nosso banco de dados, mas NÃO exclui sua assinatura. Você precisará ir até sua conta da Apple e cancelar a assinatura da Private Internet Access de lá. Caso contrário, você ainda será cobrado, mesmo que sua conta da PIA não esteja mais ativa."; +"account.delete.alert.failureMessage" = "Algo deu errado ao excluir sua conta. Tente de novo mais tarde."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "O seu IP dedicado foi ativado com sucesso. Ele estará disponível em sua lista de seleção de regiões."; "dedicated.ip.message.expired.token" = "Seu token expirou. Gere um novo na página da sua conta no site."; "dedicated.ip.message.error.token" = "Seu token expirou. Gere um novo na página da sua conta no site."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "O seu IP dedicado irá expirar em breve. Obtenha um novo"; "dedicated.ip.message.token.willexpire.link" = "Obtenha um novo"; "dedicated.ip.message.ip.updated" = "O seu IP dedicado foi atualizado"; diff --git a/PIA VPN/ru.lproj/Localizable.strings b/PIA VPN/ru.lproj/Localizable.strings index 0a0df92ab..f85b194e6 100644 --- a/PIA VPN/ru.lproj/Localizable.strings +++ b/PIA VPN/ru.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "Месячный план"; "account.subscriptions.trial" = "Пробный план"; "account.unauthorized" = "Что-то пошло не так. Попробуйте выполнить вход еще раз"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "Удалить аккаунт"; +"account.delete.alert.title" = "Вы уверены?"; +"account.delete.alert.message" = "Удаление аккаунта PIA окончательно и необратимо. Выполнив это действие, вы не сможете восстановить свои учетные данные PIA. Обратите внимание, что это действие приведет только к удалению аккаунта из нашей базы данных, Но НЕ к удалению подписки. Вам потребуется перейти в аккаунт Apple и отменить подписку на Private Internet Access. В противном случае плата будет начисляться даже в отсутствие активного аккаунта PIA."; +"account.delete.alert.failureMessage" = "При удалении аккаунта что-то пошло не так. Повторите попытку позже."; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "Ваш выделенный IP-адрес успешно активирован. Теперь он будет доступен в списке выбора региона."; "dedicated.ip.message.expired.token" = "Срок действия вашего токена истек. Сгенерируйте новый на странице своей учетной записи на нашем веб-сайте."; "dedicated.ip.message.error.token" = "Срок действия вашего токена истек. Сгенерируйте новый на странице своей учетной записи на нашем веб-сайте."; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "Действие вашего выделенного IP-адреса скоро заканчивается. Получите новый адрес"; "dedicated.ip.message.token.willexpire.link" = "Получите новый адрес"; "dedicated.ip.message.ip.updated" = "Ваш выделенный IP-адрес обновлен"; diff --git a/PIA VPN/th.lproj/Localizable.strings b/PIA VPN/th.lproj/Localizable.strings index 266b7c0b6..d353f52af 100644 --- a/PIA VPN/th.lproj/Localizable.strings +++ b/PIA VPN/th.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "แผนรายเดือน"; "account.subscriptions.trial" = "แผนทดลองใช้"; "account.unauthorized" = "บางอย่างผิดปกติ โปรดลองลงชื่อเข้าใช้อีกครั้ง"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "ลบบัญชี"; +"account.delete.alert.title" = "คุณแน่ใจหรือไม่"; +"account.delete.alert.message" = "การลบบัญชี PIA ของคุณจะเป็นการถาวรและไม่สามารถย้อนกลับได้ คุณจะไม่สามารถดึงข้อมูลประจำตัว PIA ของคุณหลังจากดำเนินการนี้ โปรดทราบว่าการดำเนินการนี้จะลบบัญชี PIA ของคุณจากฐานข้อมูลของเราเท่านั้น แต่จะไม่ลบการสมัครใช้งานของคุณ คุณจะต้องไปที่บัญชี Apple ของคุณแล้วยกเลิกการสมัครใช้งาน Private Internet Access จากที่นั่น ไม่เช่นนั้นคุณจะยังคงถูกเรียกเก็บค่าบริการแม้ว่าบัญชี PIA ของคุณจะไม่เปิดใช้งานอีกต่อไป"; +"account.delete.alert.failureMessage" = "เกิดข้อผิดพลาดขณะลบบัญชีของคุณ โปรดลองอีกครั้งในภายหลัง"; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "IP เฉพาะของคุณเปิดใช้งานสำเร็จแล้ว จะมีอยู่ในรายการการเลือกภูมิภาคของคุณ"; "dedicated.ip.message.expired.token" = "โทเค็นของคุณหมดอายุแล้ว โปรดสร้างใหม่จากหน้าบัญชีของคุณบนเว็บไซต์"; "dedicated.ip.message.error.token" = "โทเค็นของคุณหมดอายุแล้ว โปรดสร้างใหม่จากหน้าบัญชีของคุณบนเว็บไซต์"; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "IP เฉพาะของคุณจะหมดอายุในไม่ช้า รับใหม่"; "dedicated.ip.message.token.willexpire.link" = "รับใหม่"; "dedicated.ip.message.ip.updated" = "อัปเดต IP เฉพาะของคุณแล้ว"; diff --git a/PIA VPN/zh-Hans.lproj/Localizable.strings b/PIA VPN/zh-Hans.lproj/Localizable.strings index af9cca3a7..4fcf13d76 100644 --- a/PIA VPN/zh-Hans.lproj/Localizable.strings +++ b/PIA VPN/zh-Hans.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "包月套餐"; "account.subscriptions.trial" = "试用套餐"; "account.unauthorized" = "出现问题。请尝试重新登录"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "删除账户"; +"account.delete.alert.title" = "确定?"; +"account.delete.alert.message" = "删除 PIA 账户是永久的、不可挽回的。执行此操作后,您将无法检索自己的 PIA 登录信息。请注意,此操作只会从我们的数据库中删除 PIA 帐户,但不会删除您的订阅。您需要进入苹果账户,从那里取消 Private Internet Access。否则,即使您的 PIA 账户不再有效,您仍将被收取费用。"; +"account.delete.alert.failureMessage" = "删除账户出错,请稍后再试。"; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "您的专用 IP 已成功激活。您可以在“地区”选择列表中使用该专用 IP。"; "dedicated.ip.message.expired.token" = "您的令牌已过期。请从网站的“账户”页面中生成一个新的令牌。"; "dedicated.ip.message.error.token" = "您的令牌已过期。请从网站的“账户”页面中生成一个新的令牌。"; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "您的专用 IP 即将过期。请获取一个新的专用 IP"; "dedicated.ip.message.token.willexpire.link" = "请获取一个新的专用 IP"; "dedicated.ip.message.ip.updated" = "您的专用 IP 已更新"; diff --git a/PIA VPN/zh-Hant.lproj/Localizable.strings b/PIA VPN/zh-Hant.lproj/Localizable.strings index 80fe87c0d..c596113dd 100644 --- a/PIA VPN/zh-Hant.lproj/Localizable.strings +++ b/PIA VPN/zh-Hant.lproj/Localizable.strings @@ -109,10 +109,12 @@ "account.subscriptions.monthly" = "每月方案"; "account.subscriptions.trial" = "試用方案"; "account.unauthorized" = "發生錯誤,請稍後再嘗試登入。"; -"account.delete" = "Delete Account"; -"account.delete.alert.title" = "Are you sure?"; -"account.delete.alert.message" = "Deleting your PIA account is permanent and irreversible. You will not be able to retrieve your PIA credentials after performing this action. Please note that this action only deletes your PIA account from our database, but it does NOT delete your subscription. You will need to go to your Apple account and cancel the Private Internet Access subscription from there. Otherwise, you will still be charged, even though your PIA account will no longer be active."; -"account.delete.alert.failureMessage" = "Something went wrong while deleting your account, please try again later."; +"account.delete" = "刪除帳戶"; +"account.delete.alert.title" = "是否確定?"; +"account.delete.alert.message" = "PIA 帳戶一經刪除即無法還原。一旦執行這個動作,您會無法取回 PIA 憑證。請注意,這個動作只會在資料庫中刪除您的 PIA 帳戶,不會刪除訂購計劃。您必須前往 Apple 帳戶,在那裡取消 Private Internet Access 訂購計劃,否則就算 PIA 帳戶已經停用,您還是會遭到扣款。"; +"account.delete.alert.failureMessage" = "刪除帳戶時發生錯誤,請再試一次。"; +"account.survey.message" = "Want to help make PIA better? Let us know how we can improve!\nTake The Survey"; +"account.survey.messageLink" = "Take The Survey"; // SETTINGS @@ -395,6 +397,7 @@ "dedicated.ip.message.valid.token" = "您的專屬 IP 已成功啟用。它將會出現在區域選擇清單中。"; "dedicated.ip.message.expired.token" = "您的權杖已到期。請到網站的帳戶頁面產生新權杖。"; "dedicated.ip.message.error.token" = "您的權杖已到期。請到網站的帳戶頁面產生新權杖。"; +"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; "dedicated.ip.message.token.willexpire" = "您的專屬 IP 即將到期。取得新權杖"; "dedicated.ip.message.token.willexpire.link" = "取得新權杖"; "dedicated.ip.message.ip.updated" = "您的專屬 IP 已更新"; From 86520ba27107cf7939642b54a0e3e747c1ae2082 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 15 Mar 2022 10:12:55 +0100 Subject: [PATCH 045/159] Update commit sha for PIALibrary --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 5079b43ed..c3bbac995 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('2b430fce') + library_by_gitlab_by_git('0af17412') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index f4d2903c1..1fe153db6 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2b430fce`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 2b430fce + :commit: 0af17412 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 2b430fce + :commit: 0af17412 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 24686ecce7e3733cd5f30e1aa0e7ce0d618b15bb +PODFILE CHECKSUM: 7a3acc76abbc1b04d06c2c9b1646f0ed8ef15584 COCOAPODS: 1.11.2 From 77fd8e57a42737cb32bbd49be08de9a8a79519f1 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 22 Mar 2022 18:23:29 +0100 Subject: [PATCH 046/159] Add deviceType and DNS info in user setting for CSI (wip) --- PIA VPN/AppPreferences.swift | 30 +++++++++++++++++++++++++++- PIA VPN/DNSList.swift | 17 ++++++++++++++++ PIA VPN/SettingsViewController.swift | 14 +++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 1db7ad708..0b39610cd 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -25,6 +25,7 @@ import PIALibrary import TunnelKit import SwiftyBeaver import Intents +import UIKit private let log = SwiftyBeaver.self @@ -34,6 +35,8 @@ class AppPreferences { static let appVersion = "AppVersion" + static let deviceType = "deviceType" + static let version = "Version" static let launched = "Launched" // discard 2.2 key and invert logic @@ -51,7 +54,8 @@ class AppPreferences { static let useSmallPackets = "UseSmallPackets" static let wireGuardUseSmallPackets = "WireGuardUseSmallPackets" static let ikeV2UseSmallPackets = "IKEV2UseSmallPackets" - + static let usesCustomDNS = "usesCustomDNS" + static let favoriteServerIdentifiersGen4_deprecated = "FavoriteServerIdentifiersGen4" static let regionFilter = "RegionFilter" @@ -117,6 +121,15 @@ class AppPreferences { private var isTransitioningTheme = false + private var deviceType: String { + get { + return defaults.string(forKey: Entries.deviceType) ?? UIDevice.current.type.rawValue + } + set { + defaults.set(newValue, forKey: Entries.deviceType) + } + } + var wasLaunched: Bool { get { return defaults.bool(forKey: Entries.launched) @@ -312,6 +325,15 @@ class AppPreferences { } } + var usesCustomDNS: Bool { + get { + return defaults.bool(forKey: Entries.usesCustomDNS) + } + set { + defaults.set(newValue, forKey: Entries.usesCustomDNS) + } + } + var dedicatedTokenIPReleation: [String: String] { get { let keychain = PIALibrary.Keychain(team: AppConstants.teamId, group: AppConstants.appGroup) @@ -549,6 +571,7 @@ class AppPreferences { defaults.register(defaults: [ Entries.version: AppPreferences.currentVersion, + Entries.deviceType: UIDevice.current.type.rawValue, Entries.appVersion: "", Entries.launched: false, Entries.regionFilter: RegionFilter.latency.rawValue, @@ -568,6 +591,7 @@ class AppPreferences { Entries.useSmallPackets: false, Entries.wireGuardUseSmallPackets: true, Entries.ikeV2UseSmallPackets: true, + Entries.usesCustomDNS: false, Entries.canAskAgainForReview: false, Entries.successConnections: 0, Entries.failureConnections: 0, @@ -775,6 +799,7 @@ class AppPreferences { } func reset() { + deviceType = "" piaHandshake = .rsa4096 piaSocketType = nil favoriteServerIdentifiersGen4 = [] @@ -794,6 +819,7 @@ class AppPreferences { quickSettingPrivateBrowserVisible = true useSmallPackets = false ikeV2UseSmallPackets = true + usesCustomDNS = false wireGuardUseSmallPackets = true todayWidgetVpnProtocol = IKEv2Profile.vpnType todayWidgetVpnPort = "500" @@ -812,6 +838,7 @@ class AppPreferences { } func clean() { + deviceType = "" piaHandshake = .rsa4096 piaSocketType = nil favoriteServerIdentifiersGen4 = [] @@ -831,6 +858,7 @@ class AppPreferences { quickSettingPrivateBrowserVisible = true useSmallPackets = false ikeV2UseSmallPackets = true + usesCustomDNS = false wireGuardUseSmallPackets = true let preferences = Client.preferences.editable().reset() preferences.commit() diff --git a/PIA VPN/DNSList.swift b/PIA VPN/DNSList.swift index 683a976e1..dad2f78de 100644 --- a/PIA VPN/DNSList.swift +++ b/PIA VPN/DNSList.swift @@ -21,6 +21,7 @@ // import Foundation +import PIALibrary class DNSList: NSObject { @@ -157,6 +158,22 @@ class DNSList: NSObject { return L10n.Settings.Dns.custom } + /// Return if a custom DNS is set for given protocol and its configured DNS servers + func hasCustomDNS(for vpnType: String, in dnsServers: [String]) -> Bool { + if vpnType == IKEv2Profile.vpnType || dnsServers.isEmpty { + return false + } + + for dns in self.dnsList { + for (_, ipsList) in dns { + if dnsServers == ipsList { + return true + } + } + } + return false + } + /// Updates the content of the dnsList object into the plist private func updatePlist() { (self.dnsList as NSArray).write(toFile: self.plistPathInDocument, diff --git a/PIA VPN/SettingsViewController.swift b/PIA VPN/SettingsViewController.swift index df82a1a1b..09ddb58a8 100644 --- a/PIA VPN/SettingsViewController.swift +++ b/PIA VPN/SettingsViewController.swift @@ -208,6 +208,7 @@ class SettingsViewController: AutolayoutViewController, SettingsDelegate { } @objc func refreshSettings() { + updateCustomDNSAppPreferences() tableView.reloadData() } @@ -464,6 +465,19 @@ class SettingsViewController: AutolayoutViewController, SettingsDelegate { reportUpdatedPreferences() } + private func updateCustomDNSAppPreferences() { + var dnsServers = pendingOpenVPNConfiguration.dnsServers + if pendingPreferences.vpnType == PIAWGTunnelProfile.vpnType { + dnsServers = pendingWireguardVPNConfiguration.customDNSServers + } + + if let dnsServers = dnsServers { + AppPreferences.shared.usesCustomDNS = DNSList.shared.hasCustomDNS(for: pendingPreferences.vpnType, in: dnsServers) + } else { + AppPreferences.shared.usesCustomDNS = false + } + } + // MARK: ModalController override func dismissModal() { From fae26a51b681e5ae674762e70df36aedc67b2a18 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 23 Mar 2022 12:37:05 +0100 Subject: [PATCH 047/159] Update PIALibrary commit sha to avoid conflicts on closing release --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index c3bbac995..57276ab3b 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('0af17412') + library_by_gitlab_by_git('0f5ff9c8') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 1fe153db6..ca95f85c6 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0af17412`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0af17412 + :commit: 0f5ff9c8 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0af17412 + :commit: 0f5ff9c8 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 7a3acc76abbc1b04d06c2c9b1646f0ed8ef15584 +PODFILE CHECKSUM: d8894acba9d47865115152a0b98105811e8fe517 COCOAPODS: 1.11.2 From 8ca6448c33b0aa576307ba194ac245d8de612b28 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 23 Mar 2022 13:45:01 +0100 Subject: [PATCH 048/159] Update checksum --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index ca95f85c6..8dacb0348 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: d8894acba9d47865115152a0b98105811e8fe517 +PODFILE CHECKSUM: 86aaaea244abb8519015abd6aa7d9799d66862e8 COCOAPODS: 1.11.2 From 22d3f34da21d364829de4c3f58d5071062cfa2c7 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 23 Mar 2022 14:59:20 +0100 Subject: [PATCH 049/159] Update integration with CSI V1.0.2 --- Podfile | 2 +- Podfile.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Podfile b/Podfile index 488d65799..59aab4f1f 100644 --- a/Podfile +++ b/Podfile @@ -75,7 +75,7 @@ def shared_main_pods #pod "PIARegionsModule", :git => "#{$git_root}/#{$regions_repo}" pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.1' #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" - pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :commit => 'b62d1bab' + pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :branch => 'release/1.0.2' pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' #library_by_path('~/Repositories') diff --git a/Podfile.lock b/Podfile.lock index a547db24a..22c88d5e5 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -70,11 +70,11 @@ PODS: - PIAAccountModule/gradle (= 1.2.1) - PIAAccountModule/account (1.2.1) - PIAAccountModule/gradle (1.2.1) - - PIACSIModule (1.0.1): - - PIACSIModule/csi (= 1.0.1) - - PIACSIModule/gradle (= 1.0.1) - - PIACSIModule/csi (1.0.1) - - PIACSIModule/gradle (1.0.1) + - PIACSIModule (1.0.2): + - PIACSIModule/csi (= 1.0.2) + - PIACSIModule/gradle (= 1.0.2) + - PIACSIModule/csi (1.0.2) + - PIACSIModule/gradle (1.0.2) - PIAKPIModule (1.1.0): - PIAKPIModule/gradle (= 1.1.0) - PIAKPIModule/kpi (= 1.1.0) @@ -158,7 +158,7 @@ DEPENDENCIES: - GradientProgressBar (~> 2.0) - OpenSSL-Apple (from `https://github.com/keeshux/openssl-apple`) - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" + - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.0.2`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" @@ -211,7 +211,7 @@ EXTERNAL SOURCES: :commit: 697fd4f :git: "git@gitlab.kape.com:pia-mobile/shared/account.git" PIACSIModule: - :commit: b62d1bab + :branch: release/1.0.2 :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: :branch: release/1.1.0 @@ -237,7 +237,7 @@ CHECKOUT OPTIONS: :commit: 697fd4f :git: "git@gitlab.kape.com:pia-mobile/shared/account.git" PIACSIModule: - :commit: b62d1bab + :commit: 73b766fbb07c351016287f674f857b01d575ecd7 :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 @@ -276,7 +276,7 @@ SPEC CHECKSUMS: nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd OpenSSL-Apple: bb7c9715a259404de040f5359ed3b3170cedf8d6 PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 - PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea + PIACSIModule: 8ee47a1037bb7f3fbe61a0d70df29ca538e77edb PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d PIALibrary: d52e06ca2995dd5692dcadb678475acbb0000cd2 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 52320f6c9dd74ff82124a1b0d2f44076ce9e7795 +PODFILE CHECKSUM: 0e9260a8ed9262efb6c860ac3f1b58e56a903e35 COCOAPODS: 1.11.2 From 489b77af3717677ae504105159e0035f143f4b0f Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 28 Mar 2022 18:41:56 +0200 Subject: [PATCH 050/159] Fix setting's delegate reference for updating DNS info in app preferences --- PIA VPN/DNSList.swift | 2 +- PIA VPN/ProtocolSettingsViewController.swift | 1 + PIA VPN/Settings/SettingPopoverSelectionView.swift | 1 + PIA VPN/SettingsViewController.swift | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/PIA VPN/DNSList.swift b/PIA VPN/DNSList.swift index dad2f78de..eeb75d689 100644 --- a/PIA VPN/DNSList.swift +++ b/PIA VPN/DNSList.swift @@ -160,7 +160,7 @@ class DNSList: NSObject { /// Return if a custom DNS is set for given protocol and its configured DNS servers func hasCustomDNS(for vpnType: String, in dnsServers: [String]) -> Bool { - if vpnType == IKEv2Profile.vpnType || dnsServers.isEmpty { + guard vpnType != IKEv2Profile.vpnType && !dnsServers.isEmpty else { return false } diff --git a/PIA VPN/ProtocolSettingsViewController.swift b/PIA VPN/ProtocolSettingsViewController.swift index c6821e6dc..d0b165d14 100644 --- a/PIA VPN/ProtocolSettingsViewController.swift +++ b/PIA VPN/ProtocolSettingsViewController.swift @@ -98,6 +98,7 @@ class ProtocolSettingsViewController: PIABaseSettingsViewController { let height = heightForOptions(options) //Default height * 3 for 3 protocols let optionsView = ProtocolPopoverSelectionView(frame: CGRect(x: 0, y: 0, width: Int(width), height: height)) optionsView.pendingPreferences = self.pendingPreferences + optionsView.settingsDelegate = self.settingsDelegate optionsView.currentPopover = protocolPopover optionsView.protocols = options protocolPopover.show(optionsView, fromView: sender) diff --git a/PIA VPN/Settings/SettingPopoverSelectionView.swift b/PIA VPN/Settings/SettingPopoverSelectionView.swift index 1734bd0eb..0cdbed0b6 100644 --- a/PIA VPN/Settings/SettingPopoverSelectionView.swift +++ b/PIA VPN/Settings/SettingPopoverSelectionView.swift @@ -110,6 +110,7 @@ extension ProtocolPopoverSelectionView: UITableViewDelegate, UITableViewDataSour let vpnType = protocols[indexPath.row] pendingPreferences.vpnType = vpnType + settingsDelegate.updateSetting(ProtocolsSections.protocolSelection, withValue: nil) Macros.postNotification(.PIASettingsHaveChanged) Macros.postNotification(.RefreshSettings) diff --git a/PIA VPN/SettingsViewController.swift b/PIA VPN/SettingsViewController.swift index 09ddb58a8..bdf1812dd 100644 --- a/PIA VPN/SettingsViewController.swift +++ b/PIA VPN/SettingsViewController.swift @@ -208,7 +208,6 @@ class SettingsViewController: AutolayoutViewController, SettingsDelegate { } @objc func refreshSettings() { - updateCustomDNSAppPreferences() tableView.reloadData() } @@ -461,6 +460,7 @@ class SettingsViewController: AutolayoutViewController, SettingsDelegate { } } + updateCustomDNSAppPreferences() refreshSettings() reportUpdatedPreferences() } From 4ca5c3704dd045a16840c656f4c544e87b43f3a8 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 28 Mar 2022 19:42:17 +0200 Subject: [PATCH 051/159] Update pod file commit sha --- Podfile | 2 +- Podfile.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Podfile b/Podfile index 59aab4f1f..fcf17c745 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('c786f8a8') + library_by_gitlab_by_git('0c61ec35') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 22c88d5e5..22d5a97f0 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -80,9 +80,9 @@ PODS: - PIAKPIModule/kpi (= 1.1.0) - PIAKPIModule/gradle (1.1.0) - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.14.0): + - PIALibrary/Core (2.15.0): - PIAAccountModule - - PIALibrary/Library (2.14.0): + - PIALibrary/Library (2.15.0): - Alamofire (~> 4) - Gloss (~> 2) - PIAAccountModule @@ -94,18 +94,18 @@ PODS: - PopupDialog - ReachabilitySwift - SwiftyBeaver - - PIALibrary/Mock (2.14.0): + - PIALibrary/Mock (2.15.0): - PIALibrary/Library - - PIALibrary/UI (2.14.0): + - PIALibrary/UI (2.15.0): - FXPageControl - lottie-ios - PIALibrary/Library - SwiftEntryKit (= 0.7.2) - SwiftyBeaver - TPKeyboardAvoiding - - PIALibrary/Util (2.14.0): + - PIALibrary/Util (2.15.0): - PIALibrary/Core - - PIALibrary/VPN (2.14.0): + - PIALibrary/VPN (2.15.0): - PIALibrary/Library - PIAWireguard - TunnelKit @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.0.2`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c786f8a8`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: c786f8a8 + :commit: 0c61ec35 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: c786f8a8 + :commit: 0c61ec35 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -278,7 +278,7 @@ SPEC CHECKSUMS: PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 8ee47a1037bb7f3fbe61a0d70df29ca538e77edb PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: d52e06ca2995dd5692dcadb678475acbb0000cd2 + PIALibrary: 4ac21b0958f5f77186297c302df26ae99fb27fb7 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 0e9260a8ed9262efb6c860ac3f1b58e56a903e35 +PODFILE CHECKSUM: 4c618c01a05701d1f5f79575478e29a794bf9688 COCOAPODS: 1.11.2 From ebd1466164ed95a68777190d10b427d73b2e2578 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 28 Mar 2022 19:45:50 +0200 Subject: [PATCH 052/159] Refactor code for device type --- PIA VPN/AppPreferences.swift | 6 ++---- PIA VPN/Bootstrapper.swift | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 0b39610cd..78c49cec0 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -25,7 +25,6 @@ import PIALibrary import TunnelKit import SwiftyBeaver import Intents -import UIKit private let log = SwiftyBeaver.self @@ -121,9 +120,9 @@ class AppPreferences { private var isTransitioningTheme = false - private var deviceType: String { + var deviceType: String { get { - return defaults.string(forKey: Entries.deviceType) ?? UIDevice.current.type.rawValue + return defaults.string(forKey: Entries.deviceType) ?? "" } set { defaults.set(newValue, forKey: Entries.deviceType) @@ -571,7 +570,6 @@ class AppPreferences { defaults.register(defaults: [ Entries.version: AppPreferences.currentVersion, - Entries.deviceType: UIDevice.current.type.rawValue, Entries.appVersion: "", Entries.launched: false, Entries.regionFilter: RegionFilter.latency.rawValue, diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index a76e6337a..6d54b80ff 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -230,7 +230,7 @@ class Bootstrapper { if AppPreferences.shared.checksDipExpirationRequest, let dipToken = Client.providers.serverProvider.dipTokens?.first { Client.providers.serverProvider.handleDIPTokenExpiration(dipToken: dipToken, nil) } - + AppPreferences.shared.deviceType = UIDevice.current.type.rawValue } private func setDefaultPlanProducts() { From 7f8ff77e4ed601a9be31967718e520bb5fbb6bf9 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 5 Apr 2022 14:35:28 +0200 Subject: [PATCH 053/159] Bump PIALibrary to 2.16.0 commit sha --- Podfile | 2 +- Podfile.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Podfile b/Podfile index 5d1a2d0cf..b105c9fe2 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('0f5ff9c8') + library_by_gitlab_by_git('3e7c4629') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 8dacb0348..afe681e9b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -80,9 +80,9 @@ PODS: - PIAKPIModule/kpi (= 1.1.0) - PIAKPIModule/gradle (1.1.0) - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.15.0): + - PIALibrary/Core (2.16.0): - PIAAccountModule - - PIALibrary/Library (2.15.0): + - PIALibrary/Library (2.16.0): - Alamofire (~> 4) - Gloss (~> 2) - PIAAccountModule @@ -94,18 +94,18 @@ PODS: - PopupDialog - ReachabilitySwift - SwiftyBeaver - - PIALibrary/Mock (2.15.0): + - PIALibrary/Mock (2.16.0): - PIALibrary/Library - - PIALibrary/UI (2.15.0): + - PIALibrary/UI (2.16.0): - FXPageControl - lottie-ios - PIALibrary/Library - SwiftEntryKit (= 0.7.2) - SwiftyBeaver - TPKeyboardAvoiding - - PIALibrary/Util (2.15.0): + - PIALibrary/Util (2.16.0): - PIALibrary/Core - - PIALibrary/VPN (2.15.0): + - PIALibrary/VPN (2.16.0): - PIALibrary/Library - PIAWireguard - TunnelKit @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0f5ff9c8 + :commit: 3e7c4629 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0f5ff9c8 + :commit: 3e7c4629 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -278,7 +278,7 @@ SPEC CHECKSUMS: PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: 4ac21b0958f5f77186297c302df26ae99fb27fb7 + PIALibrary: f13d65b1508c8782092de9c6f11e7328dbe913b4 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 86aaaea244abb8519015abd6aa7d9799d66862e8 +PODFILE CHECKSUM: 96c7e6a761fdb3ebd6432f211eef501d0774f55f COCOAPODS: 1.11.2 From 9d72ec30484a075b2fc2fa8fa20fdfb29979104a Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 5 Apr 2022 14:37:33 +0200 Subject: [PATCH 054/159] Bump version to 3.16.0 --- PIA VPN.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 4b2803338..45f165b15 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2611,7 +2611,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2642,7 +2642,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2681,7 +2681,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2723,7 +2723,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2817,7 +2817,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2850,7 +2850,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2996,7 +2996,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -3033,7 +3033,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3135,7 +3135,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3173,7 +3173,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3206,7 +3206,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3242,7 +3242,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.15.0; + MARKETING_VERSION = 3.16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 6f1e15b6ccb983e4020cd754cf18b32eb4061b0f Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 12 Apr 2022 13:11:31 +0200 Subject: [PATCH 055/159] Convert rating alert UI to use default iOS UX widgets --- PIA VPN/RatingManager.swift | 63 ++++++++++++++++++++++------ PIA VPN/SwiftGen+Strings.swift | 16 ++++++- PIA VPN/en.lproj/Localizable.strings | 6 ++- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 77707b317..0251172de 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -49,7 +49,7 @@ class RatingManager { if AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt { log.debug("Show rating") - reviewApp() + showDefaultAlertForAppReview() } else if AppPreferences.shared.canAskAgainForReview { let now = Date() @@ -72,7 +72,15 @@ class RatingManager { AppPreferences.shared.failureConnections += 1 } } - + + private func ratingAlertCancelHandler() { + log.debug("No review but maybe we can try in the future") + AppPreferences.shared.canAskAgainForReview = true + if AppPreferences.shared.lastRatingRejection == nil { + AppPreferences.shared.lastRatingRejection = Date() + } + } + private func openRatingViewInAppstore() { let urlStr = AppConstants.Reviews.appReviewUrl @@ -92,7 +100,40 @@ class RatingManager { SKStoreReviewController.requestReview() } - private func reviewApp() { + // MARK: Default Alerts + + private func showDefaultAlertForAppReview() { + guard let rootView = AppDelegate.delegate().topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) else { + return + } + + let sheet = Macros.alertController(L10n.Rating.Enjoy.question, nil) + sheet.addAction(UIAlertAction(title: L10n.Rating.Alert.Button.notreally, style: .default, handler: { action in + // Ask for feedback + let alert = self.createCustomFeedbackDialog() + rootView.present(alert, animated: true, completion: nil) + })) + sheet.addAction(UIAlertAction(title: L10n.Global.yes, style: .default, handler: { action in + let alert = self.createDefaultReviewAlert() + rootView.present(alert, animated: true, completion: nil) + })) + rootView.present(sheet, animated: true, completion: nil) + } + + private func createDefaultReviewAlert() -> UIAlertController { + let sheet = Macros.alertController(L10n.Rating.Rate.question, nil) + sheet.addAction(UIAlertAction(title: L10n.Rating.Alert.Button.nothanks, style: .default, handler: { action in + self.ratingAlertCancelHandler() + })) + sheet.addAction(UIAlertAction(title: L10n.Rating.Alert.Button.oksure, style: .default, handler: { action in + self.openRatingViewInAppstore() + })) + return sheet + } + + // MARK: Custom Alerts + + private func showCustomAlertForAppReview() { guard let rootView = AppDelegate.delegate().topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) else { return @@ -104,12 +145,12 @@ class RatingManager { ) sheet.addCancelActionWithTitle(L10n.Global.no, handler: { // Ask for feedback - let alert = self.feedback() + let alert = self.createCustomFeedbackDialog() rootView.present(alert, animated: true, completion: nil) }) sheet.addActionWithTitle(L10n.Global.yes) { - let alert = self.askForReview() + let alert = self.createCustomReviewDialog() rootView.present(alert, animated: true, completion: nil) } @@ -117,7 +158,7 @@ class RatingManager { } - private func feedback() -> PopupDialog { + private func createCustomFeedbackDialog() -> PopupDialog { let sheet = Macros.alert( L10n.Rating.Problems.question, @@ -135,18 +176,14 @@ class RatingManager { } - private func askForReview() -> PopupDialog { + private func createCustomReviewDialog() -> PopupDialog { let sheet = Macros.alert( - L10n.Rating.Rate.question, + L10n.Rating.Review.question, L10n.Rating.Rate.subtitle ) sheet.addCancelActionWithTitle(L10n.Global.no, handler: { - log.debug("No review but maybe we can try in the future") - AppPreferences.shared.canAskAgainForReview = true - if AppPreferences.shared.lastRatingRejection == nil { - AppPreferences.shared.lastRatingRejection = Date() - } + self.ratingAlertCancelHandler() }) sheet.addActionWithTitle(L10n.Global.yes) { diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index 8cdcd42ff..6932bb76f 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -640,6 +640,16 @@ internal enum L10n { } internal enum Rating { + internal enum Alert { + internal enum Button { + /// No, thanks. + internal static let nothanks = L10n.tr("Localizable", "rating.alert.button.nothanks") + /// Not Really + internal static let notreally = L10n.tr("Localizable", "rating.alert.button.notreally") + /// Ok, sure! + internal static let oksure = L10n.tr("Localizable", "rating.alert.button.oksure") + } + } internal enum Enjoy { /// Are you enjoying PIA VPN? internal static let question = L10n.tr("Localizable", "rating.enjoy.question") @@ -663,11 +673,15 @@ internal enum L10n { internal static let subtitle = L10n.tr("Localizable", "rating.problems.subtitle") } internal enum Rate { - /// How about an AppStore review? + /// How about a rating on the AppStore? internal static let question = L10n.tr("Localizable", "rating.rate.question") /// We appreciate you sharing your experience internal static let subtitle = L10n.tr("Localizable", "rating.rate.subtitle") } + internal enum Review { + /// How about an AppStore review? + internal static let question = L10n.tr("Localizable", "rating.review.question") + } } internal enum Region { diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index f145f2b91..248e79a07 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "We hope our VPN product is meeting your expectations"; "rating.problems.question" = "What went wrong?"; "rating.problems.subtitle" = "Do you want to give feedback? We can help you to improve your experience using PIA"; -"rating.rate.question" = "How about an AppStore review?"; +"rating.review.question" = "How about an AppStore review?"; +"rating.rate.question" = "How about a rating on the AppStore?"; "rating.rate.subtitle" = "We appreciate you sharing your experience"; "rating.error.question" = "The connection couldn't be established"; "rating.error.subtitle" = "You can try selecting a different region or letting us know about it by opening a support ticket."; "rating.error.button.send" = "Send feedback"; +"rating.alert.button.notreally" = "Not Really"; +"rating.alert.button.nothanks" = "No, thanks."; +"rating.alert.button.oksure" = "Ok, sure!"; // CALLING CARDS "card.wireguard.title" = "Try WireGuard® today!"; From 55c115b454548ed163b40178da4ab10b22b62df9 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 12 Apr 2022 21:40:49 +0200 Subject: [PATCH 056/159] Bump commit sha for feature flag --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 5d1a2d0cf..1af8ea7bf 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('0f5ff9c8') + library_by_gitlab_by_git('492fb2e8') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 8dacb0348..ab1bca96a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0f5ff9c8`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0f5ff9c8 + :commit: 492fb2e8 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0f5ff9c8 + :commit: 492fb2e8 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 86aaaea244abb8519015abd6aa7d9799d66862e8 +PODFILE CHECKSUM: a68219970696edfa0c5dad2d627ce2f619aef18d COCOAPODS: 1.11.2 From c27fd5926394187ade8ef09e4e49d7775ee58c7f Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 12 Apr 2022 21:51:45 +0200 Subject: [PATCH 057/159] Add control logic for showing default UX of rating alert --- PIA VPN/AppConfiguration.swift | 2 +- PIA VPN/AppPreferences.swift | 16 ++++++++++++++ PIA VPN/Bootstrapper.swift | 15 +++++++++----- PIA VPN/RatingManager.swift | 38 +++++++++++++++++++++++++++++----- 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/PIA VPN/AppConfiguration.swift b/PIA VPN/AppConfiguration.swift index 3e6ce758a..7df23036f 100644 --- a/PIA VPN/AppConfiguration.swift +++ b/PIA VPN/AppConfiguration.swift @@ -123,7 +123,7 @@ struct AppConfiguration { } struct Rating { - + static let successfulDisconnectionsUntilPrompt: Int = 2 static let successConnectionsUntilPrompt: Int = 3 static let successConnectionsUntilPromptAgain: Int = 50 static let errorInConnectionsUntilPrompt: Int = 1 diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 1db7ad708..9373f31ff 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -80,6 +80,7 @@ class AppPreferences { static let failureConnections = "failureConnections" static let canAskAgainForReview = "canAskAgainForReview" static let lastRatingRejection = "lastRatingRejection" + static let successDisconnections = "successDisconnections" // GEO servers static let showGeoServers = "ShowGeoServers" @@ -442,6 +443,15 @@ class AppPreferences { } } + var successDisconnections: Int { + get { + return defaults.integer(forKey: Entries.successDisconnections) + } + set { + defaults.set(newValue, forKey: Entries.successDisconnections) + } + } + var appVersion: String? { get { return defaults.string(forKey: Entries.appVersion) @@ -569,6 +579,7 @@ class AppPreferences { Entries.wireGuardUseSmallPackets: true, Entries.ikeV2UseSmallPackets: true, Entries.canAskAgainForReview: false, + Entries.successDisconnections: 0, Entries.successConnections: 0, Entries.failureConnections: 0, Entries.showGeoServers: true, @@ -800,6 +811,7 @@ class AppPreferences { todayWidgetVpnSocket = "UDP" todayWidgetTrustedNetwork = false Client.resetServers(completionBlock: {_ in }) + successDisconnections = 0 successConnections = 0 failureConnections = 0 showGeoServers = true @@ -835,6 +847,7 @@ class AppPreferences { let preferences = Client.preferences.editable().reset() preferences.commit() Client.resetServers(completionBlock: {_ in }) + successDisconnections = 0 successConnections = 0 failureConnections = 0 showGeoServers = true @@ -893,4 +906,7 @@ class AppPreferences { successConnections += 1 } + func incrementSuccessDisconnections() { + successDisconnections += 1 + } } diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index a76e6337a..20c0d2293 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -256,12 +256,17 @@ class Bootstrapper { } @objc private func vpnStatusDidChange(notification: Notification) { - guard (Client.providers.vpnProvider.vpnStatus == .connected) else { - return + let vpnStatus = Client.providers.vpnProvider.vpnStatus + switch vpnStatus { + case .connected: + AppPreferences.shared.incrementSuccessConnections() + UserSurveyManager.shared.handleConnectionSuccess() + case .disconnected: + AppPreferences.shared.incrementSuccessDisconnections() + default: + break } - AppPreferences.shared.incrementSuccessConnections() - RatingManager.shared.handleConnectionSuccess() - UserSurveyManager.shared.handleConnectionSuccess() + RatingManager.shared.handleConnectionStatusChanged() } @objc private func internetReachable(notification: Notification) { diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 0251172de..c52cf7d94 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -33,23 +33,44 @@ class RatingManager { static let shared = RatingManager() + private var successfulDisconnectionsUntilPrompt: Int private var successConnectionsUntilPrompt: Int private var successConnectionsUntilPromptAgain: Int private var timeIntervalUntilPromptAgain: Double private var errorInConnectionsUntilPrompt: Int init() { + self.successfulDisconnectionsUntilPrompt = AppConfiguration.Rating.successfulDisconnectionsUntilPrompt self.successConnectionsUntilPrompt = AppConfiguration.Rating.successConnectionsUntilPrompt self.successConnectionsUntilPromptAgain = AppConfiguration.Rating.successConnectionsUntilPromptAgain self.errorInConnectionsUntilPrompt = AppConfiguration.Rating.errorInConnectionsUntilPrompt self.timeIntervalUntilPromptAgain = AppConfiguration.Rating.timeIntervalUntilPromptAgain } - func handleConnectionSuccess() { - - if AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt { + func handleConnectionStatusChanged() { + // By default do not use custom alert + // and compare should be: when vpn is disconnected + var useCustomAlert: Bool = false + var vpnComparisonState: VPNStatus = .disconnected + if Client.configuration.featureFlags.contains(Client.FeatureFlags.useCustomRatingPopup) { + useCustomAlert = true + vpnComparisonState = .connected + } + + if Client.providers.vpnProvider.vpnStatus == vpnComparisonState { + showAppReviewWith(customPopup: useCustomAlert) + } + } + + private func showAppReviewWith(customPopup useCustomDialog: Bool) { + let requirementMetForRatingAlert = useCustomDialog ? connectionCondition() : disonnectionCondition() + if requirementMetForRatingAlert { log.debug("Show rating") - showDefaultAlertForAppReview() + if useCustomDialog { + showCustomAlertForAppReview() + } else { + showDefaultAlertForAppReview() + } } else if AppPreferences.shared.canAskAgainForReview { let now = Date() @@ -60,7 +81,14 @@ class RatingManager { reviewAppWithoutPrompt() } } - + } + + private func disonnectionCondition() -> Bool { + return AppPreferences.shared.successDisconnections == self.successfulDisconnectionsUntilPrompt + } + + private func connectionCondition() -> Bool { + return AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt } func handleConnectionError() { From dc161c35a4f4c1507fe59579519c569a5148c53a Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 19 Apr 2022 09:50:46 +0200 Subject: [PATCH 058/159] Code cleaning --- PIA VPN/AppConfiguration.swift | 2 +- PIA VPN/RatingManager.swift | 54 +++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/PIA VPN/AppConfiguration.swift b/PIA VPN/AppConfiguration.swift index 7df23036f..39f7881d6 100644 --- a/PIA VPN/AppConfiguration.swift +++ b/PIA VPN/AppConfiguration.swift @@ -123,7 +123,7 @@ struct AppConfiguration { } struct Rating { - static let successfulDisconnectionsUntilPrompt: Int = 2 + static let successDisconnectionsUntilPrompt: Int = 2 static let successConnectionsUntilPrompt: Int = 3 static let successConnectionsUntilPromptAgain: Int = 50 static let errorInConnectionsUntilPrompt: Int = 1 diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index c52cf7d94..61b330293 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -33,14 +33,26 @@ class RatingManager { static let shared = RatingManager() - private var successfulDisconnectionsUntilPrompt: Int + private var successDisconnectionsUntilPrompt: Int private var successConnectionsUntilPrompt: Int private var successConnectionsUntilPromptAgain: Int private var timeIntervalUntilPromptAgain: Double private var errorInConnectionsUntilPrompt: Int + private var isRatingFlagAvailable: Bool { + return Client.configuration.featureFlags.contains(Client.FeatureFlags.useCustomRatingPopup) + } + + private var disconnectionCondition: Bool { + return AppPreferences.shared.successDisconnections == self.successDisconnectionsUntilPrompt + } + + private var connectionCondition: Bool { + return AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt + } + init() { - self.successfulDisconnectionsUntilPrompt = AppConfiguration.Rating.successfulDisconnectionsUntilPrompt + self.successDisconnectionsUntilPrompt = AppConfiguration.Rating.successDisconnectionsUntilPrompt self.successConnectionsUntilPrompt = AppConfiguration.Rating.successConnectionsUntilPrompt self.successConnectionsUntilPromptAgain = AppConfiguration.Rating.successConnectionsUntilPromptAgain self.errorInConnectionsUntilPrompt = AppConfiguration.Rating.errorInConnectionsUntilPrompt @@ -49,22 +61,15 @@ class RatingManager { func handleConnectionStatusChanged() { // By default do not use custom alert - // and compare should be: when vpn is disconnected - var useCustomAlert: Bool = false - var vpnComparisonState: VPNStatus = .disconnected - if Client.configuration.featureFlags.contains(Client.FeatureFlags.useCustomRatingPopup) { - useCustomAlert = true - vpnComparisonState = .connected - } - - if Client.providers.vpnProvider.vpnStatus == vpnComparisonState { - showAppReviewWith(customPopup: useCustomAlert) + // and comparison should be: when vpn is disconnected + if Client.providers.vpnProvider.vpnStatus == (isRatingFlagAvailable ? .connected : .disconnected) { + showAppReviewWith(customPopup: isRatingFlagAvailable) } } private func showAppReviewWith(customPopup useCustomDialog: Bool) { - let requirementMetForRatingAlert = useCustomDialog ? connectionCondition() : disonnectionCondition() - if requirementMetForRatingAlert { + let shouldShowRatingAlert = useCustomDialog ? connectionCondition : disconnectionCondition + if shouldShowRatingAlert { log.debug("Show rating") if useCustomDialog { showCustomAlertForAppReview() @@ -83,14 +88,6 @@ class RatingManager { } } - private func disonnectionCondition() -> Bool { - return AppPreferences.shared.successDisconnections == self.successfulDisconnectionsUntilPrompt - } - - private func connectionCondition() -> Bool { - return AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt - } - func handleConnectionError() { if Client.daemons.isNetworkReachable { if AppPreferences.shared.failureConnections == self.errorInConnectionsUntilPrompt { @@ -138,7 +135,7 @@ class RatingManager { let sheet = Macros.alertController(L10n.Rating.Enjoy.question, nil) sheet.addAction(UIAlertAction(title: L10n.Rating.Alert.Button.notreally, style: .default, handler: { action in // Ask for feedback - let alert = self.createCustomFeedbackDialog() + let alert = self.createDefaultFeedbackDialog() rootView.present(alert, animated: true, completion: nil) })) sheet.addAction(UIAlertAction(title: L10n.Global.yes, style: .default, handler: { action in @@ -148,6 +145,17 @@ class RatingManager { rootView.present(sheet, animated: true, completion: nil) } + private func createDefaultFeedbackDialog() -> UIAlertController { + let sheet = Macros.alertController(L10n.Rating.Problems.question, L10n.Rating.Problems.subtitle) + sheet.addAction(UIAlertAction(title: L10n.Global.no, style: .default, handler: { action in + log.debug("No feedback") + })) + sheet.addAction(UIAlertAction(title: L10n.Global.yes, style: .default, handler: { action in + self.openFeedbackWebsite() + })) + return sheet + } + private func createDefaultReviewAlert() -> UIAlertController { let sheet = Macros.alertController(L10n.Rating.Rate.question, nil) sheet.addAction(UIAlertAction(title: L10n.Rating.Alert.Button.nothanks, style: .default, handler: { action in From 36ea4f5b0fba3019cc8d3e8164781a79d682fe8a Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 21 Apr 2022 13:29:42 +0200 Subject: [PATCH 059/159] Bump the version to 3.16.1 --- PIA VPN.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 45f165b15..9739e5cd1 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2611,7 +2611,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2642,7 +2642,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2681,7 +2681,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2723,7 +2723,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2817,7 +2817,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2850,7 +2850,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2996,7 +2996,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -3033,7 +3033,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3135,7 +3135,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3173,7 +3173,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3206,7 +3206,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3242,7 +3242,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.0; + MARKETING_VERSION = 3.16.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 46b26ecd0964fe8b85f431dd8cbcb3dc8154b970 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 21 Apr 2022 13:36:53 +0200 Subject: [PATCH 060/159] Bump commit sha for token migration hotfix --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index b105c9fe2..dec7196af 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('3e7c4629') + library_by_gitlab_by_git('2a43d303') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index afe681e9b..b2f7f8676 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `3e7c4629`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 3e7c4629 + :commit: 2a43d303 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 3e7c4629 + :commit: 2a43d303 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 96c7e6a761fdb3ebd6432f211eef501d0774f55f +PODFILE CHECKSUM: 03973b2d86eab7fb029c027c94f8157826e62002 COCOAPODS: 1.11.2 From 79b76232761898f185bb98fb1c841990173e2e78 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 21 Apr 2022 14:17:38 +0200 Subject: [PATCH 061/159] Flag rename and bump PIALibrary commit sha --- PIA VPN/RatingManager.swift | 2 +- Podfile | 2 +- Podfile.lock | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 61b330293..9b5027373 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -40,7 +40,7 @@ class RatingManager { private var errorInConnectionsUntilPrompt: Int private var isRatingFlagAvailable: Bool { - return Client.configuration.featureFlags.contains(Client.FeatureFlags.useCustomRatingPopup) + return Client.configuration.featureFlags.contains(Client.FeatureFlags.disableSystemRatingDialog) } private var disconnectionCondition: Bool { diff --git a/Podfile b/Podfile index 1af8ea7bf..ab9ed56d0 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('492fb2e8') + library_by_gitlab_by_git('8b859104') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index ab1bca96a..4ce4ccfa4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `492fb2e8`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 492fb2e8 + :commit: 8b859104 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 492fb2e8 + :commit: 8b859104 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: a68219970696edfa0c5dad2d627ce2f619aef18d +PODFILE CHECKSUM: fd10c28e08744bad02dffcf52dac82184a95b5d8 COCOAPODS: 1.11.2 From e31d1c36ed7d349724afbfa5d7dab102bdee8892 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 26 Apr 2022 15:57:42 +0200 Subject: [PATCH 062/159] Code refactor --- PIA VPN/AppDelegate.swift | 4 ++++ PIA VPN/RatingManager.swift | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/PIA VPN/AppDelegate.swift b/PIA VPN/AppDelegate.swift index 1f0145230..c4a2b8882 100644 --- a/PIA VPN/AppDelegate.swift +++ b/PIA VPN/AppDelegate.swift @@ -312,4 +312,8 @@ extension AppDelegate { return UIApplication.shared.delegate as! AppDelegate } + class func getRootViewController() -> UIViewController? { + return AppDelegate.delegate().topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) + } + } diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 9b5027373..db0a6a365 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -43,11 +43,11 @@ class RatingManager { return Client.configuration.featureFlags.contains(Client.FeatureFlags.disableSystemRatingDialog) } - private var disconnectionCondition: Bool { + private var targetDisconnectionsReachedForPrompt: Bool { return AppPreferences.shared.successDisconnections == self.successDisconnectionsUntilPrompt } - private var connectionCondition: Bool { + private var targetConnectionsReachedForPrompt: Bool { return AppPreferences.shared.successConnections == self.successConnectionsUntilPrompt } @@ -68,13 +68,13 @@ class RatingManager { } private func showAppReviewWith(customPopup useCustomDialog: Bool) { - let shouldShowRatingAlert = useCustomDialog ? connectionCondition : disconnectionCondition + let shouldShowRatingAlert = useCustomDialog ? targetConnectionsReachedForPrompt : targetDisconnectionsReachedForPrompt if shouldShowRatingAlert { log.debug("Show rating") if useCustomDialog { - showCustomAlertForAppReview() + showCustomAlertForReview() } else { - showDefaultAlertForAppReview() + showDefaultAlertForReview() } } else if AppPreferences.shared.canAskAgainForReview { let now = Date() @@ -127,8 +127,8 @@ class RatingManager { // MARK: Default Alerts - private func showDefaultAlertForAppReview() { - guard let rootView = AppDelegate.delegate().topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) else { + private func showDefaultAlertForReview() { + guard let rootView = AppDelegate.getRootViewController() else { return } @@ -169,9 +169,9 @@ class RatingManager { // MARK: Custom Alerts - private func showCustomAlertForAppReview() { + private func showCustomAlertForReview() { - guard let rootView = AppDelegate.delegate().topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) else { + guard let rootView = AppDelegate.getRootViewController() else { return } @@ -232,7 +232,7 @@ class RatingManager { private func askForConnectionIssuesFeedback() { - guard let rootView = AppDelegate.delegate().topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) else { + guard let rootView = AppDelegate.getRootViewController() else { return } From e23a54fb5df056af82cce49a28e70f2dec4b75a7 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 26 Apr 2022 16:01:47 +0200 Subject: [PATCH 063/159] Add translations for rating alert and DIP retry message --- PIA VPN/ar.lproj/Localizable.strings | 8 ++++++-- PIA VPN/da.lproj/Localizable.strings | 8 ++++++-- PIA VPN/de.lproj/Localizable.strings | 8 ++++++-- PIA VPN/es-MX.lproj/Localizable.strings | 8 ++++++-- PIA VPN/fr.lproj/Localizable.strings | 8 ++++++-- PIA VPN/it.lproj/Localizable.strings | 8 ++++++-- PIA VPN/ja.lproj/Localizable.strings | 8 ++++++-- PIA VPN/ko.lproj/Localizable.strings | 8 ++++++-- PIA VPN/nb.lproj/Localizable.strings | 8 ++++++-- PIA VPN/nl.lproj/Localizable.strings | 8 ++++++-- PIA VPN/pl.lproj/Localizable.strings | 8 ++++++-- PIA VPN/pt-BR.lproj/Localizable.strings | 8 ++++++-- PIA VPN/ru.lproj/Localizable.strings | 8 ++++++-- PIA VPN/th.lproj/Localizable.strings | 8 ++++++-- PIA VPN/zh-Hans.lproj/Localizable.strings | 8 ++++++-- PIA VPN/zh-Hant.lproj/Localizable.strings | 8 ++++++-- 16 files changed, 96 insertions(+), 32 deletions(-) diff --git a/PIA VPN/ar.lproj/Localizable.strings b/PIA VPN/ar.lproj/Localizable.strings index b8211a1eb..969fb18dc 100644 --- a/PIA VPN/ar.lproj/Localizable.strings +++ b/PIA VPN/ar.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "نأمل أن يلبي منتجنا لشبكات VPN توقعاتك"; "rating.problems.question" = "ما الخلل الذي حصل؟"; "rating.problems.subtitle" = "هل لديك أي ملاحظات؟ يمكننا مساعدتك على تحسين تجربتك في استخدام PIA"; -"rating.rate.question" = "ما رأيك بترك مراجعة في AppStore؟"; +"rating.review.question" = "ما رأيك بترك مراجعة في AppStore؟"; +"rating.rate.question" = "ماذا عن ترك تقييم في App Store؟"; "rating.rate.subtitle" = "نقدّر لك مشاركة تجربتك"; "rating.error.question" = "تعذّر تأسيس اتصال"; "rating.error.subtitle" = "يمكنك محاولة تحديد منطقة مختلفة أو إعلامنا بها عن طريق فتح تذكرة دعم."; "rating.error.button.send" = "إرسال ملاحظات"; +"rating.alert.button.notreally" = "لا أرغب"; +"rating.alert.button.nothanks" = "لا، شكرًا."; +"rating.alert.button.oksure" = "بالتأكيد"; // CALLING CARDS "card.wireguard.title" = "جرّب ®WireGuard اليوم!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "تم تنشيط عنوان IP المخصّص بنجاح. سيكون متاحًا في قائمة اختيار المنطقة."; "dedicated.ip.message.expired.token" = "انتهت صلاحية الرمز. يرجى إنشاء رمز جديد من صفحة حسابك على الموقع الإلكتروني."; "dedicated.ip.message.error.token" = "انتهت صلاحية الرمز. يرجى إنشاء رمز جديد من صفحة حسابك على الموقع الإلكتروني."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "عدد طلبات تفعيل التوكن كبير جداً. يرجى إعادة المحاولة بعد %@ ثانية."; "dedicated.ip.message.token.willexpire" = "ستنتهي صلاحية عنوان IP المخصّص قريبًا. احصل على واحد جديد"; "dedicated.ip.message.token.willexpire.link" = "احصل على واحد جديد"; "dedicated.ip.message.ip.updated" = "تم تحديث عنوان IP المخصّص"; diff --git a/PIA VPN/da.lproj/Localizable.strings b/PIA VPN/da.lproj/Localizable.strings index c35153a3c..1e53c5174 100644 --- a/PIA VPN/da.lproj/Localizable.strings +++ b/PIA VPN/da.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Vi håber, at vores VPN lever op til dine forventninger."; "rating.problems.question" = "Hvad gik galt?"; "rating.problems.subtitle" = "Vil du give noget feedback? Vi kan hjælpe dig med at forbedre din oplevelse med PIA"; -"rating.rate.question" = "Hvad med en AppStore-anmeldelse?"; +"rating.review.question" = "Hvad med en AppStore-anmeldelse?"; +"rating.rate.question" = "Hvad med en vurdering i AppStore?"; "rating.rate.subtitle" = "Vi sætter pris på, at du deler din oplevelse"; "rating.error.question" = "Forbindelsen kunne ikke oprettes"; "rating.error.subtitle" = "Du kan prøve at vælge en anden region, eller fortælle os om det, ved at åbne en supportbillet."; "rating.error.button.send" = "Send feedback"; +"rating.alert.button.notreally" = "Nej, helst ikke"; +"rating.alert.button.nothanks" = "Nej tak."; +"rating.alert.button.oksure" = "OK, selvfølgelig!"; // CALLING CARDS "card.wireguard.title" = "Prøv WireGuard® i dag!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Din dedikerede IP er blevet aktiveret. Den vil være tilgængelig på din liste over Valg af regioner."; "dedicated.ip.message.expired.token" = "Dit token er udløbet. Generer et nyt fra din kontoside på websiden."; "dedicated.ip.message.error.token" = "Dit token er udløbet. Generer et nyt fra din kontoside på websiden."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "For mange mislykkede anmodninger om aktivering af token. Prøv igen efter %@ sekund(er)."; "dedicated.ip.message.token.willexpire" = "Din dedikerede IP udløber snart. Få en ny"; "dedicated.ip.message.token.willexpire.link" = "Få en ny"; "dedicated.ip.message.ip.updated" = "Din dedikerede IP blev opdateret"; diff --git a/PIA VPN/de.lproj/Localizable.strings b/PIA VPN/de.lproj/Localizable.strings index 228b0a923..3ad30822c 100644 --- a/PIA VPN/de.lproj/Localizable.strings +++ b/PIA VPN/de.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Wir hoffen, dass unsere VPN-Lösung Ihre Erwartungen erfüllt."; "rating.problems.question" = "Was ist schiefgelaufen?"; "rating.problems.subtitle" = "Möchten Sie Feedback geben? Wir können Ihnen helfen, Ihre Erfahrungen mit PIA zu verbessern"; -"rating.rate.question" = "Wie wäre es mit einer Bewertung im AppStore?"; +"rating.review.question" = "Wie wäre es mit einer Rezension im App Store?"; +"rating.rate.question" = "Wie wäre es mit einer Bewertung im App Store?"; "rating.rate.subtitle" = "Wir wissen es zu schätzen, dass Sie uns Ihre Erfahrungen mitteilen"; "rating.error.question" = "Die Verbindung konnte nicht hergestellt werden"; "rating.error.subtitle" = "Sie können versuchen, eine andere Region auszuwählen oder uns darüber zu informieren, indem Sie ein Support-Ticket öffnen."; "rating.error.button.send" = "Feedback senden"; +"rating.alert.button.notreally" = "Nicht wirklich"; +"rating.alert.button.nothanks" = "Nein danke."; +"rating.alert.button.oksure" = "Okay, sicher!"; // CALLING CARDS "card.wireguard.title" = "WireGuard® noch heute testen!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Ihre Dedizierte IP wurde erfolgreich aktiviert. Sie wird in der Auswahlliste Ihrer Region verfügbar sein."; "dedicated.ip.message.expired.token" = "Ihr Token ist abgelaufen. Bitte generieren Sie von Ihrer Konto-Seite auf der Webseite ein neues."; "dedicated.ip.message.error.token" = "Ihr Token ist abgelaufen. Bitte generieren Sie ein neues auf Ihrer Konto-Seite auf der Webseite."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Zu viele fehlgeschlagene Token-Aktivierungsanforderungen. Bitte versuche es nach %@ Sekunde(n) erneut."; "dedicated.ip.message.token.willexpire" = "Ihre dedizierte IP wird bald ablaufen. Eine neue abrufen"; "dedicated.ip.message.token.willexpire.link" = "Eine neue abrufen"; "dedicated.ip.message.ip.updated" = "Ihre dedizierte IP wurde aktualisiert"; diff --git a/PIA VPN/es-MX.lproj/Localizable.strings b/PIA VPN/es-MX.lproj/Localizable.strings index 048ed3674..f55860ac2 100644 --- a/PIA VPN/es-MX.lproj/Localizable.strings +++ b/PIA VPN/es-MX.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Esperamos que nuestro producto de VPN cumpla tus expectativas."; "rating.problems.question" = "¿Qué ha salido mal?"; "rating.problems.subtitle" = "¿Quieres darnos tu opinión? Podemos ayudarte a mejorar tu experiencia con PIA."; -"rating.rate.question" = "¿Qué tal si nos valoras en App Store?"; +"rating.review.question" = "¿Quieres escribir una reseña en el App Store?"; +"rating.rate.question" = "¿Quieres dar una puntuación en el App Store?"; "rating.rate.subtitle" = "Te agradecemos que compartas tu experiencia."; "rating.error.question" = "No se pudo establecer la conexión."; "rating.error.subtitle" = "Puedes probar a seleccionar otra región o abrir un ticket de asistencia para comentarnos el problema."; "rating.error.button.send" = "Enviar opinión"; +"rating.alert.button.notreally" = "Realmente no"; +"rating.alert.button.nothanks" = "No, gracias."; +"rating.alert.button.oksure" = "¡Claro que sí!"; // CALLING CARDS "card.wireguard.title" = "¡Prueba WireGuard® hoy!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Tu IP dedicada se activó correctamente. Estará disponible en tu lista de selección de región."; "dedicated.ip.message.expired.token" = "Tu token caducó. Genera uno nuevo en la página de tu cuenta en el sitio web."; "dedicated.ip.message.error.token" = "Tu token caducó. Genera uno nuevo en la página de tu cuenta en el sitio web."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Demasiadas solicitudes de activación de token fallidas. Vuelve a intentarlo pasados %@ segundo(s)."; "dedicated.ip.message.token.willexpire" = "Tu IP dedicada caducará pronto. Obtén una nueva."; "dedicated.ip.message.token.willexpire.link" = "Obtén una nueva"; "dedicated.ip.message.ip.updated" = "Tu IP dedicada perfil se actualizó."; diff --git a/PIA VPN/fr.lproj/Localizable.strings b/PIA VPN/fr.lproj/Localizable.strings index 7c53fa66c..ad9c5c439 100644 --- a/PIA VPN/fr.lproj/Localizable.strings +++ b/PIA VPN/fr.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Nous espérons que notre produit VPN répond à vos attentes"; "rating.problems.question" = "Quel était le problème ?"; "rating.problems.subtitle" = "Vous voulez nous envoyer un commentaire ? Nous pouvons vous aider à améliorer votre expérience sur PIA"; -"rating.rate.question" = "Et un avis sur l'App Store ?"; +"rating.review.question" = "Et un avis sur l'App Store ?"; +"rating.rate.question" = "Et une note sur l’App Store ?"; "rating.rate.subtitle" = "Nous apprécions que vous partagiez votre expérience"; "rating.error.question" = "La connexion n'a pas pu être établie"; "rating.error.subtitle" = "Vous pouvez essayer de sélectionner une autre région ou de nous en informer en ouvrant un ticket de support."; "rating.error.button.send" = "Envoyer un commentaire"; +"rating.alert.button.notreally" = "Non merci"; +"rating.alert.button.nothanks" = "Non, merci."; +"rating.alert.button.oksure" = "Bien sûr !"; // CALLING CARDS "card.wireguard.title" = "Essayez WireGuard® aujourd'hui !"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Votre adresse IP dédiée a été activée avec succès. Elle sera disponible dans votre liste de sélection de région."; "dedicated.ip.message.expired.token" = "Votre jeton est expiré. Veuillez en générer un nouveau à partir de la page de votre compte sur le site Web."; "dedicated.ip.message.error.token" = "Votre jeton est expiré. Veuillez en générer un nouveau à partir de la page de votre compte sur le site Web."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Nombre trop élevé de demandes d’activation de jeton manquées. Veuillez réessayer dans %@ seconde(s)."; "dedicated.ip.message.token.willexpire" = "Votre adresse IP dédiée expirera bientôt. Obtenez-en une nouvelle"; "dedicated.ip.message.token.willexpire.link" = "Obtenez-en une nouvelle"; "dedicated.ip.message.ip.updated" = "Votre IP dédiée a été mise à jour"; diff --git a/PIA VPN/it.lproj/Localizable.strings b/PIA VPN/it.lproj/Localizable.strings index 60e804f31..fa8d23a0c 100644 --- a/PIA VPN/it.lproj/Localizable.strings +++ b/PIA VPN/it.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Speriamo che il nostro prodotto VPN soddisfi le tue aspettative"; "rating.problems.question" = "Cosa non ha funzionato?"; "rating.problems.subtitle" = "Vuoi offrire il tuo feedback? Possiamo aiutarti a migliorare la tua esperienza d'uso di PIA"; -"rating.rate.question" = "Che ne pensi di condividere una recensione sull'AppStore?"; +"rating.review.question" = "Che ne pensi di condividere una recensione sull'AppStore?"; +"rating.rate.question" = "Che ne dici di una valutazione su App Store?"; "rating.rate.subtitle" = "Grazie per aver condiviso la tua esperienza"; "rating.error.question" = "Impossibile stabilire la connessione"; "rating.error.subtitle" = "Puoi provare a selezionare un altro paese o comunicarcelo aprendo un ticket di supporto."; "rating.error.button.send" = "Invia feedback"; +"rating.alert.button.notreally" = "No, grazie"; +"rating.alert.button.nothanks" = "No, grazie."; +"rating.alert.button.oksure" = "Certo!"; // CALLING CARDS "card.wireguard.title" = "Prova subito WireGuard®!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Il tuo IP dedicato è stato attivato. Sarà disponibile nell'elenco di selezione del paese."; "dedicated.ip.message.expired.token" = "Token scaduto. Generane uno nuovo dalla pagina del tuo account sul sito web."; "dedicated.ip.message.error.token" = "Token scaduto. Generane uno nuovo dalla pagina del tuo account sul sito web."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Troppe richieste di attivazione token non riuscite. Riprova tra %@ secondo/i."; "dedicated.ip.message.token.willexpire" = "Il tuo IP dedicato scade a breve. Ottieni uno nuovo"; "dedicated.ip.message.token.willexpire.link" = "Ottieni uno nuovo"; "dedicated.ip.message.ip.updated" = "Il tuo IP dedicato è aggiornato"; diff --git a/PIA VPN/ja.lproj/Localizable.strings b/PIA VPN/ja.lproj/Localizable.strings index ab81ea23a..468ed7676 100644 --- a/PIA VPN/ja.lproj/Localizable.strings +++ b/PIA VPN/ja.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "VPN製品にご満足いただいていることを願っています"; "rating.problems.question" = "どのような問題があったのでしょうか?"; "rating.problems.subtitle" = "フィードバックを送信しますか?PIAご利用にあたり、お客様のエクスペリエンス改善のお手伝いをいたします。"; -"rating.rate.question" = "AppStoreでレビューしていただけますか?"; +"rating.review.question" = "AppStoreでレビューしていただけますか?"; +"rating.rate.question" = "AppStoreで評価していただけますか?"; "rating.rate.subtitle" = "お客様のご感想を共有していただければありがたいです"; "rating.error.question" = "接続を確立できませんでした"; "rating.error.subtitle" = "別の地域を選択するか、サポートチケットを開いてそれについてお知らせください。"; "rating.error.button.send" = "フィードバックを送信"; +"rating.alert.button.notreally" = "いいえ"; +"rating.alert.button.nothanks" = "いいえ、結構です"; +"rating.alert.button.oksure" = "はい、評価します"; // CALLING CARDS "card.wireguard.title" = "WireGuard®を今すぐお試しください!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "専用IPが正常にアクティベートされました。地域選択リストで利用できるようになります。"; "dedicated.ip.message.expired.token" = "トークンの有効期限が切れています。ウェブサイトのアカウントページから新しいトークンを生成してください。"; "dedicated.ip.message.error.token" = "トークンの有効期限が切れています。ウェブサイトのアカウントページから新しいトークンを生成してください。"; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "失敗したトークン有効化リクエストの数が多すぎます。%@秒後にもう一度お試しください。"; "dedicated.ip.message.token.willexpire" = "あなたの専用IPは間もなく期限が切れます。新しい専用IPを入手してください"; "dedicated.ip.message.token.willexpire.link" = "新しい専用IPを入手してください"; "dedicated.ip.message.ip.updated" = "あなたの専用IPが更新されました"; diff --git a/PIA VPN/ko.lproj/Localizable.strings b/PIA VPN/ko.lproj/Localizable.strings index 5531de845..c156af612 100644 --- a/PIA VPN/ko.lproj/Localizable.strings +++ b/PIA VPN/ko.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "저희 VPN 제품이 고객님의 기대에 부응하기를 바랍니다"; "rating.problems.question" = "어떤 문제가 있었습니까?"; "rating.problems.subtitle" = "피드백을 보내 주시겠습니까? 더 나은 PIA를 경험하실 수 있도록 도와 드리겠습니다"; -"rating.rate.question" = "App Store에 리뷰를 남겨 주시겠습니까?"; +"rating.review.question" = "App Store에 리뷰를 남겨 주시겠습니까?"; +"rating.rate.question" = "App Store에서 점수를 매겨주시겠어요?"; "rating.rate.subtitle" = "고객님의 경험을 공유해 주시면 감사하겠습니다"; "rating.error.question" = "연결을 수립할 수 없습니다"; "rating.error.subtitle" = "다른 지역을 선택해 보시거나 지원 티켓을 열어서 저희에게 알려 주세요."; "rating.error.button.send" = "피드백 보내기"; +"rating.alert.button.notreally" = "아니요"; +"rating.alert.button.nothanks" = "아니요."; +"rating.alert.button.oksure" = "네, 좋아요!"; // CALLING CARDS "card.wireguard.title" = "지금 WireGuard®를 사용해 보세요!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "회원님의 전용 IP가 성공적으로 활성화되었습니다. 지역 선택 목록에서 사용하실 수 있습니다."; "dedicated.ip.message.expired.token" = "회원님의 토큰은 만료되었습니다. 웹사이트의 계정 페이지에서 새로운 토큰을 생성하세요."; "dedicated.ip.message.error.token" = "회원님의 토큰은 만료되었습니다. 웹사이트의 계정 페이지에서 새로운 토큰을 생성하세요."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "토큰 활성화 요청이 너무 많이 실패했습니다. %@초 후 다시 시도하십시오."; "dedicated.ip.message.token.willexpire" = "회원님의 전용 IP가 곧 만료됩니다. 새로 획득하세요"; "dedicated.ip.message.token.willexpire.link" = "새로 획득하세요"; "dedicated.ip.message.ip.updated" = "회원님의 전용 IP가 업데이트되었습니다"; diff --git a/PIA VPN/nb.lproj/Localizable.strings b/PIA VPN/nb.lproj/Localizable.strings index 5d586a676..22237a276 100644 --- a/PIA VPN/nb.lproj/Localizable.strings +++ b/PIA VPN/nb.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Vi håper VPN-produktet svarer til forventningene dine"; "rating.problems.question" = "Hva gikk galt?"; "rating.problems.subtitle" = "Ønsker du å sende en tilbakemelding? Vi kan bidra til å forbedre opplevelsen din med PIA"; -"rating.rate.question" = "Hva med en anmeldelse på AppStore?"; +"rating.review.question" = "Hva med en anmeldelse på AppStore?"; +"rating.rate.question" = "Hva med en vurdering i AppStore?"; "rating.rate.subtitle" = "Vi setter pris på at du deler opplevelsen din"; "rating.error.question" = "Tilkoblingen kunne ikke opprettes"; "rating.error.subtitle" = "Du kan velge en annen region eller fortelle oss om det ved å åpne en støttesak."; "rating.error.button.send" = "Send tilbakemelding"; +"rating.alert.button.notreally" = "Ikke egentlig"; +"rating.alert.button.nothanks" = "Nei takk."; +"rating.alert.button.oksure" = "Klart det!"; // CALLING CARDS "card.wireguard.title" = "Prøv WireGuard® i dag!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Den dedikerte IP-en din ble aktivert. Den blir tilgjengelig i regionvalg-listen."; "dedicated.ip.message.expired.token" = "Tokenet er utløpt. Generer et nytt et fra kontosiden din på nettstedet."; "dedicated.ip.message.error.token" = "Tokenet er utløpt. Generer et nytt et fra kontosiden din på nettstedet."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "For mange mislykkede forespørsler om aktivering av token. Prøv på nytt etter %@ sekund(er)."; "dedicated.ip.message.token.willexpire" = "Den dedikerte IP-en din utløper snart. Skaff deg en ny en"; "dedicated.ip.message.token.willexpire.link" = "Skaff deg en ny en"; "dedicated.ip.message.ip.updated" = "Den dedikerte IP-en din ble oppdatert"; diff --git a/PIA VPN/nl.lproj/Localizable.strings b/PIA VPN/nl.lproj/Localizable.strings index 25937b02d..f1f62738f 100644 --- a/PIA VPN/nl.lproj/Localizable.strings +++ b/PIA VPN/nl.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "We hopen dat ons VPN-product aan uw verwachtingen voldoet"; "rating.problems.question" = "Wat is er fout gegaan?"; "rating.problems.subtitle" = "Wilt u feedback geven? We kunnen u helpen met het verbeteren van uw ervaring met PIA."; -"rating.rate.question" = "Waarom laat u geen beoordeling achter in de AppStore?"; +"rating.review.question" = "Waarom laat u geen beoordeling achter in de AppStore?"; +"rating.rate.question" = "Wilt u een waardering geven in de App Store?"; "rating.rate.subtitle" = "We stellen het op prijs dat u uw ervaring deelt"; "rating.error.question" = "De verbinding kan niet tot stand worden gebracht"; "rating.error.subtitle" = "U kunt een andere regio selecteren of ons hiervan op de hoogte stellen door een supportticket te openen."; "rating.error.button.send" = "Feedback sturen"; +"rating.alert.button.notreally" = "Niet echt"; +"rating.alert.button.nothanks" = "Nee, bedankt."; +"rating.alert.button.oksure" = "Graag!"; // CALLING CARDS "card.wireguard.title" = "Probeer WireGuard® vandaag nog!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Uw dedicated IP is geactiveerd. Het is beschikbaar in uw regioselectielijst."; "dedicated.ip.message.expired.token" = "Uw code is verlopen. Genereer een nieuwe via uw accountpagina op de website."; "dedicated.ip.message.error.token" = "Uw code is verlopen. Genereer een nieuwe via uw accountpagina op de website."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Teveel mislukte aanvragen voor het activeren van tokens. Probeer het opnieuw na %@ seconde(n)."; "dedicated.ip.message.token.willexpire" = "Uw dedicated IP verloopt binnenkort. Haal een nieuwe"; "dedicated.ip.message.token.willexpire.link" = "Haal een nieuwe"; "dedicated.ip.message.ip.updated" = "Uw dedicated IP is bijgewerkt"; diff --git a/PIA VPN/pl.lproj/Localizable.strings b/PIA VPN/pl.lproj/Localizable.strings index 8b19f7c2c..fe3905363 100644 --- a/PIA VPN/pl.lproj/Localizable.strings +++ b/PIA VPN/pl.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Mamy nadzieję, że nasz produkt VPN spełnia Twoje oczekiwania"; "rating.problems.question" = "Co poszło źle?"; "rating.problems.subtitle" = "Czy zechcesz podzielić się z nami swoją opinią? Może pomóc i sprawić, ze będzie Ci się lepiej korzystało z PIA"; -"rating.rate.question" = "A może napiszesz recenzję w App Store?"; +"rating.review.question" = "A może napiszesz recenzję w App Store?"; +"rating.rate.question" = "A może ocenisz nas w sklepie App Store?"; "rating.rate.subtitle" = "Będziemy wdzięczni za opowiedzenie o swoich doświadczeniach"; "rating.error.question" = "Nie udało się nawiązać połączenia"; "rating.error.subtitle" = "Możesz spróbować wybrać inny region lub powiadomić nas o tym, wypełniając zgłoszenie do pomocy technicznej."; "rating.error.button.send" = "Wyślij opinię"; +"rating.alert.button.notreally" = "Nie za bardzo"; +"rating.alert.button.nothanks" = "Nie, dziękuję."; +"rating.alert.button.oksure" = "OK, jasne!"; // CALLING CARDS "card.wireguard.title" = "Wypróbuj WireGuard® dzisiaj!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Twój dedykowany adres IP został aktywowany. Będzie dostępny na liście wyboru regionu."; "dedicated.ip.message.expired.token" = "Twój token stracił ważność. Wygeneruj nowy na stronie swojego konta w serwisie."; "dedicated.ip.message.error.token" = "Twój token stracił ważność. Wygeneruj nowy na stronie swojego konta w serwisie."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Za dużo nieprawidłowych żądań aktywacji tokena. Spróbuj ponownie za %@ s."; "dedicated.ip.message.token.willexpire" = "Twój dedykowany adres IP wkrótce wygasa. Kup nowy adres."; "dedicated.ip.message.token.willexpire.link" = "Kup nowy adres."; "dedicated.ip.message.ip.updated" = "Zaktualizowano Twój dedykowany adres IP"; diff --git a/PIA VPN/pt-BR.lproj/Localizable.strings b/PIA VPN/pt-BR.lproj/Localizable.strings index d27f6aa3d..470b23c15 100644 --- a/PIA VPN/pt-BR.lproj/Localizable.strings +++ b/PIA VPN/pt-BR.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Esperamos que nosso produto de VPN atenda às suas expectativas"; "rating.problems.question" = "O que aconteceu de errado?"; "rating.problems.subtitle" = "Deseja dar seu feedback? Podemos ajudá-lo a melhorar sua experiência com o uso da PIA"; -"rating.rate.question" = "Que tal uma avaliação na App Store?"; +"rating.review.question" = "Que tal uma avaliação na App Store?"; +"rating.rate.question" = "Que tal uma classificação na App Store?"; "rating.rate.subtitle" = "Agradecemos o compartilhamento da sua experiência"; "rating.error.question" = "Não foi possível estabelecer a conexão"; "rating.error.subtitle" = "Você pode tentar selecionar uma região diferente ou nos contar o que aconteceu abrindo um tíquete de suporte."; "rating.error.button.send" = "Enviar feedback"; +"rating.alert.button.notreally" = "Não, obrigado"; +"rating.alert.button.nothanks" = "Não, obrigado."; +"rating.alert.button.oksure" = "Sim, claro!"; // CALLING CARDS "card.wireguard.title" = "Experimente o WireGuard® hoje mesmo!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "O seu IP dedicado foi ativado com sucesso. Ele estará disponível em sua lista de seleção de regiões."; "dedicated.ip.message.expired.token" = "Seu token expirou. Gere um novo na página da sua conta no site."; "dedicated.ip.message.error.token" = "Seu token expirou. Gere um novo na página da sua conta no site."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Muitas solicitações de ativação de token malsucedidas. Tente novamente após %@ segundo(s)."; "dedicated.ip.message.token.willexpire" = "O seu IP dedicado irá expirar em breve. Obtenha um novo"; "dedicated.ip.message.token.willexpire.link" = "Obtenha um novo"; "dedicated.ip.message.ip.updated" = "O seu IP dedicado foi atualizado"; diff --git a/PIA VPN/ru.lproj/Localizable.strings b/PIA VPN/ru.lproj/Localizable.strings index f85b194e6..f81c82060 100644 --- a/PIA VPN/ru.lproj/Localizable.strings +++ b/PIA VPN/ru.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "Надеемся, что наш продукт VPN соответствует вашим ожиданиям"; "rating.problems.question" = "Что пошло не так?"; "rating.problems.subtitle" = "Хотите дать отзыв? Мы сделаем все возможное, чтобы ваши впечатления от PIA стали лучше"; -"rating.rate.question" = "Как насчет отзыва в AppStore?"; +"rating.review.question" = "Как насчет отзыва в App Store?"; +"rating.rate.question" = "Как насчет оценки в App Store?"; "rating.rate.subtitle" = "Благодарим за согласие поделиться своим опытом"; "rating.error.question" = "Не удалось установить соединение"; "rating.error.subtitle" = "Попробуйте выбрать другой регион или расскажите нам, что произошло, открыв заявку в службу поддержки."; "rating.error.button.send" = "Отправить отзыв"; +"rating.alert.button.notreally" = "Не особо"; +"rating.alert.button.nothanks" = "Спасибо, не надо."; +"rating.alert.button.oksure" = "С удовольствием!"; // CALLING CARDS "card.wireguard.title" = "Опробуйте WireGuard® сегодня!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "Ваш выделенный IP-адрес успешно активирован. Теперь он будет доступен в списке выбора региона."; "dedicated.ip.message.expired.token" = "Срок действия вашего токена истек. Сгенерируйте новый на странице своей учетной записи на нашем веб-сайте."; "dedicated.ip.message.error.token" = "Срок действия вашего токена истек. Сгенерируйте новый на странице своей учетной записи на нашем веб-сайте."; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "Слишком много неудачных запросов активации токена. Повторите попытку через %@ сек."; "dedicated.ip.message.token.willexpire" = "Действие вашего выделенного IP-адреса скоро заканчивается. Получите новый адрес"; "dedicated.ip.message.token.willexpire.link" = "Получите новый адрес"; "dedicated.ip.message.ip.updated" = "Ваш выделенный IP-адрес обновлен"; diff --git a/PIA VPN/th.lproj/Localizable.strings b/PIA VPN/th.lproj/Localizable.strings index d353f52af..5ecc26642 100644 --- a/PIA VPN/th.lproj/Localizable.strings +++ b/PIA VPN/th.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "เราหวังว่าผลิตภัณฑ์ VPN ของเราจะตอบโจทย์ความต้องการของคุณ"; "rating.problems.question" = "มีอะไรผิดปกติเหรอ"; "rating.problems.subtitle" = "คุณอยากที่จะให้ข้อเสนอแนะไหม เราช่วยปรับปรุงประสบการณ์การใช้งาน PIA ของคุณได้"; -"rating.rate.question" = "รีวิว AppStore เป็นอย่างไร?"; +"rating.review.question" = "รีวิวเราใน AppStore หน่อยไหม?"; +"rating.rate.question" = "ให้คะแนนเราใน AppStore หน่อยไหม?"; "rating.rate.subtitle" = "ขอขอบคุณที่คุณช่วยแชร์ประสบการณ์"; "rating.error.question" = "ไม่สามารถสร้างการเชื่อมต่อได้"; "rating.error.subtitle" = "คุณสามารถลองเลือกภูมิภาคอื่นหรือแจ้งให้เราทราบโดยเปิดทิกเก็ตความช่วยเหลือ"; "rating.error.button.send" = "ส่งความคิดเห็น"; +"rating.alert.button.notreally" = "ไม่ดีกว่า"; +"rating.alert.button.nothanks" = "ไม่ล่ะ ขอบคุณ."; +"rating.alert.button.oksure" = "ได้เลย!"; // CALLING CARDS "card.wireguard.title" = "ลอง WireGuard® ได้แล้ววันนี้!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "IP เฉพาะของคุณเปิดใช้งานสำเร็จแล้ว จะมีอยู่ในรายการการเลือกภูมิภาคของคุณ"; "dedicated.ip.message.expired.token" = "โทเค็นของคุณหมดอายุแล้ว โปรดสร้างใหม่จากหน้าบัญชีของคุณบนเว็บไซต์"; "dedicated.ip.message.error.token" = "โทเค็นของคุณหมดอายุแล้ว โปรดสร้างใหม่จากหน้าบัญชีของคุณบนเว็บไซต์"; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "มีคำขอเปิดใช้งานโทเค็นที่ล้มเหลวมากเกินไป โปรดลองอีกครั้งหลังเวลาผ่านไป %@ วินาที"; "dedicated.ip.message.token.willexpire" = "IP เฉพาะของคุณจะหมดอายุในไม่ช้า รับใหม่"; "dedicated.ip.message.token.willexpire.link" = "รับใหม่"; "dedicated.ip.message.ip.updated" = "อัปเดต IP เฉพาะของคุณแล้ว"; diff --git a/PIA VPN/zh-Hans.lproj/Localizable.strings b/PIA VPN/zh-Hans.lproj/Localizable.strings index 4fcf13d76..93625b161 100644 --- a/PIA VPN/zh-Hans.lproj/Localizable.strings +++ b/PIA VPN/zh-Hans.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "希望我们的 VPN 产品符合您的期望"; "rating.problems.question" = "出什么问题了?"; "rating.problems.subtitle" = "您希望提供反馈意见吗?我们可以帮助您改善使用 PIA 的体验"; -"rating.rate.question" = "在 AppStore 上发表评论如何?"; +"rating.review.question" = "在 AppStore 上发表评论如何?"; +"rating.rate.question" = "在 AppStore 上评级如何?"; "rating.rate.subtitle" = "感谢您分享使用体验"; "rating.error.question" = "无法建立连接"; "rating.error.subtitle" = "您可以尝试选择其他地区,也可以提交支持请求来告知我们。"; "rating.error.button.send" = "发送反馈"; +"rating.alert.button.notreally" = "不太想"; +"rating.alert.button.nothanks" = "不用,谢谢."; +"rating.alert.button.oksure" = "好的,没问题!"; // CALLING CARDS "card.wireguard.title" = "立即试用 WireGuard®!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "您的专用 IP 已成功激活。您可以在“地区”选择列表中使用该专用 IP。"; "dedicated.ip.message.expired.token" = "您的令牌已过期。请从网站的“账户”页面中生成一个新的令牌。"; "dedicated.ip.message.error.token" = "您的令牌已过期。请从网站的“账户”页面中生成一个新的令牌。"; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "令牌激活请求失败次数太多。请在 %@ 秒后重试。"; "dedicated.ip.message.token.willexpire" = "您的专用 IP 即将过期。请获取一个新的专用 IP"; "dedicated.ip.message.token.willexpire.link" = "请获取一个新的专用 IP"; "dedicated.ip.message.ip.updated" = "您的专用 IP 已更新"; diff --git a/PIA VPN/zh-Hant.lproj/Localizable.strings b/PIA VPN/zh-Hant.lproj/Localizable.strings index c596113dd..98795d30e 100644 --- a/PIA VPN/zh-Hant.lproj/Localizable.strings +++ b/PIA VPN/zh-Hant.lproj/Localizable.strings @@ -372,11 +372,15 @@ "rating.enjoy.subtitle" = "我們希望我們的 VPN 產品符合您的期望"; "rating.problems.question" = "發生了什麼錯誤?"; "rating.problems.subtitle" = "您想提供反饋嗎?我們可以幫助您改善使用 PIA 的體驗"; -"rating.rate.question" = "可否為我們在 AppStore 評價?"; +"rating.review.question" = "可否為我們在 AppStore 評價?"; +"rating.rate.question" = "幫我們在 App Store 上評分好嗎?"; "rating.rate.subtitle" = "我們非常感謝您分享您的經驗"; "rating.error.question" = "無法建立連線"; "rating.error.subtitle" = "您可嘗試選擇不同區域,或開立支援單來通知我們。"; "rating.error.button.send" = "發送反饋"; +"rating.alert.button.notreally" = "不要"; +"rating.alert.button.nothanks" = "不了,謝謝."; +"rating.alert.button.oksure" = "沒問題!"; // CALLING CARDS "card.wireguard.title" = "立即體驗 WireGuard®!"; @@ -397,7 +401,7 @@ "dedicated.ip.message.valid.token" = "您的專屬 IP 已成功啟用。它將會出現在區域選擇清單中。"; "dedicated.ip.message.expired.token" = "您的權杖已到期。請到網站的帳戶頁面產生新權杖。"; "dedicated.ip.message.error.token" = "您的權杖已到期。請到網站的帳戶頁面產生新權杖。"; -"dedicated.ip.message.error.retryafter" = "Too many failed token activation requests. Please try again after %@ second(s)."; +"dedicated.ip.message.error.retryafter" = "權杖啟用要求失敗次數太多。請在 %@ 秒後再試一次。"; "dedicated.ip.message.token.willexpire" = "您的專屬 IP 即將到期。取得新權杖"; "dedicated.ip.message.token.willexpire.link" = "取得新權杖"; "dedicated.ip.message.ip.updated" = "您的專屬 IP 已更新"; From d09ef39309ccdfe5e09e51f7761f4ee111688c8f Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 26 Apr 2022 17:04:08 +0200 Subject: [PATCH 064/159] Bump commit sha --- Podfile | 2 +- Podfile.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Podfile b/Podfile index fcf17c745..1d0edee13 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('0c61ec35') + library_by_gitlab_by_git('14c813d9') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 22d5a97f0..ff7c50a51 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -80,9 +80,9 @@ PODS: - PIAKPIModule/kpi (= 1.1.0) - PIAKPIModule/gradle (1.1.0) - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.15.0): + - PIALibrary/Core (2.16.0): - PIAAccountModule - - PIALibrary/Library (2.15.0): + - PIALibrary/Library (2.16.0): - Alamofire (~> 4) - Gloss (~> 2) - PIAAccountModule @@ -94,18 +94,18 @@ PODS: - PopupDialog - ReachabilitySwift - SwiftyBeaver - - PIALibrary/Mock (2.15.0): + - PIALibrary/Mock (2.16.0): - PIALibrary/Library - - PIALibrary/UI (2.15.0): + - PIALibrary/UI (2.16.0): - FXPageControl - lottie-ios - PIALibrary/Library - SwiftEntryKit (= 0.7.2) - SwiftyBeaver - TPKeyboardAvoiding - - PIALibrary/Util (2.15.0): + - PIALibrary/Util (2.16.0): - PIALibrary/Core - - PIALibrary/VPN (2.15.0): + - PIALibrary/VPN (2.16.0): - PIALibrary/Library - PIAWireguard - TunnelKit @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.0.2`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0c61ec35`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `14c813d9`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `14c813d9`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `14c813d9`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `14c813d9`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0c61ec35 + :commit: 14c813d9 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0c61ec35 + :commit: 14c813d9 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -278,7 +278,7 @@ SPEC CHECKSUMS: PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 8ee47a1037bb7f3fbe61a0d70df29ca538e77edb PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: 4ac21b0958f5f77186297c302df26ae99fb27fb7 + PIALibrary: f13d65b1508c8782092de9c6f11e7328dbe913b4 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 4c618c01a05701d1f5f79575478e29a794bf9688 +PODFILE CHECKSUM: deb00fdbd7e9934b91e5f4622e036a14c31f7e3b COCOAPODS: 1.11.2 From 8da10130b655e1a1eca735f8663bf93e5189abf3 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 26 Apr 2022 18:45:34 +0200 Subject: [PATCH 065/159] Update PIALibrary commit sha --- Podfile | 2 +- Podfile.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Podfile b/Podfile index dec7196af..f383343a2 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('2a43d303') + library_by_gitlab_by_git('62092d61') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index b2f7f8676..9e5c9dd45 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -80,9 +80,9 @@ PODS: - PIAKPIModule/kpi (= 1.1.0) - PIAKPIModule/gradle (1.1.0) - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.16.0): + - PIALibrary/Core (2.16.1): - PIAAccountModule - - PIALibrary/Library (2.16.0): + - PIALibrary/Library (2.16.1): - Alamofire (~> 4) - Gloss (~> 2) - PIAAccountModule @@ -94,18 +94,18 @@ PODS: - PopupDialog - ReachabilitySwift - SwiftyBeaver - - PIALibrary/Mock (2.16.0): + - PIALibrary/Mock (2.16.1): - PIALibrary/Library - - PIALibrary/UI (2.16.0): + - PIALibrary/UI (2.16.1): - FXPageControl - lottie-ios - PIALibrary/Library - SwiftEntryKit (= 0.7.2) - SwiftyBeaver - TPKeyboardAvoiding - - PIALibrary/Util (2.16.0): + - PIALibrary/Util (2.16.1): - PIALibrary/Core - - PIALibrary/VPN (2.16.0): + - PIALibrary/VPN (2.16.1): - PIALibrary/Library - PIAWireguard - TunnelKit @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `2a43d303`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `62092d61`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `62092d61`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `62092d61`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `62092d61`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 2a43d303 + :commit: 62092d61 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 2a43d303 + :commit: 62092d61 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -278,7 +278,7 @@ SPEC CHECKSUMS: PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: f13d65b1508c8782092de9c6f11e7328dbe913b4 + PIALibrary: 75247afe6cea9f3ccf8ab2bbbf39aa37264983d0 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 03973b2d86eab7fb029c027c94f8157826e62002 +PODFILE CHECKSUM: 5bd7858608a764c58d6e1e988073ae4ddc08dba4 COCOAPODS: 1.11.2 From 7e5c8ff9511bfbb3ddf3f921e87ddf611f1f0f36 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 4 May 2022 11:07:22 +0200 Subject: [PATCH 066/159] Bump commit sha for based PIALibrary --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index ab9ed56d0..8d05ddf5b 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('8b859104') + library_by_gitlab_by_git('4152a87b') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 8604f5f9b..583fcfe76 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `8b859104`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 8b859104 + :commit: 4152a87b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 8b859104 + :commit: 4152a87b :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: fd10c28e08744bad02dffcf52dac82184a95b5d8 +PODFILE CHECKSUM: 4ddc29c4d4919c150641fb4f20c17f3dce81fa45 COCOAPODS: 1.11.2 From 3426374fc68433da33dacd4bcf11d3e4bee9beb6 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 4 May 2022 11:11:38 +0200 Subject: [PATCH 067/159] Code clean --- PIA VPN/RatingManager.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index db0a6a365..a0c61d0d4 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -98,7 +98,7 @@ class RatingManager { } } - private func ratingAlertCancelHandler() { + private func handleRatingAlertCancel() { log.debug("No review but maybe we can try in the future") AppPreferences.shared.canAskAgainForReview = true if AppPreferences.shared.lastRatingRejection == nil { @@ -159,7 +159,7 @@ class RatingManager { private func createDefaultReviewAlert() -> UIAlertController { let sheet = Macros.alertController(L10n.Rating.Rate.question, nil) sheet.addAction(UIAlertAction(title: L10n.Rating.Alert.Button.nothanks, style: .default, handler: { action in - self.ratingAlertCancelHandler() + self.handleRatingAlertCancel() })) sheet.addAction(UIAlertAction(title: L10n.Rating.Alert.Button.oksure, style: .default, handler: { action in self.openRatingViewInAppstore() @@ -219,7 +219,7 @@ class RatingManager { L10n.Rating.Rate.subtitle ) sheet.addCancelActionWithTitle(L10n.Global.no, handler: { - self.ratingAlertCancelHandler() + self.handleRatingAlertCancel() }) sheet.addActionWithTitle(L10n.Global.yes) { From 5efdf3e7824efd963e716ed45aab439ea68945e9 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 4 May 2022 14:03:35 +0200 Subject: [PATCH 068/159] Bump PIALibrary to v2.17.0 --- Podfile | 2 +- Podfile.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Podfile b/Podfile index 8d05ddf5b..33fdc2313 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('4152a87b') + library_by_gitlab_by_git('0edc0c1c') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 583fcfe76..2ef1f396e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -80,9 +80,9 @@ PODS: - PIAKPIModule/kpi (= 1.1.0) - PIAKPIModule/gradle (1.1.0) - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.16.1): + - PIALibrary/Core (2.17.0): - PIAAccountModule - - PIALibrary/Library (2.16.1): + - PIALibrary/Library (2.17.0): - Alamofire (~> 4) - Gloss (~> 2) - PIAAccountModule @@ -94,18 +94,18 @@ PODS: - PopupDialog - ReachabilitySwift - SwiftyBeaver - - PIALibrary/Mock (2.16.1): + - PIALibrary/Mock (2.17.0): - PIALibrary/Library - - PIALibrary/UI (2.16.1): + - PIALibrary/UI (2.17.0): - FXPageControl - lottie-ios - PIALibrary/Library - SwiftEntryKit (= 0.7.2) - SwiftyBeaver - TPKeyboardAvoiding - - PIALibrary/Util (2.16.1): + - PIALibrary/Util (2.17.0): - PIALibrary/Core - - PIALibrary/VPN (2.16.1): + - PIALibrary/VPN (2.17.0): - PIALibrary/Library - PIAWireguard - TunnelKit @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 4152a87b + :commit: 0edc0c1c :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 4152a87b + :commit: 0edc0c1c :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -278,7 +278,7 @@ SPEC CHECKSUMS: PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: 75247afe6cea9f3ccf8ab2bbbf39aa37264983d0 + PIALibrary: d3afc194b4400e50caea33398145d500fb257a77 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 4ddc29c4d4919c150641fb4f20c17f3dce81fa45 +PODFILE CHECKSUM: e95e0e6b054a6280a7a5bace7ea772c858abd9cc COCOAPODS: 1.11.2 From 3deb1485da30895be6004bd493cc709c7ee0aec5 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 4 May 2022 14:07:13 +0200 Subject: [PATCH 069/159] Bump version to 3.17.0 --- PIA VPN.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 9739e5cd1..03b72edac 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2611,7 +2611,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2642,7 +2642,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2681,7 +2681,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2723,7 +2723,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2817,7 +2817,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2850,7 +2850,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2996,7 +2996,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -3033,7 +3033,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3135,7 +3135,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3173,7 +3173,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3206,7 +3206,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3242,7 +3242,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.16.1; + MARKETING_VERSION = 3.17.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 684b4102b3477f149f2ebf9e64c81c820c55ea0a Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 4 May 2022 14:14:36 +0200 Subject: [PATCH 070/159] Bump PIARegions to v1.3.2 --- Podfile | 2 +- Podfile.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Podfile b/Podfile index 33fdc2313..aa97b421e 100644 --- a/Podfile +++ b/Podfile @@ -73,7 +73,7 @@ def shared_main_pods #pod "PIAAccountModule", :git => "#{$git_root}/#{$accounts_repo}" pod "PIAAccountModule", :git => "#{$gitlab_kn_root}/#{$accounts_gitlab_repo}", :commit => '697fd4f' #pod "PIARegionsModule", :git => "#{$git_root}/#{$regions_repo}" - pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.1' + pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.2' #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :commit => 'b62d1bab' pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' diff --git a/Podfile.lock b/Podfile.lock index 2ef1f396e..2fdfe7649 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -109,11 +109,11 @@ PODS: - PIALibrary/Library - PIAWireguard - TunnelKit - - PIARegionsModule (1.3.1): - - PIARegionsModule/gradle (= 1.3.1) - - PIARegionsModule/regions (= 1.3.1) - - PIARegionsModule/gradle (1.3.1) - - PIARegionsModule/regions (1.3.1) + - PIARegionsModule (1.3.2): + - PIARegionsModule/gradle (= 1.3.2) + - PIARegionsModule/regions (= 1.3.2) + - PIARegionsModule/gradle (1.3.2) + - PIARegionsModule/regions (1.3.2) - PIAWireguard (1.2.0): - PIAWireguard/AppExtension (= 1.2.0) - PIAWireguard/Core (= 1.2.0) @@ -164,7 +164,7 @@ DEPENDENCIES: - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" - - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" + - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.2`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover - PopupDialog @@ -220,7 +220,7 @@ EXTERNAL SOURCES: :commit: 0edc0c1c :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: - :branch: release/1.3.1 + :branch: release/1.3.2 :git: "git@gitlab.kape.com:pia-mobile/shared/regions.git" PIAWireguard: :commit: 7e9d8d48 @@ -246,7 +246,7 @@ CHECKOUT OPTIONS: :commit: 0edc0c1c :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: - :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb + :commit: 355418cf4b3d35512c345e51026fbabf81fe6488 :git: "git@gitlab.kape.com:pia-mobile/shared/regions.git" PIAWireguard: :commit: 7e9d8d48 @@ -279,7 +279,7 @@ SPEC CHECKSUMS: PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d PIALibrary: d3afc194b4400e50caea33398145d500fb257a77 - PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe + PIARegionsModule: f54391cbb218b2780612158c54bd8deeda6941d9 PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d PopupDialog: 720c92befd8bc23c13442254945213db5612f149 @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: e95e0e6b054a6280a7a5bace7ea772c858abd9cc +PODFILE CHECKSUM: b5ee970af6d524fbfb666f7e123d93dc46b7ac97 COCOAPODS: 1.11.2 From 6552296346f42e328924965d73d667322f86387e Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Fri, 13 May 2022 18:15:51 +0200 Subject: [PATCH 071/159] Bump CSI to v1.1.1 and update support for Last Know Exception category on PIALibrary --- PIA VPN/AppPreferences.swift | 2 ++ PIA VPN/Bootstrapper.swift | 10 ++++++++-- Podfile | 4 ++-- Podfile.lock | 32 ++++++++++++++++---------------- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 9373f31ff..f209738c5 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -821,6 +821,7 @@ class AppPreferences { MessagesManager.shared.reset() userInteractedWithSurvey = false successConnectionsUntilSurvey = nil + Client.preferences.lastKnownException = nil } func clean() { @@ -858,6 +859,7 @@ class AppPreferences { appEnvironmentIsProduction = Client.environment == .production ? true : false userInteractedWithSurvey = false successConnectionsUntilSurvey = nil + Client.preferences.lastKnownException = nil } // + (void)eraseForTesting; diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index 20c0d2293..00db669dc 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -230,7 +230,7 @@ class Bootstrapper { if AppPreferences.shared.checksDipExpirationRequest, let dipToken = Client.providers.serverProvider.dipTokens?.first { Client.providers.serverProvider.handleDIPTokenExpiration(dipToken: dipToken, nil) } - + setupExceptionHandler() } private func setDefaultPlanProducts() { @@ -241,7 +241,13 @@ class Bootstrapper { func dispose() { Client.dispose() } - + + private func setupExceptionHandler() { + NSSetUncaughtExceptionHandler { exception in + Client.preferences.lastKnownException = "$exception,\n\(exception.callStackSymbols.joined(separator: "\n"))" + } + } + // MARK: Certificate func rsa4096Certificate() -> String? { diff --git a/Podfile b/Podfile index 8d05ddf5b..acf8c7214 100644 --- a/Podfile +++ b/Podfile @@ -75,13 +75,13 @@ def shared_main_pods #pod "PIARegionsModule", :git => "#{$git_root}/#{$regions_repo}" pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.1' #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" - pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :commit => 'b62d1bab' + pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :branch => 'release/1.1.1' pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('4152a87b') + library_by_gitlab_by_git('1a5856c1') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 583fcfe76..871377194 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -70,11 +70,11 @@ PODS: - PIAAccountModule/gradle (= 1.2.1) - PIAAccountModule/account (1.2.1) - PIAAccountModule/gradle (1.2.1) - - PIACSIModule (1.0.1): - - PIACSIModule/csi (= 1.0.1) - - PIACSIModule/gradle (= 1.0.1) - - PIACSIModule/csi (1.0.1) - - PIACSIModule/gradle (1.0.1) + - PIACSIModule (1.1.1): + - PIACSIModule/csi (= 1.1.1) + - PIACSIModule/gradle (= 1.1.1) + - PIACSIModule/csi (1.1.1) + - PIACSIModule/gradle (1.1.1) - PIAKPIModule (1.1.0): - PIAKPIModule/gradle (= 1.1.0) - PIAKPIModule/kpi (= 1.1.0) @@ -158,12 +158,12 @@ DEPENDENCIES: - GradientProgressBar (~> 2.0) - OpenSSL-Apple (from `https://github.com/keeshux/openssl-apple`) - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, commit `b62d1bab`)" + - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `4152a87b`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -211,13 +211,13 @@ EXTERNAL SOURCES: :commit: 697fd4f :git: "git@gitlab.kape.com:pia-mobile/shared/account.git" PIACSIModule: - :commit: b62d1bab + :branch: release/1.1.1 :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 4152a87b + :commit: 1a5856c1 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -237,13 +237,13 @@ CHECKOUT OPTIONS: :commit: 697fd4f :git: "git@gitlab.kape.com:pia-mobile/shared/account.git" PIACSIModule: - :commit: b62d1bab + :commit: 5e93ae8ff84bc04ac4e1cfbb6d3dd0aa7dc92a01 :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" PIAKPIModule: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 4152a87b + :commit: 1a5856c1 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -276,7 +276,7 @@ SPEC CHECKSUMS: nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd OpenSSL-Apple: bb7c9715a259404de040f5359ed3b3170cedf8d6 PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 - PIACSIModule: 32df98c20a0fc4cad5fadbb1b72f7b74315aa8ea + PIACSIModule: 2db8a7fa393d5bef757e856fff3327b8c37dac5d PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d PIALibrary: 75247afe6cea9f3ccf8ab2bbbf39aa37264983d0 PIARegionsModule: eff00bd28dea554d7b766ec5d7e9a74ab448f5fe @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 4ddc29c4d4919c150641fb4f20c17f3dce81fa45 +PODFILE CHECKSUM: b17cd675b73fcdc9ab5bcebe57aa5914a3b37fb7 COCOAPODS: 1.11.2 From 1ae4b1c798f6de0e178aaf221facef98b17b3798 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 16 May 2022 11:22:40 +0200 Subject: [PATCH 072/159] Bump PIALibrary to latest commit --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index acf8c7214..59662f579 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('1a5856c1') + library_by_gitlab_by_git('c2ad9303') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 871377194..289e240c8 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `1a5856c1`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 1a5856c1 + :commit: c2ad9303 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 1a5856c1 + :commit: c2ad9303 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: b17cd675b73fcdc9ab5bcebe57aa5914a3b37fb7 +PODFILE CHECKSUM: 14b95386b6dfbe04734dda60658392cf3fde84b7 COCOAPODS: 1.11.2 From dbc7c709bc6d53e321d023f37c2cda58ee264213 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Mon, 16 May 2022 12:57:50 +0200 Subject: [PATCH 073/159] Refactor dip header view cell to move the network request code out of it --- PIA VPN/DedicatedIpEmptyHeaderViewCell.swift | 77 ++----------------- PIA VPN/DedicatedIpViewController.swift | 79 +++++++++++++++++--- 2 files changed, 76 insertions(+), 80 deletions(-) diff --git a/PIA VPN/DedicatedIpEmptyHeaderViewCell.swift b/PIA VPN/DedicatedIpEmptyHeaderViewCell.swift index 1aa32a16f..d044f4970 100644 --- a/PIA VPN/DedicatedIpEmptyHeaderViewCell.swift +++ b/PIA VPN/DedicatedIpEmptyHeaderViewCell.swift @@ -23,8 +23,7 @@ import UIKit import PIALibrary protocol DedicatedIpEmptyHeaderViewCellDelegate: AnyObject { - func getTimeToRetryDIP() -> TimeInterval? - func setTimeToRetryDIP(newInterval: TimeInterval) + func handleDIPActivation(with token: String, cell: DedicatedIpEmptyHeaderViewCell) } class DedicatedIpEmptyHeaderViewCell: UITableViewCell { @@ -78,79 +77,15 @@ class DedicatedIpEmptyHeaderViewCell: UITableViewCell { subtitle.style(style: TextStyle.textStyle8) } - private var invalidTokenLocalisedString: String { - get { - return L10n.Dedicated.Ip.Message.Invalid.token - } - } - - private func showInvalidTokenMessage() { - Macros.displayStickyNote(withMessage: invalidTokenLocalisedString, andImage: Asset.iconWarning.image) - } - - private func displayErrorMessage(errorMessage: String?, displayDuration: Double? = nil) { - Macros.displayImageNote(withImage: Asset.iconWarning.image, message: errorMessage ?? invalidTokenLocalisedString, andDuration: displayDuration) - } - - private func handleDIPActivationError(_ error: ClientError) { - switch error { - case .unauthorized: - Client.providers.accountProvider.logout(nil) - Macros.postNotification(.PIAUnauthorized) - case .throttled(let retryAfter): - let retryAfterSeconds = Double(retryAfter) - let localisedThrottlingString = L10n.Dedicated.Ip.Message.Error.retryafter("\(Int(retryAfter))") - - self.displayErrorMessage(errorMessage: NSLocalizedString(localisedThrottlingString, comment: localisedThrottlingString), - displayDuration: retryAfterSeconds) - self.delegate?.setTimeToRetryDIP(newInterval: Date().timeIntervalSince1970 + retryAfterSeconds) - default: - self.showInvalidTokenMessage() - } - } - - private func handleDIPActivation(token: String) { - NotificationCenter.default.post(name: .DedicatedIpShowAnimation, object: nil) - Client.providers.serverProvider.activateDIPToken(token) { [weak self] (server, error) in - NotificationCenter.default.post(name: .DedicatedIpHideAnimation, object: nil) - self?.addTokenTextfield.text = "" - guard let dipServer = server else { - - guard let error = error as? ClientError else { - self?.showInvalidTokenMessage() - return - } - - self?.handleDIPActivationError(error) - return - } - switch dipServer?.dipStatus { - case .active: - Macros.displaySuccessImageNote(withImage: Asset.iconWarning.image, message: L10n.Dedicated.Ip.Message.Valid.token) - case .expired: - print(L10n.Dedicated.Ip.Message.Expired.token) // we dont show the message to the user - default: - Macros.displayStickyNote(withMessage: self?.invalidTokenLocalisedString ?? "", andImage: Asset.iconWarning.image) - } - NotificationCenter.default.post(name: .DedicatedIpReload, object: nil) - NotificationCenter.default.post(name: .PIAThemeDidChange, object: nil) + @IBAction private func activateToken() { + if let token = addTokenTextfield.text { + self.delegate?.handleDIPActivation(with: token, cell: self) } } - @IBAction private func activateToken() { - if let timeUntilNextTry = self.delegate?.getTimeToRetryDIP()?.timeSinceNow() { - displayErrorMessage(errorMessage: L10n.Dedicated.Ip.Message.Error.retryafter("\(Int(timeUntilNextTry))"), displayDuration: timeUntilNextTry) - return - } - - if let token = addTokenTextfield.text, !token.isEmpty { - handleDIPActivation(token: token) - } else { - Macros.displayStickyNote(withMessage: L10n.Dedicated.Ip.Message.Incorrect.token, - andImage: Asset.iconWarning.image) - } + func emptyTokenTextField() { + addTokenTextfield.text = "" } - } extension DedicatedIpEmptyHeaderViewCell: UITextFieldDelegate { diff --git a/PIA VPN/DedicatedIpViewController.swift b/PIA VPN/DedicatedIpViewController.swift index 5d6f60a1e..03b99930d 100644 --- a/PIA VPN/DedicatedIpViewController.swift +++ b/PIA VPN/DedicatedIpViewController.swift @@ -123,7 +123,39 @@ class DedicatedIpViewController: AutolayoutViewController { self.tableView.reloadData() } - + + // MARK: DIP Token handling + + private var invalidTokenLocalisedString: String { + get { + return L10n.Dedicated.Ip.Message.Invalid.token + } + } + + private func showInvalidTokenMessage() { + Macros.displayStickyNote(withMessage: invalidTokenLocalisedString, andImage: Asset.iconWarning.image) + } + + private func displayErrorMessage(errorMessage: String?, displayDuration: Double? = nil) { + Macros.displayImageNote(withImage: Asset.iconWarning.image, message: errorMessage ?? invalidTokenLocalisedString, andDuration: displayDuration) + } + + private func handleDIPActivationError(_ error: ClientError) { + switch error { + case .unauthorized: + Client.providers.accountProvider.logout(nil) + Macros.postNotification(.PIAUnauthorized) + case .throttled(let retryAfter): + let retryAfterSeconds = Double(retryAfter) + let localisedThrottlingString = L10n.Dedicated.Ip.Message.Error.retryafter("\(Int(retryAfter))") + + displayErrorMessage(errorMessage: NSLocalizedString(localisedThrottlingString, comment: localisedThrottlingString), + displayDuration: retryAfterSeconds) + timeToRetryDIP = Date().timeIntervalSince1970 + retryAfterSeconds + default: + showInvalidTokenMessage() + } + } } extension DedicatedIpViewController: UITableViewDelegate, UITableViewDataSource { @@ -210,13 +242,42 @@ extension DedicatedIpViewController: UITableViewDelegate, UITableViewDataSource } extension DedicatedIpViewController: DedicatedIpEmptyHeaderViewCellDelegate { - func getTimeToRetryDIP() -> TimeInterval? { - return timeToRetryDIP - } - - func setTimeToRetryDIP(newInterval: TimeInterval) { - timeToRetryDIP = newInterval + func handleDIPActivation(with token: String, cell: DedicatedIpEmptyHeaderViewCell) { + if let timeUntilNextTry = timeToRetryDIP?.timeSinceNow() { + displayErrorMessage(errorMessage: L10n.Dedicated.Ip.Message.Error.retryafter("\(Int(timeUntilNextTry))"), displayDuration: timeUntilNextTry) + return + } + + if token.isEmpty { + Macros.displayStickyNote(withMessage: L10n.Dedicated.Ip.Message.Incorrect.token, + andImage: Asset.iconWarning.image) + return + } + + NotificationCenter.default.post(name: .DedicatedIpShowAnimation, object: nil) + Client.providers.serverProvider.activateDIPToken(token) { [weak self] (server, error) in + NotificationCenter.default.post(name: .DedicatedIpHideAnimation, object: nil) + cell.emptyTokenTextField() + guard let dipServer = server else { + + guard let error = error as? ClientError else { + self?.showInvalidTokenMessage() + return + } + + self?.handleDIPActivationError(error) + return + } + switch dipServer?.dipStatus { + case .active: + Macros.displaySuccessImageNote(withImage: Asset.iconWarning.image, message: L10n.Dedicated.Ip.Message.Valid.token) + case .expired: + print(L10n.Dedicated.Ip.Message.Expired.token) // we dont show the message to the user + default: + Macros.displayStickyNote(withMessage: self?.invalidTokenLocalisedString ?? "", andImage: Asset.iconWarning.image) + } + NotificationCenter.default.post(name: .DedicatedIpReload, object: nil) + NotificationCenter.default.post(name: .PIAThemeDidChange, object: nil) + } } - - } From 3e466b90a7dad5128af863cf83abbde928b66b1d Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 25 May 2022 15:25:57 +0200 Subject: [PATCH 074/159] Fix a bug where rating alert was re-presented when app was updated --- PIA VPN/RatingManager.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index a0c61d0d4..1918ce12d 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -44,6 +44,9 @@ class RatingManager { } private var targetDisconnectionsReachedForPrompt: Bool { + if AppPreferences.shared.successConnections >= self.successConnectionsUntilPrompt { + return false // We do not check this because alert was already shown + } return AppPreferences.shared.successDisconnections == self.successDisconnectionsUntilPrompt } From 34bac70ab1cb499d6110b5f6f344c6cfbd95bd00 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 25 May 2022 16:31:44 +0200 Subject: [PATCH 075/159] Refactor if into a guard statement --- PIA VPN/RatingManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIA VPN/RatingManager.swift b/PIA VPN/RatingManager.swift index 1918ce12d..f40ed816c 100644 --- a/PIA VPN/RatingManager.swift +++ b/PIA VPN/RatingManager.swift @@ -44,7 +44,7 @@ class RatingManager { } private var targetDisconnectionsReachedForPrompt: Bool { - if AppPreferences.shared.successConnections >= self.successConnectionsUntilPrompt { + guard AppPreferences.shared.successConnections < self.successConnectionsUntilPrompt else { return false // We do not check this because alert was already shown } return AppPreferences.shared.successDisconnections == self.successDisconnectionsUntilPrompt From 09a8d1c37af6abe5f10d6ead73cd43151ea1314e Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 1 Jun 2022 10:42:35 +0200 Subject: [PATCH 076/159] Remove redundant deviceInfo key and bump PIALibrary to whitelist user_setting commit --- PIA VPN/AppPreferences.swift | 13 ------------- PIA VPN/Bootstrapper.swift | 1 - Podfile | 2 +- Podfile.lock | 14 +++++++------- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 0716d112b..9077124ea 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -34,8 +34,6 @@ class AppPreferences { static let appVersion = "AppVersion" - static let deviceType = "deviceType" - static let version = "Version" static let launched = "Launched" // discard 2.2 key and invert logic @@ -121,15 +119,6 @@ class AppPreferences { private var isTransitioningTheme = false - var deviceType: String { - get { - return defaults.string(forKey: Entries.deviceType) ?? "" - } - set { - defaults.set(newValue, forKey: Entries.deviceType) - } - } - var wasLaunched: Bool { get { return defaults.bool(forKey: Entries.launched) @@ -808,7 +797,6 @@ class AppPreferences { } func reset() { - deviceType = "" piaHandshake = .rsa4096 piaSocketType = nil favoriteServerIdentifiersGen4 = [] @@ -849,7 +837,6 @@ class AppPreferences { } func clean() { - deviceType = "" piaHandshake = .rsa4096 piaSocketType = nil favoriteServerIdentifiersGen4 = [] diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index e6556f3f6..00db669dc 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -230,7 +230,6 @@ class Bootstrapper { if AppPreferences.shared.checksDipExpirationRequest, let dipToken = Client.providers.serverProvider.dipTokens?.first { Client.providers.serverProvider.handleDIPTokenExpiration(dipToken: dipToken, nil) } - AppPreferences.shared.deviceType = UIDevice.current.type.rawValue setupExceptionHandler() } diff --git a/Podfile b/Podfile index 59662f579..275d61da0 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('c2ad9303') + library_by_gitlab_by_git('754b03b2') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 289e240c8..985a8a810 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `c2ad9303`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: c2ad9303 + :commit: 754b03b2 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: c2ad9303 + :commit: 754b03b2 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 14b95386b6dfbe04734dda60658392cf3fde84b7 +PODFILE CHECKSUM: b70abdee78ff4443f7a5bef4dd97da60b8c0626b COCOAPODS: 1.11.2 From 79ef454d7fa7232860e881924e5dfb89d11a8e80 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 1 Jun 2022 16:49:10 +0200 Subject: [PATCH 077/159] Bump to the fixed commit sha of file references fixes in PIALibrary --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 275d61da0..9c4edc0ce 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('754b03b2') + library_by_gitlab_by_git('9a6d926d') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 985a8a810..c71116a98 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `754b03b2`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 754b03b2 + :commit: 9a6d926d :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 754b03b2 + :commit: 9a6d926d :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: b70abdee78ff4443f7a5bef4dd97da60b8c0626b +PODFILE CHECKSUM: 32a86cc9fedfb1af280c1ace9d64bee1ce3f029b COCOAPODS: 1.11.2 From 0a3f42e233b7e44cb4247078f4c86965f8448fe9 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Wed, 1 Jun 2022 17:51:54 +0200 Subject: [PATCH 078/159] Bump commit for PIALibrary --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 9c4edc0ce..234f3ad37 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('9a6d926d') + library_by_gitlab_by_git('cec68611') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index c71116a98..9f87b9dbc 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `9a6d926d`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 9a6d926d + :commit: cec68611 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 9a6d926d + :commit: cec68611 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: 32a86cc9fedfb1af280c1ace9d64bee1ce3f029b +PODFILE CHECKSUM: b1f9faa73e8af85f2b7bc13cb5df4508d509dcb5 COCOAPODS: 1.11.2 From 55cb038976c1a7b81743b376646cb2c00eaf93ed Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 7 Jun 2022 15:28:10 +0200 Subject: [PATCH 079/159] Refactor fatalError to NSException for better CSI reports --- PIA VPN/Settings/DevelopmentSettingsViewController.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PIA VPN/Settings/DevelopmentSettingsViewController.swift b/PIA VPN/Settings/DevelopmentSettingsViewController.swift index 0199d0e93..35ccc6292 100644 --- a/PIA VPN/Settings/DevelopmentSettingsViewController.swift +++ b/PIA VPN/Settings/DevelopmentSettingsViewController.swift @@ -231,7 +231,7 @@ extension DevelopmentSettingsViewController: UITableViewDelegate, UITableViewDat } case .crash: - fatalError("Crashing staging app") + crashStagingApp() case .deleteKeychain: deleteKeychain() default: break @@ -240,6 +240,10 @@ extension DevelopmentSettingsViewController: UITableViewDelegate, UITableViewDat tableView.deselectRow(at: indexPath, animated: true) } + private func crashStagingApp() { + NSException(name: NSExceptionName(rawValue: "App Crash"), reason: "Crashing the staging app manually").raise() + } + private func deleteKeychain() { let secItemClasses = [kSecClassGenericPassword, kSecClassInternetPassword, kSecClassCertificate, kSecClassKey, kSecClassIdentity] for itemClass in secItemClasses { From 48577a24adbcea5c72982e93ce6ebea18ceb43ae Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 28 Jun 2022 15:48:37 +0200 Subject: [PATCH 080/159] Rename user preference flag stopInAppMessages to showServiceMessages and bump the commit sha of PIALibrary This is to make UI menu name consistent with flag name --- PIA VPN/AppPreferences.swift | 14 +++++++------- PIA VPN/GeneralSettingsViewController.swift | 8 ++++---- PIA VPN/MessagesManager.swift | 2 +- Podfile | 2 +- Podfile.lock | 14 +++++++------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index 9077124ea..a42c975cf 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -93,7 +93,7 @@ class AppPreferences { static let tokenIPRelation_deprecated = "TokenIPRelation" // In app messages - static let stopInAppMessages = "stopInAppMessages" + static let showServiceMessages = "showServiceMessages" // Features static let showsDedicatedIPView = "showsDedicatedIPView" @@ -480,12 +480,12 @@ class AppPreferences { } } - var stopInAppMessages: Bool { + var showServiceMessages: Bool { get { - return defaults.bool(forKey: Entries.stopInAppMessages) + return defaults.bool(forKey: Entries.showServiceMessages) } set { - defaults.set(newValue, forKey: Entries.stopInAppMessages) + defaults.set(newValue, forKey: Entries.showServiceMessages) } } @@ -595,7 +595,7 @@ class AppPreferences { Entries.failureConnections: 0, Entries.showGeoServers: true, Entries.dismissedMessages: [], - Entries.stopInAppMessages: false, + Entries.showServiceMessages: false, Entries.showsDedicatedIPView: true, Entries.disablesMultiDipTokens: true, Entries.checksDipExpirationRequest: true, @@ -827,7 +827,7 @@ class AppPreferences { successConnections = 0 failureConnections = 0 showGeoServers = true - stopInAppMessages = false + showServiceMessages = false dedicatedTokenIPReleation = [:] appEnvironmentIsProduction = Client.environment == .production ? true : false MessagesManager.shared.reset() @@ -865,7 +865,7 @@ class AppPreferences { successConnections = 0 failureConnections = 0 showGeoServers = true - stopInAppMessages = false + showServiceMessages = false dismissedMessages = [] dedicatedTokenIPReleation = [:] MessagesManager.shared.reset() diff --git a/PIA VPN/GeneralSettingsViewController.swift b/PIA VPN/GeneralSettingsViewController.swift index d23b7e3e1..41f1e4e3a 100644 --- a/PIA VPN/GeneralSettingsViewController.swift +++ b/PIA VPN/GeneralSettingsViewController.swift @@ -37,7 +37,7 @@ class GeneralSettingsViewController: PIABaseSettingsViewController { tableView.estimatedSectionFooterHeight = 1.0 switchGeoServers.addTarget(self, action: #selector(toggleGEOServers(_:)), for: .valueChanged) - switchInAppMessages.addTarget(self, action: #selector(toggleStopInAppMessages(_:)), for: .valueChanged) + switchInAppMessages.addTarget(self, action: #selector(toggleShowServiceMessages(_:)), for: .valueChanged) tableView.delegate = self tableView.dataSource = self @@ -60,8 +60,8 @@ class GeneralSettingsViewController: PIABaseSettingsViewController { self.tableView.reloadData() } - @objc private func toggleStopInAppMessages(_ sender: UISwitch) { - AppPreferences.shared.stopInAppMessages = sender.isOn + @objc private func toggleShowServiceMessages(_ sender: UISwitch) { + AppPreferences.shared.showServiceMessages = sender.isOn } @objc private func toggleGEOServers(_ sender: UISwitch) { @@ -129,7 +129,7 @@ extension GeneralSettingsViewController: UITableViewDelegate, UITableViewDataSou case .showServiceCommunicationMessages: cell.accessoryView = switchInAppMessages cell.selectionStyle = .none - switchInAppMessages.isOn = AppPreferences.shared.stopInAppMessages //invert the boolean as the title has change to Show messages instead of Stop messages + switchInAppMessages.isOn = AppPreferences.shared.showServiceMessages //invert the boolean as the title has change to Show messages instead of Stop messages case .showGeoRegions: cell.textLabel?.numberOfLines = 0 diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 9a72f9818..cdcc8f3d9 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -42,7 +42,7 @@ public class MessagesManager: NSObject { func refreshMessages() { - if !AppPreferences.shared.stopInAppMessages { + if !AppPreferences.shared.showServiceMessages { Client.providers.accountProvider.inAppMessages(forAppVersion: Macros.localizedVersionNumber()) { (message, error) in if let message = message, !message.wasDismissed() { self.apiMessage = message diff --git a/Podfile b/Podfile index 234f3ad37..3d78a9f76 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('cec68611') + library_by_gitlab_by_git('5cc89ea5') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 9f87b9dbc..d811d62ce 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cec68611`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5cc89ea5`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5cc89ea5`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5cc89ea5`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `5cc89ea5`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.1`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: cec68611 + :commit: 5cc89ea5 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.1 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: cec68611 + :commit: 5cc89ea5 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 6b3b763b3b1d066cb73d2d8833563669ff8e87bb @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: b1f9faa73e8af85f2b7bc13cb5df4508d509dcb5 +PODFILE CHECKSUM: 0eca7c23ca0d0c7f7f65ce2d00b63e2199dcd37b COCOAPODS: 1.11.2 From 3118c9f974401b3d50006d109c7387c2e5160b45 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Tue, 28 Jun 2022 15:53:48 +0200 Subject: [PATCH 081/159] Fix the logic when API call should be made based on flag --- PIA VPN/MessagesManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index cdcc8f3d9..142267719 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -42,7 +42,7 @@ public class MessagesManager: NSObject { func refreshMessages() { - if !AppPreferences.shared.showServiceMessages { + if AppPreferences.shared.showServiceMessages { Client.providers.accountProvider.inAppMessages(forAppVersion: Macros.localizedVersionNumber()) { (message, error) in if let message = message, !message.wasDismissed() { self.apiMessage = message From b236b34f39445188136d8e46aac44b2c7c73f848 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 14 Jul 2022 10:46:39 +0200 Subject: [PATCH 082/159] Bump PIALibrary to 2.18.0 commit sha --- Podfile | 2 +- Podfile.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Podfile b/Podfile index 88851f75f..0dae834a8 100644 --- a/Podfile +++ b/Podfile @@ -81,7 +81,7 @@ def shared_main_pods #library_by_path('~/Repositories') #library_by_git('') #library_by_gitlab_branch('') - library_by_gitlab_by_git('0edc0c1c') + library_by_gitlab_by_git('cffdca05') #library_by_version('~> 1.1.3') end diff --git a/Podfile.lock b/Podfile.lock index 801766167..1a9d5014a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -80,9 +80,9 @@ PODS: - PIAKPIModule/kpi (= 1.1.0) - PIAKPIModule/gradle (1.1.0) - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.17.0): + - PIALibrary/Core (2.18.0): - PIAAccountModule - - PIALibrary/Library (2.17.0): + - PIALibrary/Library (2.18.0): - Alamofire (~> 4) - Gloss (~> 2) - PIAAccountModule @@ -94,18 +94,18 @@ PODS: - PopupDialog - ReachabilitySwift - SwiftyBeaver - - PIALibrary/Mock (2.17.0): + - PIALibrary/Mock (2.18.0): - PIALibrary/Library - - PIALibrary/UI (2.17.0): + - PIALibrary/UI (2.18.0): - FXPageControl - lottie-ios - PIALibrary/Library - SwiftEntryKit (= 0.7.2) - SwiftyBeaver - TPKeyboardAvoiding - - PIALibrary/Util (2.17.0): + - PIALibrary/Util (2.18.0): - PIALibrary/Core - - PIALibrary/VPN (2.17.0): + - PIALibrary/VPN (2.18.0): - PIALibrary/Library - PIAWireguard - TunnelKit @@ -160,10 +160,10 @@ DEPENDENCIES: - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `0edc0c1c`)" + - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" + - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" + - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" + - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.2`)" - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - Popover @@ -217,7 +217,7 @@ EXTERNAL SOURCES: :branch: release/1.1.0 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0edc0c1c + :commit: cffdca05 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :branch: release/1.3.2 @@ -243,7 +243,7 @@ CHECKOUT OPTIONS: :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" PIALibrary: - :commit: 0edc0c1c + :commit: cffdca05 :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" PIARegionsModule: :commit: 355418cf4b3d35512c345e51026fbabf81fe6488 @@ -278,7 +278,7 @@ SPEC CHECKSUMS: PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 PIACSIModule: 2db8a7fa393d5bef757e856fff3327b8c37dac5d PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: d3afc194b4400e50caea33398145d500fb257a77 + PIALibrary: ed53091a5bc74f77354481729eff87f6c07fd00d PIARegionsModule: f54391cbb218b2780612158c54bd8deeda6941d9 PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d @@ -293,6 +293,6 @@ SPEC CHECKSUMS: TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 -PODFILE CHECKSUM: b5ee970af6d524fbfb666f7e123d93dc46b7ac97 +PODFILE CHECKSUM: 5da459779bbfdfae20771eca5db526da51714419 COCOAPODS: 1.11.2 From 14c8ab516d1eb21900973867102eee7256d25416 Mon Sep 17 00:00:00 2001 From: Waleed Mahmood Date: Thu, 14 Jul 2022 10:49:37 +0200 Subject: [PATCH 083/159] Bump version 3.18.0 --- PIA VPN.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 03b72edac..6d70fb280 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2611,7 +2611,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2642,7 +2642,7 @@ INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2681,7 +2681,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2723,7 +2723,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2817,7 +2817,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2850,7 +2850,7 @@ INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2996,7 +2996,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -3033,7 +3033,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3135,7 +3135,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3173,7 +3173,7 @@ INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3206,7 +3206,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3242,7 +3242,7 @@ INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.17.0; + MARKETING_VERSION = 3.18.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 07df43d3619792a6cc704f41a72abda937bdc3a1 Mon Sep 17 00:00:00 2001 From: kp-juan-docal <109512072+kp-juan-docal@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:50:03 +0100 Subject: [PATCH 084/159] Merge `release/3.19.0` into `master` (#1131) * Fix develop branch for an issue where commit sha does not exist on client-library-apple * Fix CSI and KPI dependencies * Migrate to SPM * Widget Cleanup * Update to wireguard-apple `1.0.15-26` * Add workflow pia-mobile/ios/vpn-ios * Sync OpenVPN TunnelKit to version 4.0.3 (#1128) * wip * wip * update dep * bump version * bump build number * Fix signup storyboard crash (#1129) * fix signup storyboard crash * update to client-library-apple merged revision * bump build version number to 20030 (#1130) --------- Co-authored-by: Waleed Mahmood Co-authored-by: Juan Docal Co-authored-by: Bogdan Danila <109506906+kp-bogdan-danila@users.noreply.github.com> --- .github/workflows/vpn-ios.yml | 256 ++++++ PIA VPN Tunnel/PacketTunnelProvider.swift | 2 +- PIA VPN.xcodeproj/project.pbxproj | 781 ++++++++++-------- .../xcschemes/PIA VPN WG Tunnel.xcscheme | 1 + PIA VPN.xcworkspace/contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - PIA VPN/AccountObserver.swift | 1 + PIA VPN/AppConfiguration.swift | 8 +- PIA VPN/AppPreferences.swift | 10 +- PIA VPN/Bootstrapper.swift | 18 +- PIA VPN/CardFactory.swift | 1 + PIA VPN/CustomDNSSettingsViewController.swift | 1 + PIA VPN/DashboardViewController.swift | 5 +- PIA VPN/DedicatedIpViewController.swift | 1 + PIA VPN/MessagesCommands.swift | 7 +- PIA VPN/MessagesManager.swift | 1 + PIA VPN/PIAHotspotHelper.swift | 1 + PIA VPN/PIAPageControl.swift | 2 +- PIA VPN/PIATunnelProvider+UI.swift | 3 +- PIA VPN/PemUtil.swift | 3 +- PIA VPN/ProtocolSettingsViewController.swift | 3 +- PIA VPN/Server+UI.swift | 3 +- .../Settings/HelpSettingsViewController.swift | 2 +- .../NetworkSettingsViewController.swift | 3 +- .../PIABaseSettingsViewController.swift | 3 +- .../SettingPopoverSelectionView.swift | 3 +- PIA VPN/SettingsDelegate.swift | 3 +- PIA VPN/SettingsViewController.swift | 12 +- .../ShowConnectionStatsViewController.swift | 1 + PIA VPN/String+VPNType.swift | 9 +- PIA VPN/SwiftGen+ScenesStoryboards.swift | 2 +- PIA VPN/Theme+App.swift | 1 - PIA VPN/Theme+DarkPalette.swift | 1 + PIA VPN/ThemeStrategy+App.swift | 1 + PIA VPN/Tiles/ConnectionTile.swift | 3 +- PIA VPN/Tiles/FavoriteServersTile.swift | 1 + PIA VPN/Tiles/MessagesTile.swift | 1 + PIA VPN/Tiles/UsageTile.swift | 1 + .../Cache/WidgetPersistenceDatasource.swift | 17 + .../Cache/WidgetUserDefaultsDatasource.swift} | 32 +- .../Model/WidgetInformation.swift} | 9 +- PIAWidget/Domain/UI/PIACircleVpnButton.swift | 44 + PIAWidget/Domain/UI/PIAIconView.swift | 31 + PIAWidget/Domain/UI/PIAWidgetView.swift | 52 ++ .../Domain/UI/PIAWidgetVpnDetailsView.swift | 33 + .../Domain/UI/PIAWidgetVpnDetaislRow.swift | 38 + PIAWidget/Domain/Widget/PIAWidget.swift | 48 ++ .../Domain/Widget/PIAWidgetPreview.swift | 38 + .../Domain/Widget/PIAWidgetProvider.swift | 73 ++ PIAWidget/PIAWidget.swift | 166 ---- Podfile | 159 ---- Podfile.lock | 298 ------- README.md | 23 - Resources/GoogleService-Info.plist | 5 - Resources/UI/en.lproj/Main.storyboard | 6 +- 56 files changed, 1155 insertions(+), 1098 deletions(-) create mode 100644 .github/workflows/vpn-ios.yml delete mode 100644 PIA VPN.xcworkspace/contents.xcworkspacedata delete mode 100644 PIA VPN.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 PIAWidget/Data/Cache/WidgetPersistenceDatasource.swift rename PIAWidget/{WidgetUtils.swift => Data/Cache/WidgetUserDefaultsDatasource.swift} (85%) rename PIAWidget/{WidgetContent.swift => Data/Model/WidgetInformation.swift} (92%) create mode 100644 PIAWidget/Domain/UI/PIACircleVpnButton.swift create mode 100644 PIAWidget/Domain/UI/PIAIconView.swift create mode 100644 PIAWidget/Domain/UI/PIAWidgetView.swift create mode 100644 PIAWidget/Domain/UI/PIAWidgetVpnDetailsView.swift create mode 100644 PIAWidget/Domain/UI/PIAWidgetVpnDetaislRow.swift create mode 100644 PIAWidget/Domain/Widget/PIAWidget.swift create mode 100644 PIAWidget/Domain/Widget/PIAWidgetPreview.swift create mode 100644 PIAWidget/Domain/Widget/PIAWidgetProvider.swift delete mode 100644 PIAWidget/PIAWidget.swift delete mode 100644 Podfile delete mode 100644 Podfile.lock delete mode 100644 Resources/GoogleService-Info.plist diff --git a/.github/workflows/vpn-ios.yml b/.github/workflows/vpn-ios.yml new file mode 100644 index 000000000..dbb57814a --- /dev/null +++ b/.github/workflows/vpn-ios.yml @@ -0,0 +1,256 @@ +name: pia-mobile/ios/vpn-ios +on: + push: + workflow_dispatch: +concurrency: + group: "${{ github.ref }}" + cancel-in-progress: true +env: + PIA_STAGING_ENDPOINT: "${{ secrets.PIA_STAGING_ENDPOINT }}" + PIA_CUSTOM_SERVERS: chipotle251:US:chipotle251.londontrustmedia.com:108.61.57.211:8080:500 sharingan:GB:sharingan.londontrustmedia.com:185.195.200.20:8080:500 + PIA_FIREBASE_PLIST: CLIENT_ID599746893663-naub4m5ppoos0tuodrjuk67t0q5saj3m.apps.googleusercontent.comREVERSED_CLIENT_IDcom.googleusercontent.apps.599746893663-naub4m5ppoos0tuodrjuk67t0q5saj3mAPI_KEYAIzaSyD_9Gdzi4WgNfAwl9PPupph1eWnf0zstA4GCM_SENDER_ID599746893663PLIST_VERSION1BUNDLE_IDcom.privateinternetaccess.ios.PIA-VPNPROJECT_IDpia-ios-e7da0STORAGE_BUCKETpia-ios-e7da0.appspot.comIS_ADS_ENABLEDIS_ANALYTICS_ENABLEDIS_APPINVITE_ENABLEDIS_GCM_ENABLEDIS_SIGNIN_ENABLEDGOOGLE_APP_ID1:599746893663:ios:9a64d6b91b33eb1f3088eaDATABASE_URLhttps://pia-ios-e7da0.firebaseio.com + APPCENTER_API_TOKEN: "${{ secrets.APPCENTER_API_TOKEN }}" + APPCENTER_OWNER_NAME: Kape + APPCENTER_APP_NAME: PIA-VPN + APPCENTER_DISTRIBUTE_DESTINATIONS: QA,Developers + APPSTORE_USERNAME: "${{ secrets.APPSTORE_USERNAME }}" + APPSTORE_SPECIFIC_PWD: "${{ secrets.APPSTORE_SPECIFIC_PWD }}" + APPSTORE_PASSWORD: 7Uyz@VouY3pvn!gdU7Zr + APP_IDENTIFIER: "${{ secrets.APP_IDENTIFIER }}" + APPSTORE_CONNECT_TEAM_ID: '609225' + APPSTORE_DEVELOPER_TEAM_ID: "${{ secrets.APPSTORE_DEVELOPER_TEAM_ID }}" + FASTLANE_USER: "${{ secrets.FASTLANE_USER }}" + FASTLANE_PASSWORD: 7Uyz@VouY3pvn!gdU7Zr + MATCH_PASSWORD: "${{ secrets.MATCH_PASSWORD }}" + SLACK_URL: "${{ secrets.SLACK_URL }}" + DELIVER_USER: "${{ secrets.DELIVER_USER }}" +jobs: + qa_archive: + runs-on: + - self-hosted + - ios + if: (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads//^release.*$/') + timeout-minutes: 60 + env: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + STAGE_BUILD_PATH: build + STAGE_ARTIFACTS_PATH: dist + STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist + STAGE_ARCHIVE_NAME: pia-vpn + SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" + MATCH_TYPE: adhoc + GYM_SCHEME: PIA VPN dev + GYM_EXPORT_METHOD: ad-hoc + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 50 + lfs: true + - run: gem install bundler --no-ri --no-rdoc + - run: bundle update + - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" + - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" + - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" + - run: bundle exec fastlane create_archive + - uses: actions/upload-artifact@v2 + if: success() + with: + name: "${{ github.job }}" + retention-days: 7 + path: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.*" + beta_manual_archive: + runs-on: + - self-hosted + - ios + if: github.event_name == 'workflow_dispatch' + timeout-minutes: 60 + env: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + STAGE_BUILD_PATH: build + STAGE_ARTIFACTS_PATH: dist + STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist + STAGE_ARCHIVE_NAME: pia-vpn + SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" + MATCH_TYPE: appstore + GYM_SCHEME: PIA VPN + GYM_EXPORT_METHOD: app-store + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 50 + lfs: true + - run: gem install bundler --no-ri --no-rdoc + - run: bundle update + - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" + - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" + - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" + - run: bundle exec fastlane create_beta_archive + - uses: actions/upload-artifact@v2 + if: success() + with: + name: "${{ github.job }}" + retention-days: 7 + path: "$STAGE_TESTFLIGHT_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.*" + manual_qa_archive: + runs-on: + - self-hosted + - ios + if: github.event_name == 'workflow_dispatch' + timeout-minutes: 60 + env: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + STAGE_BUILD_PATH: build + STAGE_ARTIFACTS_PATH: dist + STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist + STAGE_ARCHIVE_NAME: pia-vpn + SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" + MATCH_TYPE: adhoc + GYM_SCHEME: PIA VPN dev + GYM_EXPORT_METHOD: ad-hoc + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 50 + lfs: true + - run: gem install bundler --no-ri --no-rdoc + - run: bundle update + - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" + - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" + - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" + - run: bundle exec fastlane create_archive + - uses: actions/upload-artifact@v2 + if: success() + with: + name: "${{ github.job }}" + retention-days: 7 + path: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.*" + qa_deploy: + needs: + - qa_archive + - beta_manual_archive + - manual_qa_archive + runs-on: + - self-hosted + - ios + if: (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads//^release.*$/') + environment: + name: hockey + url: "$HOCKEY_URL" + timeout-minutes: 60 + env: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + STAGE_BUILD_PATH: build + STAGE_ARTIFACTS_PATH: dist + STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist + STAGE_ARCHIVE_NAME: pia-vpn + SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" + IPA_OUTPUT_PATH: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" + FL_HOCKEY_IPA: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" + FL_HOCKEY_COMMIT_SHA: "${{ github.sha }}" + FL_HOCKEY_BUILD_SERVER_URL: "${{ github.server_url }}/${{ github.repository }}/-/jobs/${{ github.job }}" + FL_HOCKEY_REPOSITORY_URL: "${{ github.server_url }}/${{ github.repository }}" + FL_HOCKEY_NOTIFY: 'false' + FL_HOCKEY_STRATEGY: replace + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 50 + lfs: true + - uses: actions/download-artifact@v2 + - run: gem install bundler --no-ri --no-rdoc + - run: bundle update + - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" + - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" + - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" + - run: bundle exec fastlane qa_deploy + - uses: actions/upload-artifact@v2 + if: success() + with: + name: "${{ github.job }}" + retention-days: 7 + path: "$SERIALIZED_ARCHIVE_JSON" + beta_manual_deploy: + needs: + - qa_archive + - beta_manual_archive + - manual_qa_archive + runs-on: + - self-hosted + - ios + if: github.event_name == 'workflow_dispatch' + environment: + name: testflight + timeout-minutes: 60 + env: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + STAGE_BUILD_PATH: build + STAGE_ARTIFACTS_PATH: dist + STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist + STAGE_ARCHIVE_NAME: pia-vpn + SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" + PILOT_IPA: "$STAGE_TESTFLIGHT_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" + PILOT_DISTRIBUTE_EXTERNAL: 'true' + DEMO_ACCOUNT_REQUIRED: 'true' + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 50 + lfs: true + - uses: actions/download-artifact@v2 + - run: gem install bundler --no-ri --no-rdoc + - run: bundle update + - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" + - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" + - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" + - run: bundle exec fastlane beta_deploy + manual_qa_deploy: + needs: + - qa_archive + - beta_manual_archive + - manual_qa_archive + runs-on: + - self-hosted + - ios + if: github.event_name == 'workflow_dispatch' + environment: + name: hockey + url: "$HOCKEY_URL" + timeout-minutes: 60 + env: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + STAGE_BUILD_PATH: build + STAGE_ARTIFACTS_PATH: dist + STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist + STAGE_ARCHIVE_NAME: pia-vpn + SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" + IPA_OUTPUT_PATH: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" + FL_HOCKEY_IPA: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" + FL_HOCKEY_COMMIT_SHA: "${{ github.sha }}" + FL_HOCKEY_BUILD_SERVER_URL: "${{ github.server_url }}/${{ github.repository }}/-/jobs/${{ github.job }}" + FL_HOCKEY_REPOSITORY_URL: "${{ github.server_url }}/${{ github.repository }}" + FL_HOCKEY_NOTIFY: 'false' + FL_HOCKEY_STRATEGY: replace + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 50 + lfs: true + - uses: actions/download-artifact@v2 + - run: gem install bundler --no-ri --no-rdoc + - run: bundle update + - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" + - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" + - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" + - run: bundle exec fastlane qa_deploy + - uses: actions/upload-artifact@v2 + if: success() + with: + name: "${{ github.job }}" + retention-days: 7 + path: "$SERIALIZED_ARCHIVE_JSON" diff --git a/PIA VPN Tunnel/PacketTunnelProvider.swift b/PIA VPN Tunnel/PacketTunnelProvider.swift index 36c8eb1d4..67aa1e0c5 100644 --- a/PIA VPN Tunnel/PacketTunnelProvider.swift +++ b/PIA VPN Tunnel/PacketTunnelProvider.swift @@ -20,7 +20,7 @@ // Internet Access iOS Client. If not, see . // -import TunnelKit +import TunnelKitOpenVPNAppExtension class PacketTunnelProvider: OpenVPNTunnelProvider { } diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 6d70fb280..6224cf258 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -3,11 +3,10 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ - 078FA875634A84BD6728DF93 /* Pods_PIA_VPN_WG_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28DFD7434ECA3047EB4A2C75 /* Pods_PIA_VPN_WG_Tunnel.framework */; }; 0E0715E7201CBB7100D6F666 /* Flags-dev.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0E0715E5201CBB7100D6F666 /* Flags-dev.plist */; }; 0E0786DE1EFA7EAE00F77466 /* Components.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0E0786DD1EFA7EAE00F77466 /* Components.plist */; }; 0E1F318620176A5F00FC1000 /* Theme+DarkPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1F318520176A5F00FC1000 /* Theme+DarkPalette.swift */; }; @@ -128,7 +127,6 @@ 0EFDC1ED1FE4B9DC007C0B9B /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1EB1FE4B9DC007C0B9B /* AppConstants.swift */; }; 0EFDC1EF1FE4B9E6007C0B9B /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1EE1FE4B9E6007C0B9B /* AppConfiguration.swift */; }; 0EFDC1F01FE4B9E6007C0B9B /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1EE1FE4B9E6007C0B9B /* AppConfiguration.swift */; }; - 12CE273914ED9E7533E3A51E /* Pods_PIA_VPN_dev.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */; }; 2909869018566430002D9687 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2909868F18566430002D9687 /* Security.framework */; }; 291C6380183EBC210039EC03 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 291C637F183EBC210039EC03 /* Foundation.framework */; }; 291C6382183EBC210039EC03 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 291C6381183EBC210039EC03 /* CoreGraphics.framework */; }; @@ -147,9 +145,6 @@ 3545E98226AADB2B00B812CC /* ServerSelectingTileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */; }; 3545E98326AADC7C00B812CC /* ServerSelectingTileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */; }; 3545E98426AADC7E00B812CC /* ServerSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E97F26AAD60C00B812CC /* ServerSelectionDelegate.swift */; }; - 3732E0F0C64DA8513903087D /* Pods_PIA_VPN_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02823EF3398B7E2C32FA7544 /* Pods_PIA_VPN_Tunnel.framework */; }; - 520C1753B3AD005B8609F080 /* Pods_PIA_VPN_UITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EB292786A3E16F34DFADE7D /* Pods_PIA_VPN_UITests.framework */; }; - 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */; }; 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */; }; @@ -170,7 +165,7 @@ 82183D9C25014FDC0033023F /* PIAHeaderCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 82183D9525014FDC0033023F /* PIAHeaderCollectionViewCell.xib */; }; 82183D9D25014FDC0033023F /* PIAHeaderCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 82183D9525014FDC0033023F /* PIAHeaderCollectionViewCell.xib */; }; 8221922B24CECFE700C24F1C /* NMTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8221922A24CECFE700C24F1C /* NMTTests.swift */; }; - 822F97B4251DD53100644EF2 /* WidgetUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 822F97B3251DD53100644EF2 /* WidgetUtils.swift */; }; + 822F97B4251DD53100644EF2 /* WidgetUserDefaultsDatasource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 822F97B3251DD53100644EF2 /* WidgetUserDefaultsDatasource.swift */; }; 824C530824C046CB003DB740 /* ConnectionTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 824C530724C046CA003DB740 /* ConnectionTile.swift */; }; 824C530924C046CB003DB740 /* ConnectionTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 824C530724C046CA003DB740 /* ConnectionTile.swift */; }; 824C531524C04796003DB740 /* ConnectionTileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 824C531324C04796003DB740 /* ConnectionTileCollectionViewCell.swift */; }; @@ -184,7 +179,7 @@ 8269A6DA251CB5E0000B4DBF /* PIAWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8269A6D9251CB5E0000B4DBF /* PIAWidget.swift */; }; 8269A6DD251CB5E3000B4DBF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */; }; 8269A6E3251CB5E3000B4DBF /* PIAWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8269A6D5251CB5E0000B4DBF /* PIAWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 8269A6FE251CBB36000B4DBF /* WidgetContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8269A6FD251CBB36000B4DBF /* WidgetContent.swift */; }; + 8269A6FE251CBB36000B4DBF /* WidgetInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8269A6FD251CBB36000B4DBF /* WidgetInformation.swift */; }; 826BE8EE253861BE002339F3 /* DedicatedRegionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 826BE8ED253861BE002339F3 /* DedicatedRegionCell.swift */; }; 826BE8EF253861BE002339F3 /* DedicatedRegionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 826BE8ED253861BE002339F3 /* DedicatedRegionCell.swift */; }; 8272C62726540B2100D846A8 /* ProtocolSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8272C62626540B2100D846A8 /* ProtocolSettingsViewController.swift */; }; @@ -266,7 +261,32 @@ 82CAB8FE255C115100BB08EF /* MessagesTile.xib in Resources */ = {isa = PBXBuildFile; fileRef = 82CAB8FC255C115100BB08EF /* MessagesTile.xib */; }; 82F41376264E8AC20098FF4B /* SettingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F41375264E8AC20098FF4B /* SettingOptions.swift */; }; 82F41377264E8AC20098FF4B /* SettingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F41375264E8AC20098FF4B /* SettingOptions.swift */; }; - 929703E310E04502651A34E9 /* Pods_PIA_VPNTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F453DF1F58B8C2676041EDF /* Pods_PIA_VPNTests.framework */; }; + AA36CDBB28A6622A00180A33 /* TweetNacl in Frameworks */ = {isa = PBXBuildFile; productRef = AA36CDBA28A6622A00180A33 /* TweetNacl */; }; + AA36CDC428A6711300180A33 /* Popover in Frameworks */ = {isa = PBXBuildFile; productRef = AA36CDC328A6711300180A33 /* Popover */; }; + AA36CDC928A6733500180A33 /* DZNEmptyDataSet in Frameworks */ = {isa = PBXBuildFile; productRef = AA36CDC828A6733500180A33 /* DZNEmptyDataSet */; }; + AA36CDCC28A673C900180A33 /* GradientProgressBar in Frameworks */ = {isa = PBXBuildFile; productRef = AA36CDCB28A673C900180A33 /* GradientProgressBar */; }; + AA36CDCF28A6746500180A33 /* SideMenu in Frameworks */ = {isa = PBXBuildFile; productRef = AA36CDCE28A6746500180A33 /* SideMenu */; }; + AA36CDDB28A6860F00180A33 /* TweetNacl in Frameworks */ = {isa = PBXBuildFile; productRef = AA36CDDA28A6860F00180A33 /* TweetNacl */; }; + AA36CDDE28A6878000180A33 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = AA36CDDD28A6878000180A33 /* AlamofireImage */; }; + AA52C59F28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA52C59E28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift */; }; + AA5F0EA928B7640B006D629D /* PIALibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA5F0EA828B7640B006D629D /* PIALibrary */; }; + AA5F0EAB28B7653A006D629D /* PIALibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA5F0EAA28B7653A006D629D /* PIALibrary */; }; + AA5F0EAD28B7653F006D629D /* PIALibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA5F0EAC28B7653F006D629D /* PIALibrary */; }; + AA5F0EAF28B76543006D629D /* PIALibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA5F0EAE28B76543006D629D /* PIALibrary */; }; + AABF826D28AD185E00CDAC64 /* TweetNacl in Frameworks */ = {isa = PBXBuildFile; productRef = AABF826C28AD185E00CDAC64 /* TweetNacl */; }; + AABF826F28AD187800CDAC64 /* Popover in Frameworks */ = {isa = PBXBuildFile; productRef = AABF826E28AD187800CDAC64 /* Popover */; }; + AABF827128AD188700CDAC64 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = AABF827028AD188700CDAC64 /* AlamofireImage */; }; + AABF827328AE2FF500CDAC64 /* GradientProgressBar in Frameworks */ = {isa = PBXBuildFile; productRef = AABF827228AE2FF500CDAC64 /* GradientProgressBar */; }; + AABF827528AE2FFE00CDAC64 /* SideMenu in Frameworks */ = {isa = PBXBuildFile; productRef = AABF827428AE2FFE00CDAC64 /* SideMenu */; }; + AABF827628AE302300CDAC64 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD606AB921C7A17900E0781D /* NetworkExtension.framework */; }; + AABF827828AE333200CDAC64 /* DZNEmptyDataSet in Frameworks */ = {isa = PBXBuildFile; productRef = AABF827728AE333200CDAC64 /* DZNEmptyDataSet */; }; + AAE8789C28E4679500557F26 /* WidgetPersistenceDatasource.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE8789B28E4679500557F26 /* WidgetPersistenceDatasource.swift */; }; + AAE8789F28E4696300557F26 /* PIAWidgetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE8789E28E4696300557F26 /* PIAWidgetProvider.swift */; }; + AAE878A128E46C1600557F26 /* PIAWidgetPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE878A028E46C1600557F26 /* PIAWidgetPreview.swift */; }; + AAE878A328E46D2B00557F26 /* PIAWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE878A228E46D2B00557F26 /* PIAWidgetView.swift */; }; + AAE878A528E4723B00557F26 /* PIACircleVpnButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE878A428E4723B00557F26 /* PIACircleVpnButton.swift */; }; + AAE878A728E473A400557F26 /* PIAWidgetVpnDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE878A628E473A400557F26 /* PIAWidgetVpnDetailsView.swift */; }; + AAE878A928E4765F00557F26 /* PIAIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE878A828E4765F00557F26 /* PIAIconView.swift */; }; DD052AC32419003300AD3A24 /* ProtocolTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD052AC22419003300AD3A24 /* ProtocolTableViewCell.swift */; }; DD052AC42419020E00AD3A24 /* ProtocolTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD052AC22419003300AD3A24 /* ProtocolTableViewCell.swift */; }; DD07AACC242CBFF2000EE1A3 /* AddEmailToAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD07AACB242CBFF2000EE1A3 /* AddEmailToAccountViewController.swift */; }; @@ -318,7 +338,6 @@ DD51F8C52372E4C8009FEED3 /* PemUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD51F8C32372E4C8009FEED3 /* PemUtil.swift */; }; DD522D22237D74380072F555 /* BooleanUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD522D21237D74380072F555 /* BooleanUtil.swift */; }; DD522D23237D74380072F555 /* BooleanUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD522D21237D74380072F555 /* BooleanUtil.swift */; }; - DD58F4B821AD579A00D043F7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DD58F4B721AD579A00D043F7 /* GoogleService-Info.plist */; }; DD58F4BF21B12CFE00D043F7 /* PIAConnectionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD58F4BE21B12CFE00D043F7 /* PIAConnectionButton.swift */; }; DD58F4C021B12CFE00D043F7 /* PIAConnectionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD58F4BE21B12CFE00D043F7 /* PIAConnectionButton.swift */; }; DD606ABA21C7A17900E0781D /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD606AB921C7A17900E0781D /* NetworkExtension.framework */; }; @@ -453,6 +472,20 @@ remoteGlobalIDString = 8269A6D4251CB5E0000B4DBF; remoteInfo = PIAWidgetExtension; }; + AABF827928AE336000CDAC64 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 291C6374183EBC210039EC03 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DDC8F5EB23EC106F005D19C6; + remoteInfo = "PIA VPN WG Tunnel"; + }; + AABF827B28AE336F00CDAC64 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 291C6374183EBC210039EC03 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8269A6D4251CB5E0000B4DBF; + remoteInfo = PIAWidgetExtension; + }; DDC8F5F223EC1070005D19C6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 291C6374183EBC210039EC03 /* Project object */; @@ -503,8 +536,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 02823EF3398B7E2C32FA7544 /* Pods_PIA_VPN_Tunnel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_Tunnel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0BE4A5FC9BACD5CAB3D6CEF0 /* Pods-PIA VPN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN/Pods-PIA VPN.release.xcconfig"; sourceTree = ""; }; 0E0715E5201CBB7100D6F666 /* Flags-dev.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Flags-dev.plist"; sourceTree = ""; }; 0E0786DD1EFA7EAE00F77466 /* Components.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Components.plist; sourceTree = ""; }; 0E1F318520176A5F00FC1000 /* Theme+DarkPalette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Theme+DarkPalette.swift"; sourceTree = ""; }; @@ -609,7 +640,6 @@ 0EFDC1E51FE4ABAA007C0B9B /* Notification+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+App.swift"; sourceTree = ""; }; 0EFDC1EB1FE4B9DC007C0B9B /* AppConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConstants.swift; sourceTree = ""; }; 0EFDC1EE1FE4B9E6007C0B9B /* AppConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = ""; }; - 28DFD7434ECA3047EB4A2C75 /* Pods_PIA_VPN_WG_Tunnel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_WG_Tunnel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2909868F18566430002D9687 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 291C637C183EBC210039EC03 /* PIA VPN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PIA VPN.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 291C637F183EBC210039EC03 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -627,10 +657,6 @@ 3524670D26B432ED00E3F0AC /* DashboardViewController+ServerSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DashboardViewController+ServerSelection.swift"; sourceTree = ""; }; 3545E97F26AAD60C00B812CC /* ServerSelectionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionDelegate.swift; sourceTree = ""; }; 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectingTileCell.swift; sourceTree = ""; }; - 59EC919445EA680B691ACC88 /* Pods-PIA VPNTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.debug.xcconfig"; sourceTree = ""; }; - 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPNTests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests.release.xcconfig"; sourceTree = ""; }; - 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_dev.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6EB292786A3E16F34DFADE7D /* Pods_PIA_VPN_UITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN_UITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; @@ -638,7 +664,6 @@ 7EC2972D27D8B8580061C56A /* CredentialsUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsUtil.swift; sourceTree = ""; }; 7EC2972E27D8B8580061C56A /* Credentials.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Credentials.plist; sourceTree = ""; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; - 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.debug.xcconfig"; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; 82183D7A2500FD460033023F /* String+Substrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Substrings.swift"; sourceTree = ""; }; 82183D9225014FDC0033023F /* NetworkCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NetworkCollectionViewCell.xib; sourceTree = ""; }; @@ -646,7 +671,7 @@ 82183D9425014FDC0033023F /* NetworkFooterCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NetworkFooterCollectionViewCell.xib; sourceTree = ""; }; 82183D9525014FDC0033023F /* PIAHeaderCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PIAHeaderCollectionViewCell.xib; sourceTree = ""; }; 8221922A24CECFE700C24F1C /* NMTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NMTTests.swift; sourceTree = ""; }; - 822F97B3251DD53100644EF2 /* WidgetUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetUtils.swift; sourceTree = ""; }; + 822F97B3251DD53100644EF2 /* WidgetUserDefaultsDatasource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetUserDefaultsDatasource.swift; sourceTree = ""; }; 824C530724C046CA003DB740 /* ConnectionTile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionTile.swift; sourceTree = ""; }; 824C531324C04796003DB740 /* ConnectionTileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionTileCollectionViewCell.swift; sourceTree = ""; }; 824C531424C04796003DB740 /* ConnectionTileCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConnectionTileCollectionViewCell.xib; sourceTree = ""; }; @@ -658,7 +683,7 @@ 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 8269A6DE251CB5E3000B4DBF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8269A6ED251CB5ED000B4DBF /* PIAWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PIAWidgetExtension.entitlements; sourceTree = ""; }; - 8269A6FD251CBB36000B4DBF /* WidgetContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetContent.swift; sourceTree = ""; }; + 8269A6FD251CBB36000B4DBF /* WidgetInformation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetInformation.swift; sourceTree = ""; }; 826BE8ED253861BE002339F3 /* DedicatedRegionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DedicatedRegionCell.swift; sourceTree = ""; }; 8272C62626540B2100D846A8 /* ProtocolSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolSettingsViewController.swift; sourceTree = ""; }; 8272C62926551F9B00D846A8 /* SettingPopoverSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SettingPopoverSelectionView.swift; path = Settings/SettingPopoverSelectionView.swift; sourceTree = ""; }; @@ -701,12 +726,14 @@ 82CAB8FC255C115100BB08EF /* MessagesTile.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessagesTile.xib; sourceTree = ""; }; 82E20B0D24F5666E0065EFE3 /* PIAAccount.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PIAAccount.framework; path = ../account/build/bin/iOS/PIAAccountReleaseFramework/PIAAccount.framework; sourceTree = ""; }; 82F41375264E8AC20098FF4B /* SettingOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingOptions.swift; sourceTree = ""; }; - 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPN.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9F453DF1F58B8C2676041EDF /* Pods_PIA_VPNTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PIA_VPNTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A6454C66DD80FE480E48BFA8 /* Pods-PIA VPN WG Tunnel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN WG Tunnel.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN WG Tunnel/Pods-PIA VPN WG Tunnel.debug.xcconfig"; sourceTree = ""; }; - A8AE4350B939233D4C2A176D /* Pods-PIA VPN UITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN UITests.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN UITests/Pods-PIA VPN UITests.release.xcconfig"; sourceTree = ""; }; - AC26C61F8D1F24341AD67A4A /* Pods-PIA VPN Tunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN Tunnel.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN Tunnel/Pods-PIA VPN Tunnel.release.xcconfig"; sourceTree = ""; }; - B5E30A2B8E23D816328C690E /* Pods-PIA VPN WG Tunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN WG Tunnel.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN WG Tunnel/Pods-PIA VPN WG Tunnel.release.xcconfig"; sourceTree = ""; }; + AA52C59E28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAWidgetVpnDetaislRow.swift; sourceTree = ""; }; + AAE8789B28E4679500557F26 /* WidgetPersistenceDatasource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetPersistenceDatasource.swift; sourceTree = ""; }; + AAE8789E28E4696300557F26 /* PIAWidgetProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAWidgetProvider.swift; sourceTree = ""; }; + AAE878A028E46C1600557F26 /* PIAWidgetPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAWidgetPreview.swift; sourceTree = ""; }; + AAE878A228E46D2B00557F26 /* PIAWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAWidgetView.swift; sourceTree = ""; }; + AAE878A428E4723B00557F26 /* PIACircleVpnButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIACircleVpnButton.swift; sourceTree = ""; }; + AAE878A628E473A400557F26 /* PIAWidgetVpnDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAWidgetVpnDetailsView.swift; sourceTree = ""; }; + AAE878A828E4765F00557F26 /* PIAIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAIconView.swift; sourceTree = ""; }; DD052AC22419003300AD3A24 /* ProtocolTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolTableViewCell.swift; sourceTree = ""; }; DD07AACB242CBFF2000EE1A3 /* AddEmailToAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEmailToAccountViewController.swift; sourceTree = ""; }; DD125DC421E77046004ECCB6 /* QuickConnectTile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectTile.swift; sourceTree = ""; }; @@ -732,7 +759,6 @@ DD51F8B22372E494009FEED3 /* PIA-RSA-4096.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "PIA-RSA-4096.pem"; sourceTree = ""; }; DD51F8C32372E4C8009FEED3 /* PemUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PemUtil.swift; sourceTree = ""; }; DD522D21237D74380072F555 /* BooleanUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooleanUtil.swift; sourceTree = ""; }; - DD58F4B721AD579A00D043F7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; DD58F4BE21B12CFE00D043F7 /* PIAConnectionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAConnectionButton.swift; sourceTree = ""; }; DD606AB921C7A17900E0781D /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; DD606ABB21C904BB00E0781D /* PIAHotspotHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAHotspotHelper.swift; sourceTree = ""; }; @@ -776,10 +802,6 @@ DDFCFA8E21E892070081F235 /* RegionTileCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RegionTileCollectionViewCell.xib; sourceTree = ""; }; DDFCFA9321E892130081F235 /* RegionTile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionTile.swift; sourceTree = ""; }; DDFCFA9621E8921F0081F235 /* RegionTile.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RegionTile.xib; sourceTree = ""; }; - E65358880CC5204F785ADE10 /* Pods-PIA VPN Tunnel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN Tunnel.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN Tunnel/Pods-PIA VPN Tunnel.debug.xcconfig"; sourceTree = ""; }; - EFF4BBB886BD153CAD50F8E2 /* Pods-PIA VPN dev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN dev.release.xcconfig"; path = "Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev.release.xcconfig"; sourceTree = ""; }; - F489C6FC39A7285DDE2A26F7 /* Pods-PIA VPN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN/Pods-PIA VPN.debug.xcconfig"; sourceTree = ""; }; - FA8D0BE3D6E8FE3AF571EE1C /* Pods-PIA VPN UITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PIA VPN UITests.debug.xcconfig"; path = "Target Support Files/Pods-PIA VPN UITests/Pods-PIA VPN UITests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -787,7 +809,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3732E0F0C64DA8513903087D /* Pods_PIA_VPN_Tunnel.framework in Frameworks */, + AA5F0EAD28B7653F006D629D /* PIALibrary in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -795,16 +817,23 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + AABF827128AD188700CDAC64 /* AlamofireImage in Frameworks */, + AABF827628AE302300CDAC64 /* NetworkExtension.framework in Frameworks */, 0EE220571F4EF307002805AE /* Security.framework in Frameworks */, 0EE220581F4EF307002805AE /* SystemConfiguration.framework in Frameworks */, + AABF827328AE2FF500CDAC64 /* GradientProgressBar in Frameworks */, + AABF827828AE333200CDAC64 /* DZNEmptyDataSet in Frameworks */, 0EE220591F4EF307002805AE /* libz.dylib in Frameworks */, 0EE2205A1F4EF307002805AE /* CoreText.framework in Frameworks */, 0EE2205B1F4EF307002805AE /* QuartzCore.framework in Frameworks */, 0EE2205C1F4EF307002805AE /* CoreGraphics.framework in Frameworks */, + AABF826F28AD187800CDAC64 /* Popover in Frameworks */, + 0EE2205F1F4EF307002805AE /* StoreKit.framework in Frameworks */, + AA5F0EAB28B7653A006D629D /* PIALibrary in Frameworks */, 0EE2205D1F4EF307002805AE /* UIKit.framework in Frameworks */, 0EE2205E1F4EF307002805AE /* Foundation.framework in Frameworks */, - 0EE2205F1F4EF307002805AE /* StoreKit.framework in Frameworks */, - 12CE273914ED9E7533E3A51E /* Pods_PIA_VPN_dev.framework in Frameworks */, + AABF826D28AD185E00CDAC64 /* TweetNacl in Frameworks */, + AABF827528AE2FFE00CDAC64 /* SideMenu in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -812,7 +841,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 929703E310E04502651A34E9 /* Pods_PIA_VPNTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -827,6 +855,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + AA36CDC428A6711300180A33 /* Popover in Frameworks */, + AA36CDCC28A673C900180A33 /* GradientProgressBar in Frameworks */, + AA36CDCF28A6746500180A33 /* SideMenu in Frameworks */, + AA36CDBB28A6622A00180A33 /* TweetNacl in Frameworks */, + AA36CDDE28A6878000180A33 /* AlamofireImage in Frameworks */, 2909869018566430002D9687 /* Security.framework in Frameworks */, 299E58511856BD31004CFD63 /* SystemConfiguration.framework in Frameworks */, 299E585F1856C6EE004CFD63 /* libz.dylib in Frameworks */, @@ -834,10 +867,11 @@ 2985E5671856BD1200D70E28 /* QuartzCore.framework in Frameworks */, 291C6382183EBC210039EC03 /* CoreGraphics.framework in Frameworks */, 291C6384183EBC210039EC03 /* UIKit.framework in Frameworks */, + AA5F0EA928B7640B006D629D /* PIALibrary in Frameworks */, DD606ABA21C7A17900E0781D /* NetworkExtension.framework in Frameworks */, + AA36CDC928A6733500180A33 /* DZNEmptyDataSet in Frameworks */, 291C6380183EBC210039EC03 /* Foundation.framework in Frameworks */, 0E9785861DA82FF000711A24 /* StoreKit.framework in Frameworks */, - 73E0B0544DF023785B11C4B9 /* Pods_PIA_VPN.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -845,7 +879,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 520C1753B3AD005B8609F080 /* Pods_PIA_VPN_UITests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -862,8 +895,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + AA5F0EAF28B76543006D629D /* PIALibrary in Frameworks */, + AA36CDDB28A6860F00180A33 /* TweetNacl in Frameworks */, DDC8F5F923EC10E4005D19C6 /* NetworkExtension.framework in Frameworks */, - 078FA875634A84BD6728DF93 /* Pods_PIA_VPN_WG_Tunnel.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1053,7 +1087,6 @@ 0E0786DD1EFA7EAE00F77466 /* Components.plist */, 0E3C9A5D20EC004D00B199F9 /* custom.servers */, 0ED66BCF20A9918000333B35 /* staging.endpoint */, - DD58F4B721AD579A00D043F7 /* GoogleService-Info.plist */, ); path = Resources; sourceTree = ""; @@ -1082,7 +1115,6 @@ 7EB8D11427CCF4C20030B060 /* PIA VPN UITests */, 291C637E183EBC210039EC03 /* Frameworks */, 291C637D183EBC210039EC03 /* Products */, - 90B2842B07AEDA4CF8A2035B /* Pods */, ); sourceTree = ""; }; @@ -1121,12 +1153,6 @@ 0E257AC41DA45D2F0000D3C3 /* NotificationCenter.framework */, 8269A67C251CB4BA000B4DBF /* WidgetKit.framework */, 8269A67E251CB4BA000B4DBF /* SwiftUI.framework */, - 8E23999135612F39173E9C1E /* Pods_PIA_VPN.framework */, - 02823EF3398B7E2C32FA7544 /* Pods_PIA_VPN_Tunnel.framework */, - 28DFD7434ECA3047EB4A2C75 /* Pods_PIA_VPN_WG_Tunnel.framework */, - 6CA493DF6602BFDE96E0E77F /* Pods_PIA_VPN_dev.framework */, - 9F453DF1F58B8C2676041EDF /* Pods_PIA_VPNTests.framework */, - 6EB292786A3E16F34DFADE7D /* Pods_PIA_VPN_UITests.framework */, ); name = Frameworks; sourceTree = ""; @@ -1236,9 +1262,8 @@ 8269A6D8251CB5E0000B4DBF /* PIAWidget */ = { isa = PBXGroup; children = ( - 8269A6D9251CB5E0000B4DBF /* PIAWidget.swift */, - 8269A6FD251CBB36000B4DBF /* WidgetContent.swift */, - 822F97B3251DD53100644EF2 /* WidgetUtils.swift */, + AAE8789728E45C6200557F26 /* Data */, + AAE8789628E45C5D00557F26 /* Domain */, 82BAACFD25B09C9200B3C733 /* PIAWidget.intentdefinition */, 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */, 8269A6DE251CB5E3000B4DBF /* Info.plist */, @@ -1292,23 +1317,61 @@ name = Settings; sourceTree = ""; }; - 90B2842B07AEDA4CF8A2035B /* Pods */ = { + AAE8789628E45C5D00557F26 /* Domain */ = { isa = PBXGroup; children = ( - F489C6FC39A7285DDE2A26F7 /* Pods-PIA VPN.debug.xcconfig */, - 0BE4A5FC9BACD5CAB3D6CEF0 /* Pods-PIA VPN.release.xcconfig */, - E65358880CC5204F785ADE10 /* Pods-PIA VPN Tunnel.debug.xcconfig */, - AC26C61F8D1F24341AD67A4A /* Pods-PIA VPN Tunnel.release.xcconfig */, - A6454C66DD80FE480E48BFA8 /* Pods-PIA VPN WG Tunnel.debug.xcconfig */, - B5E30A2B8E23D816328C690E /* Pods-PIA VPN WG Tunnel.release.xcconfig */, - 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */, - EFF4BBB886BD153CAD50F8E2 /* Pods-PIA VPN dev.release.xcconfig */, - 59EC919445EA680B691ACC88 /* Pods-PIA VPNTests.debug.xcconfig */, - 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */, - FA8D0BE3D6E8FE3AF571EE1C /* Pods-PIA VPN UITests.debug.xcconfig */, - A8AE4350B939233D4C2A176D /* Pods-PIA VPN UITests.release.xcconfig */, - ); - path = Pods; + AAE8789D28E468F400557F26 /* Widget */, + AAE8789A28E45EC500557F26 /* UI */, + ); + path = Domain; + sourceTree = ""; + }; + AAE8789728E45C6200557F26 /* Data */ = { + isa = PBXGroup; + children = ( + AAE8789928E45DDE00557F26 /* Model */, + AAE8789828E45DD500557F26 /* Cache */, + ); + path = Data; + sourceTree = ""; + }; + AAE8789828E45DD500557F26 /* Cache */ = { + isa = PBXGroup; + children = ( + 822F97B3251DD53100644EF2 /* WidgetUserDefaultsDatasource.swift */, + AAE8789B28E4679500557F26 /* WidgetPersistenceDatasource.swift */, + ); + path = Cache; + sourceTree = ""; + }; + AAE8789928E45DDE00557F26 /* Model */ = { + isa = PBXGroup; + children = ( + 8269A6FD251CBB36000B4DBF /* WidgetInformation.swift */, + ); + path = Model; + sourceTree = ""; + }; + AAE8789A28E45EC500557F26 /* UI */ = { + isa = PBXGroup; + children = ( + AAE878A228E46D2B00557F26 /* PIAWidgetView.swift */, + AAE878A428E4723B00557F26 /* PIACircleVpnButton.swift */, + AAE878A628E473A400557F26 /* PIAWidgetVpnDetailsView.swift */, + AAE878A828E4765F00557F26 /* PIAIconView.swift */, + AA52C59E28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift */, + ); + path = UI; + sourceTree = ""; + }; + AAE8789D28E468F400557F26 /* Widget */ = { + isa = PBXGroup; + children = ( + 8269A6D9251CB5E0000B4DBF /* PIAWidget.swift */, + AAE8789E28E4696300557F26 /* PIAWidgetProvider.swift */, + AAE878A028E46C1600557F26 /* PIAWidgetPreview.swift */, + ); + path = Widget; sourceTree = ""; }; DD1C137E21E6073A004004B3 /* Tiles */ = { @@ -1454,7 +1517,6 @@ isa = PBXNativeTarget; buildConfigurationList = 0E67FC2E1E3F802D00EF9929 /* Build configuration list for PBXNativeTarget "PIA VPN Tunnel" */; buildPhases = ( - 5FDBE92B797C696CA4F5D6E3 /* [CP] Check Pods Manifest.lock */, 0E67FC1E1E3F802D00EF9929 /* Sources */, 0E67FC1F1E3F802D00EF9929 /* Frameworks */, 0E67FC201E3F802D00EF9929 /* Resources */, @@ -1464,6 +1526,9 @@ dependencies = ( ); name = "PIA VPN Tunnel"; + packageProductDependencies = ( + AA5F0EAC28B7653F006D629D /* PIALibrary */, + ); productName = "PIA OpenVPN"; productReference = 0E67FC221E3F802D00EF9929 /* PIA VPN Tunnel.appex */; productType = "com.apple.product-type.app-extension"; @@ -1472,7 +1537,6 @@ isa = PBXNativeTarget; buildConfigurationList = 0EE220771F4EF307002805AE /* Build configuration list for PBXNativeTarget "PIA VPN dev" */; buildPhases = ( - C26EC034B9AB0A02D7164F82 /* [CP] Check Pods Manifest.lock */, 0EF9ABF71FF792DD005E1418 /* SwiftGen */, 0EE2200F1F4EF307002805AE /* Sources */, 0EE220561F4EF307002805AE /* Frameworks */, @@ -1480,16 +1544,25 @@ 0EE220711F4EF307002805AE /* Download Latest Regions List */, 0EE220731F4EF307002805AE /* Embed App Extensions */, 0E60FF9E1F4F50A2001D30DB /* Embed Frameworks */, - DD58F4B921AE84B300D043F7 /* ShellScript */, - DA22F2CE535A37D07F2DE296 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( 0EE2200C1F4EF307002805AE /* PBXTargetDependency */, 0EFB6078203D7A2C0095398C /* PBXTargetDependency */, + AABF827A28AE336000CDAC64 /* PBXTargetDependency */, + AABF827C28AE336F00CDAC64 /* PBXTargetDependency */, ); name = "PIA VPN dev"; + packageProductDependencies = ( + AABF826C28AD185E00CDAC64 /* TweetNacl */, + AABF826E28AD187800CDAC64 /* Popover */, + AABF827028AD188700CDAC64 /* AlamofireImage */, + AABF827228AE2FF500CDAC64 /* GradientProgressBar */, + AABF827428AE2FFE00CDAC64 /* SideMenu */, + AABF827728AE333200CDAC64 /* DZNEmptyDataSet */, + AA5F0EAA28B7653A006D629D /* PIALibrary */, + ); productName = "PIA VPN"; productReference = 0EE2207A1F4EF307002805AE /* PIA VPN dev.app */; productType = "com.apple.product-type.application"; @@ -1498,11 +1571,9 @@ isa = PBXNativeTarget; buildConfigurationList = 0EEE1BF01E4F6EF400397DE2 /* Build configuration list for PBXNativeTarget "PIA VPNTests" */; buildPhases = ( - B6830C11847908188577F5A5 /* [CP] Check Pods Manifest.lock */, 0EEE1BE31E4F6EF400397DE2 /* Sources */, 0EEE1BE41E4F6EF400397DE2 /* Frameworks */, 0EEE1BE51E4F6EF400397DE2 /* Resources */, - 6B3D203586AD5EDAB8192C77 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1536,13 +1607,11 @@ isa = PBXNativeTarget; buildConfigurationList = 291C63AE183EBC220039EC03 /* Build configuration list for PBXNativeTarget "PIA VPN" */; buildPhases = ( - 11E8771AD2F21EF4B428154B /* [CP] Check Pods Manifest.lock */, 291C6378183EBC210039EC03 /* Sources */, 291C6379183EBC210039EC03 /* Frameworks */, 291C637A183EBC210039EC03 /* Resources */, 2931563B18513F6500E769A7 /* Download Latest Regions List */, 0E98CF0E1DCBFB3B003F1986 /* Embed App Extensions */, - 7103EE1907DBBDBB001EC846 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1553,6 +1622,15 @@ 8269A6E2251CB5E3000B4DBF /* PBXTargetDependency */, ); name = "PIA VPN"; + packageProductDependencies = ( + AA36CDBA28A6622A00180A33 /* TweetNacl */, + AA36CDC328A6711300180A33 /* Popover */, + AA36CDC828A6733500180A33 /* DZNEmptyDataSet */, + AA36CDCB28A673C900180A33 /* GradientProgressBar */, + AA36CDCE28A6746500180A33 /* SideMenu */, + AA36CDDD28A6878000180A33 /* AlamofireImage */, + AA5F0EA828B7640B006D629D /* PIALibrary */, + ); productName = "PIA VPN"; productReference = 291C637C183EBC210039EC03 /* PIA VPN.app */; productType = "com.apple.product-type.application"; @@ -1561,11 +1639,9 @@ isa = PBXNativeTarget; buildConfigurationList = 7EB8D11D27CCF4C20030B060 /* Build configuration list for PBXNativeTarget "PIA VPN UITests" */; buildPhases = ( - 5B1D3ED823A958D1046A4FB6 /* [CP] Check Pods Manifest.lock */, 7EB8D10F27CCF4C20030B060 /* Sources */, 7EB8D11027CCF4C20030B060 /* Frameworks */, 7EB8D11127CCF4C20030B060 /* Resources */, - CDF5626F60268FA0C4FBC24A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1598,7 +1674,6 @@ isa = PBXNativeTarget; buildConfigurationList = DDC8F5F723EC1070005D19C6 /* Build configuration list for PBXNativeTarget "PIA VPN WG Tunnel" */; buildPhases = ( - 31FE519987479E33FFCDAB88 /* [CP] Check Pods Manifest.lock */, DDC8F5E823EC106F005D19C6 /* Sources */, DDC8F5E923EC106F005D19C6 /* Frameworks */, DDC8F5EA23EC106F005D19C6 /* Resources */, @@ -1608,6 +1683,10 @@ dependencies = ( ); name = "PIA VPN WG Tunnel"; + packageProductDependencies = ( + AA36CDDA28A6860F00180A33 /* TweetNacl */, + AA5F0EAE28B76543006D629D /* PIALibrary */, + ); productName = "PIA VPN WG Tunnel"; productReference = DDC8F5EC23EC106F005D19C6 /* PIA VPN WG Tunnel.appex */; productType = "com.apple.product-type.app-extension"; @@ -1735,6 +1814,15 @@ Base, ); mainGroup = 291C6373183EBC210039EC03; + packageReferences = ( + AA36CDB928A6622A00180A33 /* XCRemoteSwiftPackageReference "tweetnacl-swiftwrap" */, + AA36CDC228A6711300180A33 /* XCRemoteSwiftPackageReference "Popover" */, + AA36CDC728A6733500180A33 /* XCRemoteSwiftPackageReference "DZNEmptyDataSet" */, + AA36CDCA28A673C900180A33 /* XCRemoteSwiftPackageReference "GradientProgressBar" */, + AA36CDCD28A6746500180A33 /* XCRemoteSwiftPackageReference "SideMenu" */, + AA36CDDC28A6878000180A33 /* XCRemoteSwiftPackageReference "AlamofireImage" */, + AA5F0EA728B7640B006D629D /* XCRemoteSwiftPackageReference "cpz_pia-mobile_ios_client-library-apple" */, + ); productRefGroup = 291C637D183EBC210039EC03 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -1772,7 +1860,6 @@ 0E0715E7201CBB7100D6F666 /* Flags-dev.plist in Resources */, DD1C138A21E60C63004004B3 /* IPTile.xib in Resources */, 82C9F3CF25C43863005039F9 /* ActiveDedicatedIpHeaderViewCell.xib in Resources */, - DD58F4B821AD579A00D043F7 /* GoogleService-Info.plist in Resources */, 82CAB8F5255C0CD100BB08EF /* MessagesTileCollectionViewCell.xib in Resources */, DD76291421ECBD8B0092DF50 /* SubscriptionTileCollectionViewCell.xib in Resources */, DD51F8BA2372E494009FEED3 /* PIA-RSA-4096.pem in Resources */, @@ -1926,28 +2013,6 @@ shellPath = /bin/sh; shellScript = "if which swiftgen >/dev/null; then\n set -e\n swiftgen\nelse\n echo \"warning: SwiftGen not installed, download it from https://github.com/SwiftGen/SwiftGen\"\nfi\n"; }; - 11E8771AD2F21EF4B428154B /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PIA VPN-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 2931563B18513F6500E769A7 /* Download Latest Regions List */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 12; @@ -1962,194 +2027,6 @@ shellPath = /bin/bash; shellScript = "# update max once an hour\nset -e\n\nREGIONS_FILE=\"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Regions.json\"\n\nif [[ ! -r \"${REGIONS_FILE}\" || $(find \"${REGIONS_FILE}\" -mmin +60) ]]; then\necho \"downloading regions list to ${REGIONS_FILE}\"\ncurl -sfo \"/tmp/piaios.tmp\" \"https://serverlist.piaservers.net/vpninfo/servers/v6\"\n\nif [ $? -ne 0 ]; then\necho \"failed to fetch regions list from server\"\nexit 1\nfi\n\nhead -n 1 \"/tmp/piaios.tmp\" > \"${REGIONS_FILE}\"\nrm \"/tmp/piaios.tmp\"\nfi\n"; }; - 31FE519987479E33FFCDAB88 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PIA VPN WG Tunnel-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 5B1D3ED823A958D1046A4FB6 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PIA VPN UITests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 5FDBE92B797C696CA4F5D6E3 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PIA VPN Tunnel-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 6B3D203586AD5EDAB8192C77 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PIA VPNTests/Pods-PIA VPNTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 7103EE1907DBBDBB001EC846 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PIA VPN/Pods-PIA VPN-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - B6830C11847908188577F5A5 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PIA VPNTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C26EC034B9AB0A02D7164F82 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PIA VPN dev-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - CDF5626F60268FA0C4FBC24A /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PIA VPN UITests/Pods-PIA VPN UITests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - DA22F2CE535A37D07F2DE296 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PIA VPN dev/Pods-PIA VPN dev-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - DD58F4B921AE84B300D043F7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${PODS_ROOT}/Fabric/run\" 970f0999b1ac221604548824b2a49d005754ca32 5606a7ca9a2b622029ba4b67c2adf2e419d60e5460b92564806db96e3141bb72\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -2459,10 +2336,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8269A6FE251CBB36000B4DBF /* WidgetContent.swift in Sources */, - 822F97B4251DD53100644EF2 /* WidgetUtils.swift in Sources */, + 8269A6FE251CBB36000B4DBF /* WidgetInformation.swift in Sources */, + 822F97B4251DD53100644EF2 /* WidgetUserDefaultsDatasource.swift in Sources */, 8269A6DA251CB5E0000B4DBF /* PIAWidget.swift in Sources */, + AAE878A928E4765F00557F26 /* PIAIconView.swift in Sources */, + AAE8789F28E4696300557F26 /* PIAWidgetProvider.swift in Sources */, + AAE878A128E46C1600557F26 /* PIAWidgetPreview.swift in Sources */, + AAE878A528E4723B00557F26 /* PIACircleVpnButton.swift in Sources */, 82BAACFB25B09C9200B3C733 /* PIAWidget.intentdefinition in Sources */, + AAE8789C28E4679500557F26 /* WidgetPersistenceDatasource.swift in Sources */, + AAE878A328E46D2B00557F26 /* PIAWidgetView.swift in Sources */, + AAE878A728E473A400557F26 /* PIAWidgetVpnDetailsView.swift in Sources */, + AA52C59F28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2517,6 +2402,16 @@ target = 8269A6D4251CB5E0000B4DBF /* PIAWidgetExtension */; targetProxy = 8269A6E1251CB5E3000B4DBF /* PBXContainerItemProxy */; }; + AABF827A28AE336000CDAC64 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DDC8F5EB23EC106F005D19C6 /* PIA VPN WG Tunnel */; + targetProxy = AABF827928AE336000CDAC64 /* PBXContainerItemProxy */; + }; + AABF827C28AE336F00CDAC64 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8269A6D4251CB5E0000B4DBF /* PIAWidgetExtension */; + targetProxy = AABF827B28AE336F00CDAC64 /* PBXContainerItemProxy */; + }; DDC8F5F323EC1070005D19C6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DDC8F5EB23EC106F005D19C6 /* PIA VPN WG Tunnel */; @@ -2596,7 +2491,6 @@ /* Begin XCBuildConfiguration section */ 0E67FC2C1E3F802D00EF9929 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E65358880CC5204F785ADE10 /* Pods-PIA VPN Tunnel.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_MODULES = YES; @@ -2604,14 +2498,18 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN Tunnel/PIA VPN Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2626,7 +2524,6 @@ }; 0E67FC2D1E3F802D00EF9929 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AC26C61F8D1F24341AD67A4A /* Pods-PIA VPN Tunnel.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_MODULES = YES; @@ -2635,14 +2532,18 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2655,7 +2556,6 @@ }; 0EE220781F4EF307002805AE /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7F6FF659836C1192332662A8 /* Pods-PIA VPN dev.debug.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = NO; ARCHS = "$(ARCHS_STANDARD)"; @@ -2664,9 +2564,9 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEVELOPMENT_TEAM = 5357M5NW9W; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -2676,12 +2576,15 @@ ); INFOPLIST_FILE = "PIA VPN/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.18.0; + MARKETING_VERSION = 3.19.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2697,7 +2600,6 @@ }; 0EE220791F4EF307002805AE /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EFF4BBB886BD153CAD50F8E2 /* Pods-PIA VPN dev.release.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = NO; ARCHS = "$(ARCHS_STANDARD)"; @@ -2706,9 +2608,9 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEVELOPMENT_TEAM = 5357M5NW9W; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -2718,12 +2620,15 @@ ); INFOPLIST_FILE = "PIA VPN/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.18.0; + MARKETING_VERSION = 3.19.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2738,7 +2643,6 @@ }; 0EEE1BEE1E4F6EF400397DE2 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 59EC919445EA680B691ACC88 /* Pods-PIA VPNTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -2753,7 +2657,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPNTests/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPNTests"; "PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = ""; @@ -2770,7 +2678,6 @@ }; 0EEE1BEF1E4F6EF400397DE2 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 678A887CFC02122F4FB83216 /* Pods-PIA VPNTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -2786,7 +2693,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPNTests/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPNTests"; "PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = ""; @@ -2809,15 +2720,19 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2842,15 +2757,19 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2964,7 +2883,8 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; @@ -2972,7 +2892,6 @@ }; 291C63AF183EBC220039EC03 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F489C6FC39A7285DDE2A26F7 /* Pods-PIA VPN.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ARCHS = "$(ARCHS_STANDARD)"; @@ -2981,9 +2900,9 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEVELOPMENT_TEAM = 5357M5NW9W; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/PIAAccount/frameworks/iPhone\"", @@ -2991,12 +2910,15 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; INFOPLIST_FILE = "PIA VPN/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.18.0; + MARKETING_VERSION = 3.19.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -3009,7 +2931,6 @@ }; 291C63B0183EBC220039EC03 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0BE4A5FC9BACD5CAB3D6CEF0 /* Pods-PIA VPN.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ARCHS = "$(ARCHS_STANDARD)"; @@ -3018,9 +2939,9 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEVELOPMENT_TEAM = 5357M5NW9W; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/PIAAccount/frameworks/iPhone\"", @@ -3028,12 +2949,15 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; INFOPLIST_FILE = "PIA VPN/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.18.0; + MARKETING_VERSION = 3.19.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3045,7 +2969,6 @@ }; 7EB8D11B27CCF4C20030B060 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FA8D0BE3D6E8FE3AF571EE1C /* Pods-PIA VPN UITests.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3062,7 +2985,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -3080,7 +3007,6 @@ }; 7EB8D11C27CCF4C20030B060 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A8AE4350B939233D4C2A176D /* Pods-PIA VPN UITests.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3098,7 +3024,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -3127,15 +3057,19 @@ CODE_SIGN_ENTITLEMENTS = PIAWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3165,15 +3099,19 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = PIAWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3187,7 +3125,6 @@ }; DDC8F5F523EC1070005D19C6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A6454C66DD80FE480E48BFA8 /* Pods-PIA VPN WG Tunnel.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3198,15 +3135,19 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN WG Tunnel/PIA_VPN_WG_Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3222,7 +3163,6 @@ }; DDC8F5F623EC1070005D19C6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B5E30A2B8E23D816328C690E /* Pods-PIA VPN WG Tunnel.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3234,15 +3174,19 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19748; + CURRENT_PROJECT_VERSION = 20030; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 3.19.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3339,6 +3283,153 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + AA36CDB928A6622A00180A33 /* XCRemoteSwiftPackageReference "tweetnacl-swiftwrap" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/bitmark-inc/tweetnacl-swiftwrap.git"; + requirement = { + kind = exactVersion; + version = 1.1.0; + }; + }; + AA36CDC228A6711300180A33 /* XCRemoteSwiftPackageReference "Popover" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sarathsarah/Popover.git"; + requirement = { + branch = master; + kind = branch; + }; + }; + AA36CDC728A6733500180A33 /* XCRemoteSwiftPackageReference "DZNEmptyDataSet" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/dzenbot/DZNEmptyDataSet.git"; + requirement = { + branch = master; + kind = branch; + }; + }; + AA36CDCA28A673C900180A33 /* XCRemoteSwiftPackageReference "GradientProgressBar" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/fxm90/GradientProgressBar.git"; + requirement = { + kind = exactVersion; + version = 2.0.3; + }; + }; + AA36CDCD28A6746500180A33 /* XCRemoteSwiftPackageReference "SideMenu" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/jonkykong/SideMenu.git"; + requirement = { + kind = exactVersion; + version = 6.5.0; + }; + }; + AA36CDDC28A6878000180A33 /* XCRemoteSwiftPackageReference "AlamofireImage" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Alamofire/AlamofireImage.git"; + requirement = { + kind = exactVersion; + version = 4.0.0; + }; + }; + AA5F0EA728B7640B006D629D /* XCRemoteSwiftPackageReference "cpz_pia-mobile_ios_client-library-apple" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "git@github.com:xvpn/cpz_pia-mobile_ios_client-library-apple.git"; + requirement = { + kind = revision; + revision = 289c5d959bd8e47906306bf75002d6c88266e09d; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + AA36CDBA28A6622A00180A33 /* TweetNacl */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDB928A6622A00180A33 /* XCRemoteSwiftPackageReference "tweetnacl-swiftwrap" */; + productName = TweetNacl; + }; + AA36CDC328A6711300180A33 /* Popover */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDC228A6711300180A33 /* XCRemoteSwiftPackageReference "Popover" */; + productName = Popover; + }; + AA36CDC828A6733500180A33 /* DZNEmptyDataSet */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDC728A6733500180A33 /* XCRemoteSwiftPackageReference "DZNEmptyDataSet" */; + productName = DZNEmptyDataSet; + }; + AA36CDCB28A673C900180A33 /* GradientProgressBar */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDCA28A673C900180A33 /* XCRemoteSwiftPackageReference "GradientProgressBar" */; + productName = GradientProgressBar; + }; + AA36CDCE28A6746500180A33 /* SideMenu */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDCD28A6746500180A33 /* XCRemoteSwiftPackageReference "SideMenu" */; + productName = SideMenu; + }; + AA36CDDA28A6860F00180A33 /* TweetNacl */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDB928A6622A00180A33 /* XCRemoteSwiftPackageReference "tweetnacl-swiftwrap" */; + productName = TweetNacl; + }; + AA36CDDD28A6878000180A33 /* AlamofireImage */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDDC28A6878000180A33 /* XCRemoteSwiftPackageReference "AlamofireImage" */; + productName = AlamofireImage; + }; + AA5F0EA828B7640B006D629D /* PIALibrary */ = { + isa = XCSwiftPackageProductDependency; + package = AA5F0EA728B7640B006D629D /* XCRemoteSwiftPackageReference "cpz_pia-mobile_ios_client-library-apple" */; + productName = PIALibrary; + }; + AA5F0EAA28B7653A006D629D /* PIALibrary */ = { + isa = XCSwiftPackageProductDependency; + package = AA5F0EA728B7640B006D629D /* XCRemoteSwiftPackageReference "cpz_pia-mobile_ios_client-library-apple" */; + productName = PIALibrary; + }; + AA5F0EAC28B7653F006D629D /* PIALibrary */ = { + isa = XCSwiftPackageProductDependency; + package = AA5F0EA728B7640B006D629D /* XCRemoteSwiftPackageReference "cpz_pia-mobile_ios_client-library-apple" */; + productName = PIALibrary; + }; + AA5F0EAE28B76543006D629D /* PIALibrary */ = { + isa = XCSwiftPackageProductDependency; + package = AA5F0EA728B7640B006D629D /* XCRemoteSwiftPackageReference "cpz_pia-mobile_ios_client-library-apple" */; + productName = PIALibrary; + }; + AABF826C28AD185E00CDAC64 /* TweetNacl */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDB928A6622A00180A33 /* XCRemoteSwiftPackageReference "tweetnacl-swiftwrap" */; + productName = TweetNacl; + }; + AABF826E28AD187800CDAC64 /* Popover */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDC228A6711300180A33 /* XCRemoteSwiftPackageReference "Popover" */; + productName = Popover; + }; + AABF827028AD188700CDAC64 /* AlamofireImage */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDDC28A6878000180A33 /* XCRemoteSwiftPackageReference "AlamofireImage" */; + productName = AlamofireImage; + }; + AABF827228AE2FF500CDAC64 /* GradientProgressBar */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDCA28A673C900180A33 /* XCRemoteSwiftPackageReference "GradientProgressBar" */; + productName = GradientProgressBar; + }; + AABF827428AE2FFE00CDAC64 /* SideMenu */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDCD28A6746500180A33 /* XCRemoteSwiftPackageReference "SideMenu" */; + productName = SideMenu; + }; + AABF827728AE333200CDAC64 /* DZNEmptyDataSet */ = { + isa = XCSwiftPackageProductDependency; + package = AA36CDC728A6733500180A33 /* XCRemoteSwiftPackageReference "DZNEmptyDataSet" */; + productName = DZNEmptyDataSet; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 291C6374183EBC210039EC03 /* Project object */; } diff --git a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme index 7af92a33e..abcf43837 100644 --- a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme +++ b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN WG Tunnel.xcscheme @@ -84,6 +84,7 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES" + askForAppToLaunch = "Yes" launchAutomaticallySubstyle = "2"> diff --git a/PIA VPN.xcworkspace/contents.xcworkspacedata b/PIA VPN.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 0ba381045..000000000 --- a/PIA VPN.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/PIA VPN.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PIA VPN.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/PIA VPN.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5e..000000000 --- a/PIA VPN.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/PIA VPN/AccountObserver.swift b/PIA VPN/AccountObserver.swift index 3a6d3ee0d..2588e36ef 100644 --- a/PIA VPN/AccountObserver.swift +++ b/PIA VPN/AccountObserver.swift @@ -23,6 +23,7 @@ import Foundation import PIALibrary import SwiftyBeaver +import UIKit private let log = SwiftyBeaver.self diff --git a/PIA VPN/AppConfiguration.swift b/PIA VPN/AppConfiguration.swift index 39f7881d6..ce0f3cc10 100644 --- a/PIA VPN/AppConfiguration.swift +++ b/PIA VPN/AppConfiguration.swift @@ -22,7 +22,9 @@ import Foundation import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN +import UIKit struct AppConfiguration { private static let customClientEnvironment: Client.Environment = .staging @@ -65,7 +67,7 @@ struct AppConfiguration { static let profileName = "Private Internet Access" - static let piaDefaultConfigurationBuilder: OpenVPNTunnelProvider.ConfigurationBuilder = { + static let piaDefaultConfigurationBuilder: OpenVPNProvider.ConfigurationBuilder = { var sessionBuilder = OpenVPN.ConfigurationBuilder() sessionBuilder.renegotiatesAfter = piaRenegotiationInterval sessionBuilder.cipher = .aes128gcm @@ -76,7 +78,7 @@ struct AppConfiguration { sessionBuilder.endpointProtocols = piaAutomaticProtocols sessionBuilder.dnsServers = [] sessionBuilder.usesPIAPatches = true - var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build()) + var builder = OpenVPNProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build()) if AppPreferences.shared.useSmallPackets { builder.sessionConfiguration.mtu = AppConstants.OpenVPNPacketSize.smallPacketSize } else { diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index a42c975cf..f79d6a55c 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -22,9 +22,11 @@ import Foundation import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN import SwiftyBeaver import Intents +import UIKit private let log = SwiftyBeaver.self @@ -635,8 +637,8 @@ class AppPreferences { func migrateOVPN() { - guard let currentOpenVPNConfiguration = Client.preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration ?? - Client.preferences.defaults.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration else { + guard let currentOpenVPNConfiguration = Client.preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration ?? + Client.preferences.defaults.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration else { return } @@ -658,7 +660,7 @@ class AppPreferences { } if shouldUpdate { - var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: pendingOpenVPNConfiguration.build()) + var builder = OpenVPNProvider.ConfigurationBuilder(sessionConfiguration: pendingOpenVPNConfiguration.build()) if AppPreferences.shared.useSmallPackets { builder.sessionConfiguration.mtu = AppConstants.OpenVPNPacketSize.smallPacketSize } else { diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index 00db669dc..74a200c12 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -22,14 +22,10 @@ import Foundation import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN import SwiftyBeaver import PIAWireguard -#if PIA_DEV -import Firebase -import Fabric -import Crashlytics -#endif class Bootstrapper { @@ -50,21 +46,13 @@ class Bootstrapper { let console = ConsoleDestination() #if PIA_DEV console.minLevel = .debug - - if let path = Bundle.main.url(forResource: "GoogleService-Info", withExtension: "plist"), - let plist = NSDictionary(contentsOf: path) as? [String: Any], - plist.count > 0 { - FirebaseApp.configure() - Fabric.sharedSDK().debug = true - Fabric.with([Crashlytics.self()]) - } #else console.minLevel = .info #endif SwiftyBeaver.addDestination(console) // Load the database first - Client.database = Client.Database(team: AppConstants.teamId, group: AppConstants.appGroup) + Client.database = Client.Database(group: AppConstants.appGroup) // Check if should clean the account after delete the app and install again if Client.providers.accountProvider.shouldCleanAccount { diff --git a/PIA VPN/CardFactory.swift b/PIA VPN/CardFactory.swift index 90f16e03b..5faf0b2ae 100644 --- a/PIA VPN/CardFactory.swift +++ b/PIA VPN/CardFactory.swift @@ -22,6 +22,7 @@ import Foundation import PIALibrary import PIAWireguard +import UIKit struct CardFactory { diff --git a/PIA VPN/CustomDNSSettingsViewController.swift b/PIA VPN/CustomDNSSettingsViewController.swift index 1066cde57..d6527fd7c 100644 --- a/PIA VPN/CustomDNSSettingsViewController.swift +++ b/PIA VPN/CustomDNSSettingsViewController.swift @@ -22,6 +22,7 @@ import Foundation import PIALibrary +import UIKit class CustomDNSSettingsViewController: AutolayoutViewController { diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 9e9b0e8a8..c7601fae4 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -365,7 +365,8 @@ class DashboardViewController: AutolayoutViewController { } @objc private func openMenu(_ sender: Any?) { - perform(segue: StoryboardSegue.Main.menuSegueIdentifier) + Theme.current.applySideMenu() + present(SideMenuManager.default.leftMenuNavigationController!, animated: true) } @objc private func closeTileEditingMode(_ sender: Any?) { @@ -493,7 +494,7 @@ class DashboardViewController: AutolayoutViewController { override func prepare(for segue: UIStoryboardSegue, sender: Any?) { navigationItem.setEmptyBackButton() - if let sideMenu = segue.destination as? UISideMenuNavigationController { + if let sideMenu = segue.destination as? SideMenuNavigationController { setMenuDelegate(menuNavigationController: sideMenu) } else if let nmt = segue.destination as? TrustedNetworksViewController { nmt.shouldReconnectAutomatically = true diff --git a/PIA VPN/DedicatedIpViewController.swift b/PIA VPN/DedicatedIpViewController.swift index 03b99930d..97cf12a0d 100644 --- a/PIA VPN/DedicatedIpViewController.swift +++ b/PIA VPN/DedicatedIpViewController.swift @@ -22,6 +22,7 @@ import Foundation import PIALibrary import SwiftyBeaver +import UIKit private let log = SwiftyBeaver.self diff --git a/PIA VPN/MessagesCommands.swift b/PIA VPN/MessagesCommands.swift index db8b58bb2..04ac97ffe 100644 --- a/PIA VPN/MessagesCommands.swift +++ b/PIA VPN/MessagesCommands.swift @@ -23,7 +23,8 @@ import Foundation import UIKit import PIALibrary import PIAWireguard -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN protocol Command { func execute() @@ -157,8 +158,8 @@ class ActionCommand: Command { private func activateOpenVPN() { let preferences = Client.preferences.editable() - guard let currentVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration ?? - Client.preferences.defaults.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration else { + guard let currentVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration ?? + Client.preferences.defaults.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration else { fatalError("No default VPN custom configuration provided for PIA OpenVPN protocol") } diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 142267719..45e7f6d5e 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -22,6 +22,7 @@ import Foundation import PIALibrary import PIAAccount +import UIKit public class MessagesManager: NSObject { diff --git a/PIA VPN/PIAHotspotHelper.swift b/PIA VPN/PIAHotspotHelper.swift index c9ee827b0..b96a0c152 100644 --- a/PIA VPN/PIAHotspotHelper.swift +++ b/PIA VPN/PIAHotspotHelper.swift @@ -24,6 +24,7 @@ import Foundation import NetworkExtension import PIALibrary import SwiftyBeaver +import UIKit private let log = SwiftyBeaver.self diff --git a/PIA VPN/PIAPageControl.swift b/PIA VPN/PIAPageControl.swift index de0b14a2d..77ef66359 100644 --- a/PIA VPN/PIAPageControl.swift +++ b/PIA VPN/PIAPageControl.swift @@ -20,7 +20,7 @@ // Internet Access iOS Client. If not, see . // -import FXPageControl +import __PIALibraryNative class PIAPageControl: FXPageControl { override func draw(_ rect: CGRect) { diff --git a/PIA VPN/PIATunnelProvider+UI.swift b/PIA VPN/PIATunnelProvider+UI.swift index b97d7c017..3d36081b5 100644 --- a/PIA VPN/PIATunnelProvider+UI.swift +++ b/PIA VPN/PIATunnelProvider+UI.swift @@ -21,7 +21,8 @@ // import Foundation -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN extension SocketType: CustomStringConvertible { public var description: String { diff --git a/PIA VPN/PemUtil.swift b/PIA VPN/PemUtil.swift index 2220f398f..e952cb829 100644 --- a/PIA VPN/PemUtil.swift +++ b/PIA VPN/PemUtil.swift @@ -22,7 +22,8 @@ // import Foundation -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN public extension OpenVPN.Configuration { diff --git a/PIA VPN/ProtocolSettingsViewController.swift b/PIA VPN/ProtocolSettingsViewController.swift index d0b165d14..fe38f7147 100644 --- a/PIA VPN/ProtocolSettingsViewController.swift +++ b/PIA VPN/ProtocolSettingsViewController.swift @@ -23,7 +23,8 @@ import UIKit import Popover import SwiftyBeaver import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN class ProtocolSettingsViewController: PIABaseSettingsViewController { diff --git a/PIA VPN/Server+UI.swift b/PIA VPN/Server+UI.swift index 7d2cbf519..21763a819 100644 --- a/PIA VPN/Server+UI.swift +++ b/PIA VPN/Server+UI.swift @@ -22,7 +22,8 @@ import Foundation import PIALibrary -import AlamofireImage +import Alamofire +import UIKit extension Server: CustomStringConvertible { func name(forStatus status: VPNStatus) -> String { diff --git a/PIA VPN/Settings/HelpSettingsViewController.swift b/PIA VPN/Settings/HelpSettingsViewController.swift index 1af28ff6a..fec85269c 100644 --- a/PIA VPN/Settings/HelpSettingsViewController.swift +++ b/PIA VPN/Settings/HelpSettingsViewController.swift @@ -232,7 +232,7 @@ extension HelpSettingsViewController: UITableViewDelegate, UITableViewDataSource } @objc private func showShareDataInformation() { - let storyboard = UIStoryboard(name: "Signup", bundle: Bundle(for: ShareDataInformationViewController.self)) + let storyboard = Client.signupStoryboard() let shareDataInformationViewController = storyboard.instantiateViewController(withIdentifier: ViewControllerIdentifiers.shareDataInformation) presentModally(viewController: shareDataInformationViewController) } diff --git a/PIA VPN/Settings/NetworkSettingsViewController.swift b/PIA VPN/Settings/NetworkSettingsViewController.swift index 6deea4a3b..37719ff3c 100644 --- a/PIA VPN/Settings/NetworkSettingsViewController.swift +++ b/PIA VPN/Settings/NetworkSettingsViewController.swift @@ -23,7 +23,8 @@ import UIKit import Popover import SwiftyBeaver import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN import PIAWireguard protocol DNSSettingsDelegate: AnyObject { diff --git a/PIA VPN/Settings/PIABaseSettingsViewController.swift b/PIA VPN/Settings/PIABaseSettingsViewController.swift index 960423357..33e3c9a31 100644 --- a/PIA VPN/Settings/PIABaseSettingsViewController.swift +++ b/PIA VPN/Settings/PIABaseSettingsViewController.swift @@ -21,7 +21,8 @@ import UIKit import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN class PIABaseSettingsViewController: AutolayoutViewController { diff --git a/PIA VPN/Settings/SettingPopoverSelectionView.swift b/PIA VPN/Settings/SettingPopoverSelectionView.swift index 0cdbed0b6..df42d0b07 100644 --- a/PIA VPN/Settings/SettingPopoverSelectionView.swift +++ b/PIA VPN/Settings/SettingPopoverSelectionView.swift @@ -22,7 +22,8 @@ import UIKit import PIALibrary import Popover -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN class SettingPopoverSelectionView: UIView { diff --git a/PIA VPN/SettingsDelegate.swift b/PIA VPN/SettingsDelegate.swift index be9293aba..ae1aec848 100644 --- a/PIA VPN/SettingsDelegate.swift +++ b/PIA VPN/SettingsDelegate.swift @@ -21,7 +21,8 @@ import Foundation import PIAWireguard -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN protocol SettingsDelegate: AnyObject { diff --git a/PIA VPN/SettingsViewController.swift b/PIA VPN/SettingsViewController.swift index bdf1812dd..af0dd1093 100644 --- a/PIA VPN/SettingsViewController.swift +++ b/PIA VPN/SettingsViewController.swift @@ -22,11 +22,13 @@ import UIKit import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN import SafariServices import SwiftyBeaver import PIAWireguard import WidgetKit +import AlamofireImage private let log = SwiftyBeaver.self @@ -264,7 +266,7 @@ class SettingsViewController: AutolayoutViewController, SettingsDelegate { preferences.ikeV2PacketSize = pendingPreferences.ikeV2PacketSize preferences.commit() - guard let currentOpenVPNConfiguration = pendingPreferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration else { + guard let currentOpenVPNConfiguration = pendingPreferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration else { fatalError("No default VPN custom configuration provided for PIA protocol") } AppPreferences.shared.reset() @@ -412,8 +414,8 @@ class SettingsViewController: AutolayoutViewController, SettingsDelegate { @objc func reloadSettings() { pendingPreferences = Client.preferences.editable() - guard let currentOpenVPNConfiguration = pendingPreferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration ?? - Client.preferences.defaults.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration else { + guard let currentOpenVPNConfiguration = pendingPreferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration ?? + Client.preferences.defaults.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration else { fatalError("No default VPN custom configuration provided for PIA OpenVPN protocol") } @@ -439,7 +441,7 @@ class SettingsViewController: AutolayoutViewController, SettingsDelegate { log.debug("OpenVPN endpoints: \(pendingOpenVPNConfiguration.endpointProtocols ?? [])") if pendingPreferences.vpnType == PIATunnelProfile.vpnType { - var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: pendingOpenVPNConfiguration.build()) + var builder = OpenVPNProvider.ConfigurationBuilder(sessionConfiguration: pendingOpenVPNConfiguration.build()) if pendingPreferences.vpnType == PIATunnelProfile.vpnType { if AppPreferences.shared.useSmallPackets { diff --git a/PIA VPN/ShowConnectionStatsViewController.swift b/PIA VPN/ShowConnectionStatsViewController.swift index cb697bc11..aa6ff8e56 100644 --- a/PIA VPN/ShowConnectionStatsViewController.swift +++ b/PIA VPN/ShowConnectionStatsViewController.swift @@ -21,6 +21,7 @@ import Foundation import PIALibrary +import UIKit class ShowConnectionStatsViewController: AutolayoutViewController { diff --git a/PIA VPN/String+VPNType.swift b/PIA VPN/String+VPNType.swift index f496752b1..e2de89442 100644 --- a/PIA VPN/String+VPNType.swift +++ b/PIA VPN/String+VPNType.swift @@ -21,7 +21,8 @@ import Foundation import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN import PIAWireguard public extension String { @@ -46,7 +47,7 @@ public extension String { case PIATunnelProfile.vpnType: if AppPreferences.shared.piaSocketType != nil { let preferences = Client.preferences.editable() - if let currentOpenVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration { + if let currentOpenVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration { let port = currentOpenVPNConfiguration.sessionConfiguration.builder().endpointProtocols?.first?.port ?? 0 return "\(port)" } @@ -93,7 +94,7 @@ public extension String { return "ChaCha20" case PIATunnelProfile.vpnType: let preferences = Client.preferences.editable() - if let currentOpenVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration { + if let currentOpenVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration { return currentOpenVPNConfiguration.sessionConfiguration.builder().cipher?.rawValue ?? "" } return "---" @@ -111,7 +112,7 @@ public extension String { return "Poly1305" case PIATunnelProfile.vpnType: let preferences = Client.preferences.editable() - if let currentOpenVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration { + if let currentOpenVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration { return currentOpenVPNConfiguration.sessionConfiguration.builder().digest?.rawValue ?? "" } return "---" diff --git a/PIA VPN/SwiftGen+ScenesStoryboards.swift b/PIA VPN/SwiftGen+ScenesStoryboards.swift index 65b7d0076..5ce12dc8b 100644 --- a/PIA VPN/SwiftGen+ScenesStoryboards.swift +++ b/PIA VPN/SwiftGen+ScenesStoryboards.swift @@ -18,7 +18,7 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: Main.self) - internal static let sideMenuNavigationController = SceneType(storyboard: Main.self, identifier: "SideMenuNavigationController") + internal static let sideMenuNavigationController = SceneType(storyboard: Main.self, identifier: "SideMenuNavigationController") internal static let vpnPermissionViewController = SceneType(storyboard: Main.self, identifier: "VPNPermissionViewController") } diff --git a/PIA VPN/Theme+App.swift b/PIA VPN/Theme+App.swift index e2898e32f..304c42bc2 100644 --- a/PIA VPN/Theme+App.swift +++ b/PIA VPN/Theme+App.swift @@ -23,7 +23,6 @@ import Foundation import PIALibrary import SideMenu -import FXPageControl import UIKit extension Theme { diff --git a/PIA VPN/Theme+DarkPalette.swift b/PIA VPN/Theme+DarkPalette.swift index 54c38a506..47b8fcb80 100644 --- a/PIA VPN/Theme+DarkPalette.swift +++ b/PIA VPN/Theme+DarkPalette.swift @@ -22,6 +22,7 @@ import Foundation import PIALibrary +import UIKit extension Theme.Palette { static var dark: Theme.Palette { diff --git a/PIA VPN/ThemeStrategy+App.swift b/PIA VPN/ThemeStrategy+App.swift index d61a62006..244189e05 100644 --- a/PIA VPN/ThemeStrategy+App.swift +++ b/PIA VPN/ThemeStrategy+App.swift @@ -22,6 +22,7 @@ import Foundation import PIALibrary +import UIKit extension ThemeCode { func apply(theme: Theme, reload: Bool) { diff --git a/PIA VPN/Tiles/ConnectionTile.swift b/PIA VPN/Tiles/ConnectionTile.swift index c3fc8a05a..227360069 100644 --- a/PIA VPN/Tiles/ConnectionTile.swift +++ b/PIA VPN/Tiles/ConnectionTile.swift @@ -21,7 +21,8 @@ import UIKit import PIALibrary -import TunnelKit +import TunnelKitCore +import TunnelKitOpenVPN import WidgetKit class ConnectionTile: UIView, Tileable { diff --git a/PIA VPN/Tiles/FavoriteServersTile.swift b/PIA VPN/Tiles/FavoriteServersTile.swift index a0e57ab10..dd94a7ab4 100644 --- a/PIA VPN/Tiles/FavoriteServersTile.swift +++ b/PIA VPN/Tiles/FavoriteServersTile.swift @@ -23,6 +23,7 @@ import Foundation import PIALibrary +import UIKit class FavoriteServersTile: UIView, Tileable { diff --git a/PIA VPN/Tiles/MessagesTile.swift b/PIA VPN/Tiles/MessagesTile.swift index b1e5a1ec8..230b54b01 100644 --- a/PIA VPN/Tiles/MessagesTile.swift +++ b/PIA VPN/Tiles/MessagesTile.swift @@ -21,6 +21,7 @@ import Foundation import PIALibrary +import UIKit class MessagesTile: UIView, Tileable { diff --git a/PIA VPN/Tiles/UsageTile.swift b/PIA VPN/Tiles/UsageTile.swift index 8efe0d41c..4067f1cfb 100644 --- a/PIA VPN/Tiles/UsageTile.swift +++ b/PIA VPN/Tiles/UsageTile.swift @@ -23,6 +23,7 @@ import Foundation import PIALibrary +import UIKit class UsageTile: UIView, Tileable { diff --git a/PIAWidget/Data/Cache/WidgetPersistenceDatasource.swift b/PIAWidget/Data/Cache/WidgetPersistenceDatasource.swift new file mode 100644 index 000000000..5e1bdd1ec --- /dev/null +++ b/PIAWidget/Data/Cache/WidgetPersistenceDatasource.swift @@ -0,0 +1,17 @@ +// +// WidgetPersistenceDatasource.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-28. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation + +internal protocol WidgetPersistenceDatasource { + func getIsVPNConnected() -> Bool + func getIsTrustedNetwork() -> Bool + func getVpnProtocol() -> String + func getVpnPort() -> String + func getVpnSocket() -> String +} diff --git a/PIAWidget/WidgetUtils.swift b/PIAWidget/Data/Cache/WidgetUserDefaultsDatasource.swift similarity index 85% rename from PIAWidget/WidgetUtils.swift rename to PIAWidget/Data/Cache/WidgetUserDefaultsDatasource.swift index 90af3576e..0c25e32c0 100644 --- a/PIAWidget/WidgetUtils.swift +++ b/PIAWidget/Data/Cache/WidgetUserDefaultsDatasource.swift @@ -1,5 +1,5 @@ // -// WidgetUtils.swift +// WidgetUserDefaultsDatasource.swift // PIA VPN // // Created by Jose Blaya on 25/09/2020. @@ -21,53 +21,51 @@ import Foundation -public class WidgetUtils { - - private static let appGroup = "group.com.privateinternetaccess" +internal class WidgetUserDefaultsDatasource: WidgetPersistenceDatasource { - static var isVPNConnected: Bool { + private let appGroup = "group.com.privateinternetaccess" + + // MARK: WidgetPersistenceDatasource + + func getIsVPNConnected() -> Bool { var connected = false if let sharedDefaults = UserDefaults(suiteName: appGroup), let status = sharedDefaults.string(forKey: "vpn.status") { - if status == "connected" { connected = true } - } return connected } - - static var isTrustedNetwork: Bool { + + func getIsTrustedNetwork() -> Bool { if let sharedDefaults = UserDefaults(suiteName: appGroup) { return sharedDefaults.bool(forKey: "vpn.widget.trusted.network") } return false } - - static var vpnProtocol: String { + + func getVpnProtocol() -> String { if let sharedDefaults = UserDefaults(suiteName: appGroup), let value = sharedDefaults.string(forKey: "vpn.widget.protocol") { return value } return "--" } - - static var vpnPort: String { + + func getVpnPort() -> String { if let sharedDefaults = UserDefaults(suiteName: appGroup), let value = sharedDefaults.string(forKey: "vpn.widget.port") { return value } return "--" } - - static var vpnSocket: String { + + func getVpnSocket() -> String { if let sharedDefaults = UserDefaults(suiteName: appGroup), let value = sharedDefaults.string(forKey: "vpn.widget.socket") { return value } return "--" } - - } diff --git a/PIAWidget/WidgetContent.swift b/PIAWidget/Data/Model/WidgetInformation.swift similarity index 92% rename from PIAWidget/WidgetContent.swift rename to PIAWidget/Data/Model/WidgetInformation.swift index e15c8f53f..da8c874f3 100644 --- a/PIAWidget/WidgetContent.swift +++ b/PIAWidget/Data/Model/WidgetInformation.swift @@ -1,5 +1,5 @@ // -// WidgetContent.swift +// WidgetInformation.swift // PIA VPN // // Created by Jose Blaya on 24/09/2020. @@ -22,13 +22,10 @@ import Foundation import WidgetKit -struct WidgetContent: Codable, TimelineEntry { - - var date = Date() - +struct WidgetInformation: Codable, TimelineEntry { + var date: Date = Date() let connected: Bool let vpnProtocol: String let vpnPort: String let vpnSocket: String - } diff --git a/PIAWidget/Domain/UI/PIACircleVpnButton.swift b/PIAWidget/Domain/UI/PIACircleVpnButton.swift new file mode 100644 index 000000000..75a08e9d9 --- /dev/null +++ b/PIAWidget/Domain/UI/PIACircleVpnButton.swift @@ -0,0 +1,44 @@ +// +// PIACircleVpnButton.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-28. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import SwiftUI + +internal struct PIACircleVpnButton: View { + + internal let color: Color + + private let buttonSize: CGFloat = 40.0 + private let strokeWidth: CGFloat = 8.0 + + @State private var outerCircle: CGFloat + @State private var innerCircle: CGFloat + + init(color: Color) { + self.color = color + self.outerCircle = buttonSize + self.innerCircle = buttonSize - strokeWidth + } + + var body: some View { + return Circle() + .strokeBorder(Color("BorderColor"), lineWidth: strokeWidth * 0.75) + .background(Circle() + .strokeBorder(color, lineWidth: strokeWidth) + .background(Image("vpn-button") + .resizable() + .renderingMode(.template) + .foregroundColor(color) + .aspectRatio(contentMode: .fit) + .frame(width: buttonSize, height: buttonSize) + .padding(0.0) + ) + ) + .padding(buttonSize / 2.0) + } +} diff --git a/PIAWidget/Domain/UI/PIAIconView.swift b/PIAWidget/Domain/UI/PIAIconView.swift new file mode 100644 index 000000000..22c18fabe --- /dev/null +++ b/PIAWidget/Domain/UI/PIAIconView.swift @@ -0,0 +1,31 @@ +// +// PIAIconView.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-28. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import SwiftUI + +internal struct PIAIconView: View { + + private let iconRotation: CGFloat = -35.0 + private let iconSize: CGFloat + private let padding: CGFloat + + init(iconSize: CGFloat, padding: CGFloat) { + self.iconSize = iconSize + self.padding = padding + } + + var body: some View { + return Image("robot") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: iconSize, height: iconSize, alignment: .center) + .rotationEffect(Angle(degrees: iconRotation)) + .padding(padding) + } +} diff --git a/PIAWidget/Domain/UI/PIAWidgetView.swift b/PIAWidget/Domain/UI/PIAWidgetView.swift new file mode 100644 index 000000000..d1f5b4f04 --- /dev/null +++ b/PIAWidget/Domain/UI/PIAWidgetView.swift @@ -0,0 +1,52 @@ +// +// PIAWidgetView.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-28. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import SwiftUI + +struct PIAWidgetView : View { + + @Environment(\.widgetFamily) var widgetFamily + + let entry: PIAWidgetProvider.Entry + let widgetPersistenceDatasource: WidgetPersistenceDatasource + + init( + entry: PIAWidgetProvider.Entry, + widgetPersistenceDatasource: WidgetPersistenceDatasource + ) { + self.entry = entry + self.widgetPersistenceDatasource = widgetPersistenceDatasource + } + + var body: some View { + ZStack(alignment: .bottomTrailing) { + let targetIconSize = widgetFamily == .systemMedium ? 100.0 : 50.0 + let targetPadding = widgetFamily == .systemMedium ? -25.0 : -9.0 + PIAIconView(iconSize: targetIconSize, padding: targetPadding) + HStack { + if widgetPersistenceDatasource.getIsTrustedNetwork() { + PIACircleVpnButton(color: Color("TrustedNetworkColor")) + } else { + let targetColor = widgetPersistenceDatasource.getIsVPNConnected() ? "AccentColor" : "RedColor" + PIACircleVpnButton(color: Color(targetColor)) + } + + if widgetFamily == .systemMedium { + PIAWidgetVpnDetailsView( + vpnProtocol: widgetPersistenceDatasource.getVpnProtocol(), + port: widgetPersistenceDatasource.getVpnPort(), + socket: widgetPersistenceDatasource.getVpnSocket() + ) + } + } + + }.widgetURL(URL(string: widgetFamily == .systemMedium ? "piavpn:view" : "piavpn:connect")) + .background(Color("WidgetBackground")) + } +} diff --git a/PIAWidget/Domain/UI/PIAWidgetVpnDetailsView.swift b/PIAWidget/Domain/UI/PIAWidgetVpnDetailsView.swift new file mode 100644 index 000000000..08404b645 --- /dev/null +++ b/PIAWidget/Domain/UI/PIAWidgetVpnDetailsView.swift @@ -0,0 +1,33 @@ +// +// PIAWidgetVpnDetailsView.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-28. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import SwiftUI + +internal struct PIAWidgetVpnDetailsView: View { + + private let viewTrailingPadding: CGFloat = 40.0 + + internal let vpnProtocol: String + internal let port: String + internal let socket: String + + init(vpnProtocol: String, port: String, socket: String) { + self.vpnProtocol = vpnProtocol + self.port = port + self.socket = socket + } + + var body: some View { + return VStack { + PIAWidgetVpnDetaislRow(iconName: "icon-protocol", text: vpnProtocol) + PIAWidgetVpnDetaislRow(iconName: "icon-port", text: port) + PIAWidgetVpnDetaislRow(iconName: "icon-socket", text: socket) + }.padding(.trailing, viewTrailingPadding) + } +} diff --git a/PIAWidget/Domain/UI/PIAWidgetVpnDetaislRow.swift b/PIAWidget/Domain/UI/PIAWidgetVpnDetaislRow.swift new file mode 100644 index 000000000..4026469e0 --- /dev/null +++ b/PIAWidget/Domain/UI/PIAWidgetVpnDetaislRow.swift @@ -0,0 +1,38 @@ +// +// PIAWidgetVpnDetaislRow.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-29. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import SwiftUI + +internal struct PIAWidgetVpnDetaislRow: View { + + private let fontSize: CGFloat = 14.0 + private let iconsSize: CGFloat = 25.0 + private let rowSpacing: CGFloat = 0.0 + + internal let iconName: String + internal let text: String + + init(iconName: String, text: String) { + self.iconName = iconName + self.text = text + } + + var body: some View { + HStack(alignment: .center, spacing: rowSpacing) { + Image(iconName) + .resizable() + .frame(width: iconsSize, height: iconsSize, alignment: .leading) + Spacer() + Text(text) + .font(.system(size: fontSize)) + .foregroundColor(Color("FontColor")) + .frame(maxWidth: .infinity, alignment: .leading) + } + } +} diff --git a/PIAWidget/Domain/Widget/PIAWidget.swift b/PIAWidget/Domain/Widget/PIAWidget.swift new file mode 100644 index 000000000..35268132b --- /dev/null +++ b/PIAWidget/Domain/Widget/PIAWidget.swift @@ -0,0 +1,48 @@ +// +// PIAWidget.swift +// PIA VPN +// +// Created by Jose Blaya on 24/09/2020. +// Copyright © 2020 Private Internet Access, Inc. +// +// This file is part of the Private Internet Access iOS Client. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// + +import WidgetKit +import SwiftUI +import Intents + +@main +struct PIAWidget: Widget { + + let kind: String = "PIAWidget" + let displayName: String = "PIA VPN" + + var body: some WidgetConfiguration { + let widgetPersistenceDatasource = WidgetUserDefaultsDatasource() + return StaticConfiguration( + kind: kind, + provider: PIAWidgetProvider( + widgetPersistenceDatasource: widgetPersistenceDatasource + ) + ) { entry in + PIAWidgetView( + entry: entry, + widgetPersistenceDatasource: widgetPersistenceDatasource + ) + } + .configurationDisplayName(displayName) + .supportedFamilies([.systemSmall, .systemMedium]) + } +} diff --git a/PIAWidget/Domain/Widget/PIAWidgetPreview.swift b/PIAWidget/Domain/Widget/PIAWidgetPreview.swift new file mode 100644 index 000000000..3bfd659c6 --- /dev/null +++ b/PIAWidget/Domain/Widget/PIAWidgetPreview.swift @@ -0,0 +1,38 @@ +// +// PIAWidgetPreview.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-28. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import SwiftUI +import WidgetKit + +struct PIAWidgetPreview: PreviewProvider { + + static var previews: some View { + let widgetPersistenceDatasource = WidgetUserDefaultsDatasource() + PIAWidgetView( + entry: WidgetInformation( + date: Date(), + connected: true, + vpnProtocol: "IKEv2", + vpnPort: "500", + vpnSocket: "UDP" + ), + widgetPersistenceDatasource: widgetPersistenceDatasource + ).previewContext(WidgetPreviewContext(family: .systemSmall)) + PIAWidgetView( + entry: WidgetInformation( + date: Date(), + connected: false, + vpnProtocol: "WireGuard", + vpnPort: "1443", + vpnSocket: "UDP" + ), + widgetPersistenceDatasource: widgetPersistenceDatasource + ).previewContext(WidgetPreviewContext(family: .systemMedium)) + } +} diff --git a/PIAWidget/Domain/Widget/PIAWidgetProvider.swift b/PIAWidget/Domain/Widget/PIAWidgetProvider.swift new file mode 100644 index 000000000..269b10bd2 --- /dev/null +++ b/PIAWidget/Domain/Widget/PIAWidgetProvider.swift @@ -0,0 +1,73 @@ +// +// PIAWidgetProvider.swift +// PIAWidgetExtension +// +// Created by Juan Docal on 2022-09-28. +// Copyright © 2022 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import WidgetKit + +struct PIAWidgetProvider: TimelineProvider { + + let widgetPersistenceDatasource: WidgetPersistenceDatasource + + init(widgetPersistenceDatasource: WidgetPersistenceDatasource) { + self.widgetPersistenceDatasource = widgetPersistenceDatasource + } + + func placeholder(in context: Context) -> WidgetInformation { + WidgetInformation( + date: Date(), + connected: false, + vpnProtocol: "IPSec (IKEv2)", + vpnPort: "500", + vpnSocket: "UDP" + ) + } + + func getSnapshot( + in context: Context, + completion: @escaping (WidgetInformation) -> () + ) { + let entry: WidgetInformation + entry = WidgetInformation( + date: Date(), + connected: widgetPersistenceDatasource.getIsVPNConnected(), + vpnProtocol: widgetPersistenceDatasource.getVpnProtocol(), + vpnPort: widgetPersistenceDatasource.getVpnPort(), + vpnSocket: widgetPersistenceDatasource.getVpnSocket() + ) + completion(entry) + } + + func getTimeline( + in context: Context, + completion: @escaping (Timeline) -> () + ) { + var entries: [WidgetInformation] = [] + + // Generate a timeline consisting of five entries an hour apart, + // starting from the current date. + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date( + byAdding: .hour, + value: hourOffset, + to: currentDate + )! + let entry = WidgetInformation( + date: entryDate, + connected: widgetPersistenceDatasource.getIsVPNConnected(), + vpnProtocol: widgetPersistenceDatasource.getVpnProtocol(), + vpnPort: widgetPersistenceDatasource.getVpnPort(), + vpnSocket: widgetPersistenceDatasource.getVpnSocket() + ) + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } +} diff --git a/PIAWidget/PIAWidget.swift b/PIAWidget/PIAWidget.swift deleted file mode 100644 index 9338a85f2..000000000 --- a/PIAWidget/PIAWidget.swift +++ /dev/null @@ -1,166 +0,0 @@ -// -// PIAWidget.swift -// PIA VPN -// -// Created by Jose Blaya on 24/09/2020. -// Copyright © 2020 Private Internet Access, Inc. -// -// This file is part of the Private Internet Access iOS Client. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// - -import WidgetKit -import SwiftUI -import Intents - -struct Provider: TimelineProvider { - - func placeholder(in context: Context) -> WidgetContent { - WidgetContent(date: Date(), - connected: false, - vpnProtocol: "IPSec (IKEv2)", - vpnPort: "500", - vpnSocket: "UDP") - } - - func getSnapshot(in context: Context, completion: @escaping (WidgetContent) -> ()) { - let entry: WidgetContent - entry = WidgetContent(date: Date(), - connected: WidgetUtils.isVPNConnected, - vpnProtocol: WidgetUtils.vpnProtocol, - vpnPort: WidgetUtils.vpnPort, - vpnSocket: WidgetUtils.vpnSocket) - completion(entry) - } - - func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { - var entries: [WidgetContent] = [] - - // Generate a timeline consisting of five entries an hour apart, starting from the current date. - let currentDate = Date() - for hourOffset in 0 ..< 5 { - let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! - let entry = WidgetContent(date: entryDate, - connected: WidgetUtils.isVPNConnected, - vpnProtocol: WidgetUtils.vpnProtocol, - vpnPort: WidgetUtils.vpnPort, - vpnSocket: WidgetUtils.vpnSocket) - entries.append(entry) - } - - let timeline = Timeline(entries: entries, policy: .atEnd) - completion(timeline) - } -} - -struct PIAWidgetEntryView : View { - - @Environment(\.widgetFamily) var widgetFamily - - var entry: Provider.Entry - - var body: some View { - - ZStack(alignment: .bottomTrailing) { - - Image("robot") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: widgetFamily == .systemMedium ? 100 : 50, height: widgetFamily == .systemMedium ? 100 : 50, alignment: .center) - .rotationEffect(Angle(degrees: -35.0)) - .padding(widgetFamily == .systemMedium ? -25 : -9) - - HStack { - - if WidgetUtils.isTrustedNetwork { - - Circle() - .strokeBorder(Color("BorderColor"),lineWidth: 6) - .background(Circle() - .strokeBorder(Color("TrustedNetworkColor"),lineWidth: 8) - .background(Image("vpn-button") - .resizable() - .renderingMode(.template).foregroundColor(Color("TrustedNetworkColor")) - .aspectRatio(contentMode: .fit) - .frame(width: 40, height: 40, alignment: .center) - .padding(0))) - .padding(20) - - } else { - Circle() - .strokeBorder(Color("BorderColor"),lineWidth: 6) - .background(Circle() - .strokeBorder(Color(WidgetUtils.isVPNConnected ? "AccentColor" : "RedColor"),lineWidth: 8) - .background(Image("vpn-button") - .resizable() - .renderingMode(.template).foregroundColor(Color(WidgetUtils.isVPNConnected ? "AccentColor" : "RedColor")) - .aspectRatio(contentMode: .fit) - .frame(width: 40, height: 40, alignment: .center) - .padding(0))) - .padding(20) - - } - - if widgetFamily == .systemMedium { - VStack { - HStack(alignment: .center, spacing: 0) { - Image("icon-protocol").resizable().frame(width: 25, height: 25, alignment: .leading) - Spacer() - Text(WidgetUtils.vpnProtocol).font(.system(size: 14)).foregroundColor(Color("FontColor")).frame(maxWidth: .infinity, alignment: .leading) - } - HStack(alignment: .center, spacing: 0) { - Image("icon-port").resizable().frame(width: 25, height: 25, alignment: .leading) - Spacer() - Text(WidgetUtils.vpnPort).font(.system(size: 14)).foregroundColor(Color("FontColor")).frame(maxWidth: .infinity, alignment: .leading) - } - HStack(alignment: .center, spacing: 0) { - Image("icon-socket").resizable().frame(width: 25, height: 25, alignment: .leading) - Spacer() - Text(WidgetUtils.vpnSocket).font(.system(size: 14)).foregroundColor(Color("FontColor")).frame(maxWidth: .infinity, alignment: .leading) - } - }.padding(.trailing, 40) - - } - - } - - }.widgetURL(URL(string: widgetFamily == .systemMedium ? "piavpn:view" : "piavpn:connect")) - .background(Color("WidgetBackground")) - - } - -} - -@main -struct PIAWidget: Widget { - let kind: String = "PIAWidget" - - var body: some WidgetConfiguration { - StaticConfiguration(kind: kind, provider: Provider()) { entry in - PIAWidgetEntryView(entry: entry) - } - .configurationDisplayName("PIA VPN") - .description("") - .supportedFamilies([.systemSmall, .systemMedium]) - - } -} - -struct PIAWidget_Previews: PreviewProvider { - static var previews: some View { - PIAWidgetEntryView(entry: WidgetContent(date: Date(), connected: true, vpnProtocol: "IKEv2", vpnPort: "500", vpnSocket: "UDP")) - .previewContext(WidgetPreviewContext(family: .systemSmall)) - PIAWidgetEntryView(entry: WidgetContent(date: Date(), connected: false, vpnProtocol: "WireGuard", vpnPort: "1443", vpnSocket: "UDP")) - .previewContext(WidgetPreviewContext(family: .systemMedium)) - } -} diff --git a/Podfile b/Podfile deleted file mode 100644 index 0dae834a8..000000000 --- a/Podfile +++ /dev/null @@ -1,159 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '12.0' -use_frameworks! - -install! 'cocoapods', - :deterministic_uuids => false, - :disable_input_output_paths => true #fix dSym issue https://github.com/CocoaPods/CocoaPods/issues/9185 - - -# ignore all warnings from all pods -inhibit_all_warnings! - -# Libraries - -$git_root = "https://github.com/pia-foss" -$gitlab_vpn_root = "git@gitlab.kape.com:pia-mobile/ios" -$gitlab_kn_root = "git@gitlab.kape.com:pia-mobile/shared" - -$library_pod = 'PIALibrary' -$library_repo = 'client-library-apple' -$library_gitlab_repo = 'client-library-apple.git' -$library_subspecs = [ - 'Library', - 'UI', - 'Mock', - 'VPN' -] - -$regions_repo = 'mobile-common-regions' -$accounts_repo = 'mobile-common-account' -$csi_repo = 'mobile-common-csi' - -$regions_gitlab_repo = 'regions.git' -$accounts_gitlab_repo = 'account.git' -$csi_gitlab_repo = 'csi.git' -$kpi_gitlab_repo = 'kpi.git' - -def library_by_path(root) - $library_subspecs.each { |name| - pod "#{$library_pod}/#{name}", :path => "#{root}/#{$library_repo}" - } -end - -def library_by_git(sha) - $library_subspecs.each { |name| - pod "#{$library_pod}/#{name}", :git => "#{$git_root}/#{$library_repo}", :commit => sha - } -end - -def library_by_gitlab_branch(branch) - $library_subspecs.each { |name| - pod "#{$library_pod}/#{name}", :git => "#{$gitlab_vpn_root}/#{$library_gitlab_repo}", :branch => branch - } -end - -def library_by_gitlab_by_git(sha) - $library_subspecs.each { |name| - pod "#{$library_pod}/#{name}", :git => "#{$gitlab_vpn_root}/#{$library_gitlab_repo}", :commit => sha - } -end - -def library_by_version(version) - $library_subspecs.each { |name| - pod "#{$library_pod}/#{name}", version - } -end - -# Pod groups - -def shared_main_pods - pod 'AlamofireImage' - - #pod "PIAAccountModule", :git => "#{$git_root}/#{$accounts_repo}" - pod "PIAAccountModule", :git => "#{$gitlab_kn_root}/#{$accounts_gitlab_repo}", :commit => '697fd4f' - #pod "PIARegionsModule", :git => "#{$git_root}/#{$regions_repo}" - pod "PIARegionsModule", :git => "#{$gitlab_kn_root}/#{$regions_gitlab_repo}", :branch => 'release/1.3.2' - #pod "PIACSIModule", :git => "#{$git_root}/#{$csi_repo}" - pod "PIACSIModule", :git => "#{$gitlab_kn_root}/#{$csi_gitlab_repo}", :branch => 'release/1.1.1' - pod "PIAKPIModule", :git => "#{$gitlab_kn_root}/#{$kpi_gitlab_repo}", :branch => 'release/1.1.0' - - #library_by_path('~/Repositories') - #library_by_git('') - #library_by_gitlab_branch('') - library_by_gitlab_by_git('cffdca05') - #library_by_version('~> 1.1.3') -end - -def app_pods - shared_main_pods - pod 'TPKeyboardAvoiding' - pod 'SideMenu', '6.1.3' - pod 'DZNEmptyDataSet' - pod 'PopupDialog' - pod 'ReachabilitySwift', '~> 4.3.0' - pod 'GradientProgressBar', '~> 2.0' - pod 'Popover' -end - -def tunnel_pods - pod 'TunnelKit', :git => 'https://github.com/pia-foss/tunnelkit', :branch => 'master' - pod 'OpenSSL-Apple', :git => 'https://github.com/keeshux/openssl-apple' -end - -def piawireguard_pod - pod 'PIAWireguard', :git => "#{$git_root}/pia-wireguard" -end - -def piawireguard_gitlab_pod - pod 'PIAWireguard', :git => "#{$gitlab_vpn_root}/pia-wireguard.git", :commit => '7e9d8d48' -end - -# Targets - -target 'PIA VPN' do - app_pods -end - -target 'PIA VPN dev' do - app_pods - #only use the following pods for internal (non-public) builds - pod 'Firebase/Core', '6.5.0' - pod 'Crashlytics' - pod 'Fabric' -end - -target 'PIA VPN Tunnel' do - tunnel_pods -end - -target 'PIA VPN WG Tunnel' do - #piawireguard_pod - piawireguard_gitlab_pod -end - -target 'PIA VPNTests' do - app_pods - pod 'Firebase/Core', '6.5.0' - pod 'Crashlytics' - pod 'Fabric' -end - -target 'PIA VPN UITests' do - app_pods -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - if ['PopupDialog'].include? target.name - target.build_configurations.each do |config| - config.build_settings['SWIFT_VERSION'] = '4.2' - end - end - if ['SwiftEntryKit', 'QuickLayout'].include? target.name - target.build_configurations.each do |config| - config.build_settings['SWIFT_VERSION'] = '4.0' - end - end - end -end diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index 1a9d5014a..000000000 --- a/Podfile.lock +++ /dev/null @@ -1,298 +0,0 @@ -PODS: - - Alamofire (4.9.1) - - AlamofireImage (3.6.0): - - Alamofire (~> 4.9) - - Crashlytics (3.14.0): - - Fabric (~> 1.10.2) - - DynamicBlurView (4.1.0) - - DZNEmptyDataSet (1.8.1) - - Fabric (1.10.2) - - Firebase/Core (6.5.0): - - Firebase/CoreOnly - - FirebaseAnalytics (= 6.0.4) - - Firebase/CoreOnly (6.5.0): - - FirebaseCore (= 6.1.0) - - FirebaseAnalytics (6.0.4): - - FirebaseCore (~> 6.1) - - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.0.4) - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - FirebaseCore (6.1.0): - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/Logger (~> 6.0) - - FirebaseInstanceID (4.2.7): - - FirebaseCore (~> 6.0) - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/UserDefaults (~> 6.0) - - FXPageControl (1.5) - - Gloss (2.1.1) - - GoogleAppMeasurement (6.0.4): - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - GoogleUtilities/AppDelegateSwizzler (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.7.2): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.7.2): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.7.2): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.7.2)" - - GoogleUtilities/Reachability (6.7.2): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.7.2): - - GoogleUtilities/Logger - - GradientProgressBar (2.0.3): - - LightweightObservable (~> 2.0) - - LightweightObservable (2.1.2) - - lottie-ios (3.2.3) - - nanopb (0.3.9011): - - nanopb/decode (= 0.3.9011) - - nanopb/encode (= 0.3.9011) - - nanopb/decode (0.3.9011) - - nanopb/encode (0.3.9011) - - OpenSSL-Apple (1.1.1j.11) - - PIAAccountModule (1.2.1): - - PIAAccountModule/account (= 1.2.1) - - PIAAccountModule/gradle (= 1.2.1) - - PIAAccountModule/account (1.2.1) - - PIAAccountModule/gradle (1.2.1) - - PIACSIModule (1.1.1): - - PIACSIModule/csi (= 1.1.1) - - PIACSIModule/gradle (= 1.1.1) - - PIACSIModule/csi (1.1.1) - - PIACSIModule/gradle (1.1.1) - - PIAKPIModule (1.1.0): - - PIAKPIModule/gradle (= 1.1.0) - - PIAKPIModule/kpi (= 1.1.0) - - PIAKPIModule/gradle (1.1.0) - - PIAKPIModule/kpi (1.1.0) - - PIALibrary/Core (2.18.0): - - PIAAccountModule - - PIALibrary/Library (2.18.0): - - Alamofire (~> 4) - - Gloss (~> 2) - - PIAAccountModule - - PIACSIModule - - PIAKPIModule - - PIALibrary/Core - - PIALibrary/Util - - PIARegionsModule - - PopupDialog - - ReachabilitySwift - - SwiftyBeaver - - PIALibrary/Mock (2.18.0): - - PIALibrary/Library - - PIALibrary/UI (2.18.0): - - FXPageControl - - lottie-ios - - PIALibrary/Library - - SwiftEntryKit (= 0.7.2) - - SwiftyBeaver - - TPKeyboardAvoiding - - PIALibrary/Util (2.18.0): - - PIALibrary/Core - - PIALibrary/VPN (2.18.0): - - PIALibrary/Library - - PIAWireguard - - TunnelKit - - PIARegionsModule (1.3.2): - - PIARegionsModule/gradle (= 1.3.2) - - PIARegionsModule/regions (= 1.3.2) - - PIARegionsModule/gradle (1.3.2) - - PIARegionsModule/regions (1.3.2) - - PIAWireguard (1.2.0): - - PIAWireguard/AppExtension (= 1.2.0) - - PIAWireguard/Core (= 1.2.0) - - PIAWireguard/AppExtension (1.2.0): - - PIAWireguard/Core - - PIAWireguard/Core (1.2.0): - - Alamofire - - TweetNacl - - Popover (1.3.0) - - PopupDialog (1.1.1): - - DynamicBlurView (~> 4.0) - - PromisesObjC (1.2.12) - - QuickLayout (2.0.2) - - ReachabilitySwift (4.3.1) - - SideMenu (6.1.3) - - SwiftEntryKit (0.7.2): - - QuickLayout (= 2.0.2) - - SwiftyBeaver (1.9.5) - - TPKeyboardAvoiding (1.3.5) - - TunnelKit (3.3.0): - - TunnelKit/Protocols/OpenVPN (= 3.3.0) - - TunnelKit/AppExtension (3.3.0): - - SwiftyBeaver - - TunnelKit/Core - - TunnelKit/Core (3.3.0): - - SwiftyBeaver - - TunnelKit/Manager (3.3.0): - - SwiftyBeaver - - TunnelKit/Protocols/OpenVPN (3.3.0): - - OpenSSL-Apple (~> 1.1.1h.10) - - TunnelKit/AppExtension - - TunnelKit/Core - - TunnelKit/Manager - - TweetNacl (1.0.2) - -DEPENDENCIES: - - AlamofireImage - - Crashlytics - - DZNEmptyDataSet - - Fabric - - Firebase/Core (= 6.5.0) - - GradientProgressBar (~> 2.0) - - OpenSSL-Apple (from `https://github.com/keeshux/openssl-apple`) - - "PIAAccountModule (from `git@gitlab.kape.com:pia-mobile/shared/account.git`, commit `697fd4f`)" - - "PIACSIModule (from `git@gitlab.kape.com:pia-mobile/shared/csi.git`, branch `release/1.1.1`)" - - "PIAKPIModule (from `git@gitlab.kape.com:pia-mobile/shared/kpi.git`, branch `release/1.1.0`)" - - "PIALibrary/Library (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" - - "PIALibrary/Mock (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" - - "PIALibrary/UI (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" - - "PIALibrary/VPN (from `git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git`, commit `cffdca05`)" - - "PIARegionsModule (from `git@gitlab.kape.com:pia-mobile/shared/regions.git`, branch `release/1.3.2`)" - - "PIAWireguard (from `git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git`, commit `7e9d8d48`)" - - Popover - - PopupDialog - - ReachabilitySwift (~> 4.3.0) - - SideMenu (= 6.1.3) - - TPKeyboardAvoiding - - TunnelKit (from `https://github.com/pia-foss/tunnelkit`, branch `master`) - -SPEC REPOS: - https://github.com/CocoaPods/Specs.git: - - Alamofire - - AlamofireImage - - Crashlytics - - DynamicBlurView - - DZNEmptyDataSet - - Fabric - - Firebase - - FirebaseAnalytics - - FirebaseCore - - FirebaseInstanceID - - FXPageControl - - Gloss - - GoogleAppMeasurement - - GoogleUtilities - - GradientProgressBar - - LightweightObservable - - lottie-ios - - nanopb - - Popover - - PopupDialog - - PromisesObjC - - QuickLayout - - ReachabilitySwift - - SideMenu - - SwiftEntryKit - - SwiftyBeaver - - TPKeyboardAvoiding - - TweetNacl - -EXTERNAL SOURCES: - OpenSSL-Apple: - :git: https://github.com/keeshux/openssl-apple - PIAAccountModule: - :commit: 697fd4f - :git: "git@gitlab.kape.com:pia-mobile/shared/account.git" - PIACSIModule: - :branch: release/1.1.1 - :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" - PIAKPIModule: - :branch: release/1.1.0 - :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" - PIALibrary: - :commit: cffdca05 - :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" - PIARegionsModule: - :branch: release/1.3.2 - :git: "git@gitlab.kape.com:pia-mobile/shared/regions.git" - PIAWireguard: - :commit: 7e9d8d48 - :git: "git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git" - TunnelKit: - :branch: master - :git: https://github.com/pia-foss/tunnelkit - -CHECKOUT OPTIONS: - OpenSSL-Apple: - :commit: ada87845f854a21fea436c76deb97be2acc58c8a - :git: https://github.com/keeshux/openssl-apple - PIAAccountModule: - :commit: 697fd4f - :git: "git@gitlab.kape.com:pia-mobile/shared/account.git" - PIACSIModule: - :commit: 5e93ae8ff84bc04ac4e1cfbb6d3dd0aa7dc92a01 - :git: "git@gitlab.kape.com:pia-mobile/shared/csi.git" - PIAKPIModule: - :commit: e444c78b61e280a1ce444d8d8a3a4c0eeb50b981 - :git: "git@gitlab.kape.com:pia-mobile/shared/kpi.git" - PIALibrary: - :commit: cffdca05 - :git: "git@gitlab.kape.com:pia-mobile/ios/client-library-apple.git" - PIARegionsModule: - :commit: 355418cf4b3d35512c345e51026fbabf81fe6488 - :git: "git@gitlab.kape.com:pia-mobile/shared/regions.git" - PIAWireguard: - :commit: 7e9d8d48 - :git: "git@gitlab.kape.com:pia-mobile/ios/pia-wireguard.git" - TunnelKit: - :commit: 2556fbe9feb53adc88e97e5bdc09779da31468f5 - :git: https://github.com/pia-foss/tunnelkit - -SPEC CHECKSUMS: - Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 - AlamofireImage: be9963c6582d68b39e89191f64c82a7d7bf40fdd - Crashlytics: 9220f5bc89e7a618df411b4f639389dbfb0e03d2 - DynamicBlurView: 58e18fae80bb614e34681a4486870e7d257b62e8 - DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7 - Fabric: ea977e3cd9c20425516d3dafd3bf8c941c51223f - Firebase: dedc9e48ea3f3649ad5f6b982f8a0c73508a14b5 - FirebaseAnalytics: 3fb375bc9d13779add4039716f868d233a473fad - FirebaseCore: aecf02fb2274ec361b9bebeac112f5daa18273bd - FirebaseInstanceID: ebd2ea79ee38db0cb5f5167b17a0d387e1cc7b6e - FXPageControl: 97620412515365d10a3282ec0660f49f6401a8f0 - Gloss: 9df15229b2f6484c14752a738474fd2528b4c5cd - GoogleAppMeasurement: 183bd916af7f80deb67c01888368f1108d641832 - GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 - GradientProgressBar: f11fb57cffc615a4794da558ce283171a992130a - LightweightObservable: c5ac85423a5edbed9a920b4d5c7b8f94ab3688c4 - lottie-ios: c058aeafa76daa4cf64d773554bccc8385d0150e - nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd - OpenSSL-Apple: bb7c9715a259404de040f5359ed3b3170cedf8d6 - PIAAccountModule: 31264ad54dfa98cd8be6810885f910b8bedc9592 - PIACSIModule: 2db8a7fa393d5bef757e856fff3327b8c37dac5d - PIAKPIModule: 37c56c0153d693db4d4adb43fd12b9c96ec7ad6d - PIALibrary: ed53091a5bc74f77354481729eff87f6c07fd00d - PIARegionsModule: f54391cbb218b2780612158c54bd8deeda6941d9 - PIAWireguard: e6fc3a37758af8d83704dd61e327c2ff6da88b13 - Popover: 10e1d9528f81d9504df984b7b3f491292bc1822d - PopupDialog: 720c92befd8bc23c13442254945213db5612f149 - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 - QuickLayout: a730730b646b231fd4ef7cffaeb1e81fe0e1ca92 - ReachabilitySwift: 4032e2f59586e11e3b0ebe15b167abdd587a388b - SideMenu: 46c1b79dab51fc2f1c1ff3e1212ca47cc231b0bf - SwiftEntryKit: 83d312243af7397e38a222b17b7a744b9a7d2145 - SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 - TPKeyboardAvoiding: d55b3ea7b362540af8fcf36aa3ed2e87bf210cc6 - TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db - TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 - -PODFILE CHECKSUM: 5da459779bbfdfae20771eca5db526da51714419 - -COCOAPODS: 1.11.2 diff --git a/README.md b/README.md index a870fa1bc..feea50969 100644 --- a/README.md +++ b/README.md @@ -31,33 +31,11 @@ The PIA VPN app features: - Xcode 9+ (Swift 4) - Git (preinstalled with Xcode Command Line Tools) - Ruby (preinstalled with macOS) -- [CocoaPods 1.5.0][dep-cocoapods] - [SwiftGen][dep-swiftgen] - [Go][dep-golang] It's highly recommended to use the Git and Ruby packages provided by [Homebrew][dep-brew]. -### Testing - -Download the app codebase locally: - - $ git clone https://github.com/pia-foss/vpn-ios.git - -Assuming you have a [working CocoaPods environment][dep-cocoapods], setting up the app workspace only requires installing the pod dependencies: - - $ pod install - -After that, open `PIA VPN.xcworkspace` in Xcode and run the `PIA VPN` target. - -If the build does not complete due to missing modules (often `PIAAccount`), run `pod install` again with the partial build, then build again. - -For the VPN to work properly, the app requires: - -- _App Groups_ and _Keychain Sharing_ capabilities -- App IDs with _Packet Tunnel_ entitlements - -both in the main app and the tunnel extension target. - ### Hotspot Helper API We use a special entitlement to participate in the process of joining Wi-Fi/hotspot networks (https://developer.apple.com/documentation/networkextension/nehotspothelper) @@ -101,7 +79,6 @@ This project is licensed under the [MIT (Expat) license](https://choosealicense. [pia-url]: https://www.privateinternetaccess.com/ [pia-wiki]: https://en.wikipedia.org/wiki/Private_Internet_Access -[dep-cocoapods]: https://guides.cocoapods.org/using/getting-started.html [dep-swiftgen]: https://github.com/SwiftGen/SwiftGen [dep-jazzy]: https://github.com/realm/jazzy [dep-brew]: https://brew.sh/ diff --git a/Resources/GoogleService-Info.plist b/Resources/GoogleService-Info.plist deleted file mode 100644 index 0c67376eb..000000000 --- a/Resources/GoogleService-Info.plist +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/Resources/UI/en.lproj/Main.storyboard b/Resources/UI/en.lproj/Main.storyboard index ec71595b5..b6eec2637 100644 --- a/Resources/UI/en.lproj/Main.storyboard +++ b/Resources/UI/en.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -1425,7 +1425,7 @@ - + From 2851973c3fd079aa95e0564dce8285dabe0bf2ce Mon Sep 17 00:00:00 2001 From: kp-juan-docal <109512072+kp-juan-docal@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:44:33 +0100 Subject: [PATCH 085/159] remove messages logic (#1133) --- PIA VPN.xcodeproj/project.pbxproj | 2 +- PIA VPN/Bootstrapper.swift | 1 - PIA VPN/MessagesManager.swift | 12 ------------ 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 6224cf258..2e2d3958a 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -3338,7 +3338,7 @@ repositoryURL = "git@github.com:xvpn/cpz_pia-mobile_ios_client-library-apple.git"; requirement = { kind = revision; - revision = 289c5d959bd8e47906306bf75002d6c88266e09d; + revision = 3aefb632854b3838910d5948117bec06cac63f2f; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index 74a200c12..e1fee4a07 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -131,7 +131,6 @@ class Bootstrapper { AppPreferences.shared.disablesMultiDipTokens = Client.configuration.featureFlags.contains(Client.FeatureFlags.disableMultiDipTokens) AppPreferences.shared.showNewInitialScreen = Client.configuration.featureFlags.contains(Client.FeatureFlags.showNewInitialScreen) }) - MessagesManager.shared.refreshMessages() //FORCE THE MIGRATION TO GEN4 if Client.providers.vpnProvider.needsMigrationToGEN4() { diff --git a/PIA VPN/MessagesManager.swift b/PIA VPN/MessagesManager.swift index 45e7f6d5e..6b7b4634b 100644 --- a/PIA VPN/MessagesManager.swift +++ b/PIA VPN/MessagesManager.swift @@ -40,18 +40,6 @@ public class MessagesManager: NSObject { deinit { NotificationCenter.default.removeObserver(self) } - - func refreshMessages() { - - if AppPreferences.shared.showServiceMessages { - Client.providers.accountProvider.inAppMessages(forAppVersion: Macros.localizedVersionNumber()) { (message, error) in - if let message = message, !message.wasDismissed() { - self.apiMessage = message - Macros.postNotification(.PIAUpdateFixedTiles) - } - } - } - } func postSystemMessage(message: InAppMessage) { self.systemMessage = message From 06b3995d30e9227f5f61efc7c28c6dc898aad3db Mon Sep 17 00:00:00 2001 From: kp-juan-docal <109512072+kp-juan-docal@users.noreply.github.com> Date: Thu, 23 Feb 2023 16:01:41 +0100 Subject: [PATCH 086/159] bump version to 3.20.0 (#1134) --- PIA VPN.xcodeproj/project.pbxproj | 48 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 2e2d3958a..9f591270c 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2498,7 +2498,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN Tunnel/PIA VPN Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2509,7 +2509,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2532,7 +2532,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2543,7 +2543,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2564,7 +2564,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2584,7 +2584,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2608,7 +2608,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2628,7 +2628,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2720,7 +2720,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2732,7 +2732,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2757,7 +2757,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2769,7 +2769,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2900,7 +2900,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2918,7 +2918,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -2939,7 +2939,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2957,7 +2957,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3057,7 +3057,7 @@ CODE_SIGN_ENTITLEMENTS = PIAWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3069,7 +3069,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3099,7 +3099,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3111,7 +3111,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3135,7 +3135,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN WG Tunnel/PIA_VPN_WG_Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3147,7 +3147,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3174,7 +3174,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20030; + CURRENT_PROJECT_VERSION = 20031; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3186,7 +3186,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.19.0; + MARKETING_VERSION = 3.20.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 2c4732a2a37d7e6c3b355aedd5bac4fe40352c4c Mon Sep 17 00:00:00 2001 From: kp-juan-docal <109512072+kp-juan-docal@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:58:23 +0200 Subject: [PATCH 087/159] bump version to 3.21.0 (#1135) --- PIA VPN.xcodeproj/project.pbxproj | 48 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 9f591270c..77d642497 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2498,7 +2498,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN Tunnel/PIA VPN Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2509,7 +2509,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2532,7 +2532,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2543,7 +2543,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2564,7 +2564,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2584,7 +2584,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2608,7 +2608,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2628,7 +2628,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2720,7 +2720,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2732,7 +2732,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2757,7 +2757,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2769,7 +2769,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2900,7 +2900,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2918,7 +2918,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -2939,7 +2939,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2957,7 +2957,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3057,7 +3057,7 @@ CODE_SIGN_ENTITLEMENTS = PIAWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3069,7 +3069,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3099,7 +3099,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3111,7 +3111,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3135,7 +3135,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN WG Tunnel/PIA_VPN_WG_Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3147,7 +3147,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3174,7 +3174,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20031; + CURRENT_PROJECT_VERSION = 20032; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3186,7 +3186,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.20.0; + MARKETING_VERSION = 3.21.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 778cddbfc392e071da54e42683d26d18903d976f Mon Sep 17 00:00:00 2001 From: kp-juan-docal <109512072+kp-juan-docal@users.noreply.github.com> Date: Wed, 7 Jun 2023 09:04:45 +0200 Subject: [PATCH 088/159] update openssl to version 3.0.2 (#1136) --- PIA VPN.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 77d642497..7f9745800 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -3338,7 +3338,7 @@ repositoryURL = "git@github.com:xvpn/cpz_pia-mobile_ios_client-library-apple.git"; requirement = { kind = revision; - revision = 3aefb632854b3838910d5948117bec06cac63f2f; + revision = 197536330933dd1b393e6bf7d408d3f6fd185546; }; }; /* End XCRemoteSwiftPackageReference section */ From bf76672a3a5f069b022e4bef1b67fbc021296c09 Mon Sep 17 00:00:00 2001 From: kp-juan-docal <109512072+kp-juan-docal@users.noreply.github.com> Date: Wed, 7 Jun 2023 17:42:17 +0200 Subject: [PATCH 089/159] bump version to 3.22.0 (#1137) --- PIA VPN.xcodeproj/project.pbxproj | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 7f9745800..50e7938e1 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2498,7 +2498,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN Tunnel/PIA VPN Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2509,7 +2509,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2532,7 +2532,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2543,7 +2543,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2564,7 +2564,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2584,7 +2584,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2608,7 +2608,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2628,7 +2628,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2720,7 +2720,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2732,7 +2732,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2757,7 +2757,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2769,7 +2769,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2918,7 +2918,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -2957,7 +2957,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3057,7 +3057,7 @@ CODE_SIGN_ENTITLEMENTS = PIAWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3069,7 +3069,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3099,7 +3099,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3111,7 +3111,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3135,7 +3135,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN WG Tunnel/PIA_VPN_WG_Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3147,7 +3147,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3174,7 +3174,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20033; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3186,7 +3186,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.21.0; + MARKETING_VERSION = 3.22.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 131b99fa9387a16772248a19af14f078a87d6cc5 Mon Sep 17 00:00:00 2001 From: kp-juan-docal <109512072+kp-juan-docal@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:01:31 +0200 Subject: [PATCH 090/159] bump version to 3.23.0 (#1138) --- PIA VPN.xcodeproj/project.pbxproj | 48 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 50e7938e1..6ced221e2 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2498,7 +2498,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN Tunnel/PIA VPN Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2509,7 +2509,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2532,7 +2532,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2543,7 +2543,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2564,7 +2564,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2584,7 +2584,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2608,7 +2608,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2628,7 +2628,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2720,7 +2720,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2732,7 +2732,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2757,7 +2757,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2769,7 +2769,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2900,7 +2900,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20034; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2918,7 +2918,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -2939,7 +2939,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20032; + CURRENT_PROJECT_VERSION = 20034; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2957,7 +2957,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; @@ -3057,7 +3057,7 @@ CODE_SIGN_ENTITLEMENTS = PIAWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3069,7 +3069,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3099,7 +3099,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3111,7 +3111,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3135,7 +3135,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN WG Tunnel/PIA_VPN_WG_Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3147,7 +3147,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3174,7 +3174,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20033; + CURRENT_PROJECT_VERSION = 20034; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3186,7 +3186,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.22.0; + MARKETING_VERSION = 3.23.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 860388a96125cacf1a593420822cc0a6b7052f94 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Fri, 28 Jul 2023 12:27:28 +0200 Subject: [PATCH 091/159] CXAPP-3161: Show leak protection toggles on privacy settings --- PIA VPN/SettingOptions.swift | 8 ++- ...rivacyFeaturesSettingsViewController.swift | 52 ++++++++++++++----- PIA VPN/SwiftGen+Strings.swift | 10 ++++ PIA VPN/en.lproj/Localizable.strings | 4 ++ 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/PIA VPN/SettingOptions.swift b/PIA VPN/SettingOptions.swift index cbb745cee..58805a109 100644 --- a/PIA VPN/SettingOptions.swift +++ b/PIA VPN/SettingOptions.swift @@ -190,12 +190,16 @@ public enum NetworkSections: Int, SettingSection, EnumsBuilder { public enum PrivacyFeaturesSections: Int, SettingSection, EnumsBuilder { case killswitch = 0 + case leakProtection + case allowAccessOnLocalNetwork case safariContentBlocker case refresh public func localizedTitleMessage() -> String { switch self { case .killswitch: return L10n.Settings.ApplicationSettings.KillSwitch.title + case .leakProtection: return L10n.Settings.ApplicationSettings.LeakProtection.title + case .allowAccessOnLocalNetwork: return L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.title case .safariContentBlocker: return L10n.Settings.ContentBlocker.title case .refresh: return L10n.Settings.ContentBlocker.Refresh.title } @@ -204,13 +208,15 @@ public enum PrivacyFeaturesSections: Int, SettingSection, EnumsBuilder { public func localizedSubtitleMessage() -> String { switch self { case .killswitch: return "" + case .leakProtection: return "" + case .allowAccessOnLocalNetwork: return "" case .safariContentBlocker: return "" case .refresh: return "" } } public static func all() -> [Self] { - return [.killswitch, .safariContentBlocker, .refresh] + return [.killswitch, .leakProtection, .allowAccessOnLocalNetwork, .safariContentBlocker, .refresh] } } diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index a4d465dff..ff0f09680 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -31,6 +31,8 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { @IBOutlet weak var tableView: UITableView! private lazy var switchPersistent = UISwitch() private lazy var switchContentBlocker = UISwitch() + private lazy var switchLeakProtection = UISwitch() + private lazy var switchAllowDevicesOnLocalNetwork = UISwitch() private var isContentBlockerEnabled = false override func viewDidLoad() { @@ -147,19 +149,31 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + guard let privacySettingsSection = PrivacyFeaturesSections(rawValue: section), + let cell = tableView.dequeueReusableCell(withIdentifier: Cells.footer) else { + return nil + } - if section != PrivacyFeaturesSections.refresh.rawValue, let cell = tableView.dequeueReusableCell(withIdentifier: Cells.footer) { - cell.textLabel?.numberOfLines = 0 - cell.textLabel?.style(style: TextStyle.textStyle21) - cell.backgroundColor = .clear - if section == PrivacyFeaturesSections.killswitch.rawValue { - cell.textLabel?.text = L10n.Settings.ApplicationSettings.KillSwitch.footer - } else { - cell.textLabel?.text = L10n.Settings.ContentBlocker.footer - } + cell.textLabel?.numberOfLines = 0 + cell.textLabel?.style(style: TextStyle.textStyle21) + cell.backgroundColor = .clear + + switch privacySettingsSection { + case .killswitch: + cell.textLabel?.text = L10n.Settings.ApplicationSettings.KillSwitch.footer + return cell + case .leakProtection: + cell.textLabel?.text = L10n.Settings.ApplicationSettings.LeakProtection.footer return cell + case .allowAccessOnLocalNetwork: + cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.footer + return cell + case .safariContentBlocker: + cell.textLabel?.text = L10n.Settings.ContentBlocker.footer + return cell + case .refresh: + return nil } - return nil } @@ -179,8 +193,22 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie cell.accessoryView = switchPersistent cell.selectionStyle = .none switchPersistent.isOn = pendingPreferences.isPersistentConnection - - + case .leakProtection: + cell.textLabel?.text = L10n.Settings.ApplicationSettings.LeakProtection.title + cell.detailTextLabel?.text = nil + cell.accessoryView = switchLeakProtection + cell.selectionStyle = .none + // TODO: Persist the state of this toggle + // To be done on ticket: CXAPP-3162 + switchLeakProtection.isOn = true + case .allowAccessOnLocalNetwork: + cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.title + cell.detailTextLabel?.text = nil + cell.accessoryView = switchAllowDevicesOnLocalNetwork + cell.selectionStyle = .none + // TODO: Persist the state of this toggle + // To be done on ticket: CXAPP-3162 + switchAllowDevicesOnLocalNetwork.isOn = false case .safariContentBlocker: cell.textLabel?.text = L10n.Settings.ContentBlocker.title cell.detailTextLabel?.text = nil diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index 6932bb76f..edee0394a 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -806,6 +806,16 @@ internal enum L10n { /// VPN Kill Switch internal static let title = L10n.tr("Localizable", "settings.application_settings.kill_switch.title") } + internal enum LeakProtection { + internal static let footer = L10n.tr("Localizable", "settings.application_settings.leak_protection.footer") + /// VPN Leak Protection + internal static let title = L10n.tr("Localizable", "settings.application_settings.leak_protection.title") + } + internal enum AllowAccessOnLocalNetwork { + internal static let footer = L10n.tr("Localizable", "settings.application_settings.allow_local_network.footer") + /// Allow access to devices on local network + internal static let title = L10n.tr("Localizable", "settings.application_settings.allow_local_network.title") + } internal enum Mace { /// PIA MACE™ blocks ads, trackers, and malware while you're connected to the VPN. internal static let footer = L10n.tr("Localizable", "settings.application_settings.mace.footer") diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index 248e79a07..e0aeb9dfd 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -134,6 +134,10 @@ "settings.application_settings.active_theme.title" = "Active theme"; "settings.application_settings.kill_switch.title" = "VPN Kill Switch"; "settings.application_settings.kill_switch.footer" = "The VPN kill switch prevents access to the Internet if the VPN connection is reconnecting. This excludes disconnecting manually."; +"settings.application_settings.leak_protection.title" = "Leak Protection"; +"settings.application_settings.leak_protection.footer" = "When enabled, all traffic will be routed over the VPN including iOS System services including AirDrop, CarPlay, Airplay and Personal Hotspots. These systems may not function as expected while network protection is enabled. Learn More."; +"settings.application_settings.allow_local_network.title" = "Allow access to devices on local network"; +"settings.application_settings.allow_local_network.footer" = "Stay connected to local devices like printers or file servers while connected to the VPN. (Allow this only if you trust the people and devices on your network.)"; "settings.application_settings.mace.title" = "PIA MACE™"; "settings.application_settings.mace.footer" = "PIA MACE™ blocks ads, trackers, and malware while you're connected to the VPN."; From 9a8a6ebfb01ec3c323adfc02d4afb9b89757ed6d Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Fri, 28 Jul 2023 19:29:37 +0200 Subject: [PATCH 092/159] CXAPP-3160: Add feature flag feetching for leak protection --- PIA VPN/AppPreferences.swift | 20 ++++++++++++++++++++ PIA VPN/Bootstrapper.swift | 2 ++ 2 files changed, 22 insertions(+) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index f79d6a55c..faa821d9d 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -102,6 +102,8 @@ class AppPreferences { static let disablesMultiDipTokens = "disablesMultiDipTokens" static let checksDipExpirationRequest = "checksDipExpirationRequest" static let showNewInitialScreen = "showNewInitialScreen" + static let showLeakProtection = "showLeakProtection" + static let showLeakProtectionNotifications = "showLeakProtectionNotifications" // Survey static let userInteractedWithSurvey = "userInteractedWithSurvey" @@ -518,6 +520,24 @@ class AppPreferences { } } + var showLeakProtection: Bool { + get { + return defaults.bool(forKey: Entries.showLeakProtection) + } + set { + defaults.set(newValue, forKey: Entries.showLeakProtection) + } + } + + var showLeakProtectionNotifications: Bool { + get { + return defaults.bool(forKey: Entries.showLeakProtectionNotifications) + } + set { + defaults.set(newValue, forKey: Entries.showLeakProtectionNotifications) + } + } + var checksDipExpirationRequest: Bool { get { return defaults.bool(forKey: Entries.checksDipExpirationRequest) diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index e1fee4a07..eb3968d08 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -130,6 +130,8 @@ class Bootstrapper { AppPreferences.shared.checksDipExpirationRequest = Client.configuration.featureFlags.contains(Client.FeatureFlags.checkDipExpirationRequest) AppPreferences.shared.disablesMultiDipTokens = Client.configuration.featureFlags.contains(Client.FeatureFlags.disableMultiDipTokens) AppPreferences.shared.showNewInitialScreen = Client.configuration.featureFlags.contains(Client.FeatureFlags.showNewInitialScreen) + AppPreferences.shared.showLeakProtection = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtection) + AppPreferences.shared.showLeakProtectionNotifications = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtectionNotifications) }) //FORCE THE MIGRATION TO GEN4 From 33147ee6dca8430a5175b31dd1912d28b9690f46 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 31 Jul 2023 13:54:21 +0200 Subject: [PATCH 093/159] CXAPP-3231: Add leak protections UI behind feature flag --- ...rivacyFeaturesSettingsViewController.swift | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index ff0f09680..8e1f91697 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -34,11 +34,23 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { private lazy var switchLeakProtection = UISwitch() private lazy var switchAllowDevicesOnLocalNetwork = UISwitch() private var isContentBlockerEnabled = false + + private var preferences: AppPreferences? + private var sections = [PrivacyFeaturesSections]() + + private var leakProtection = true override func viewDidLoad() { super.viewDidLoad() + preferences = AppPreferences.shared + if let preferences = preferences, preferences.showLeakProtection { + sections = PrivacyFeaturesSections.all() + } else { + sections = PrivacyFeaturesSections.all().filter { $0 != .leakProtection && $0 != .allowAccessOnLocalNetwork } + } + tableView.sectionFooterHeight = UITableView.automaticDimension tableView.estimatedSectionFooterHeight = 1.0 @@ -47,6 +59,8 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { switchPersistent.addTarget(self, action: #selector(togglePersistentConnection(_:)), for: .valueChanged) switchContentBlocker.addTarget(self, action: #selector(showContentBlockerTutorial), for: .touchUpInside) + switchLeakProtection.addTarget(self, action: #selector(toggleLeakProtection(_:)), for: .valueChanged) + switchAllowDevicesOnLocalNetwork.addTarget(self, action: #selector(toggleAllowDevicesOnLocalNetwork(_:)), for: .valueChanged) NotificationCenter.default.addObserver(self, selector: #selector(reloadSettings), name: .PIASettingsHaveChanged, object: nil) NotificationCenter.default.addObserver(self, @@ -118,7 +132,15 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { @objc private func showContentBlockerTutorial() { perform(segue: StoryboardSegue.Main.contentBlockerSegueIdentifier) } - + + @objc private func toggleLeakProtection(_ sender: UISwitch) { + leakProtection = sender.isOn + tableView.reloadData() + } + + @objc private func toggleAllowDevicesOnLocalNetwork(_ sender: UISwitch) { + print(sender.isOn) + } // MARK: Restylable @@ -141,7 +163,7 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { - return PrivacyFeaturesSections.all().count + return sections.count } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { @@ -149,11 +171,13 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - guard let privacySettingsSection = PrivacyFeaturesSections(rawValue: section), + guard sections.count > section, let cell = tableView.dequeueReusableCell(withIdentifier: Cells.footer) else { return nil } + let privacySettingsSection = sections[section] + cell.textLabel?.numberOfLines = 0 cell.textLabel?.style(style: TextStyle.textStyle21) cell.backgroundColor = .clear @@ -184,7 +208,7 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie cell.selectionStyle = .default cell.detailTextLabel?.text = nil - let section = PrivacyFeaturesSections.all()[indexPath.section] + let section = sections[indexPath.section] switch section { case .killswitch: @@ -200,7 +224,7 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie cell.selectionStyle = .none // TODO: Persist the state of this toggle // To be done on ticket: CXAPP-3162 - switchLeakProtection.isOn = true + switchLeakProtection.isOn = leakProtection case .allowAccessOnLocalNetwork: cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.title cell.detailTextLabel?.text = nil @@ -209,6 +233,7 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie // TODO: Persist the state of this toggle // To be done on ticket: CXAPP-3162 switchAllowDevicesOnLocalNetwork.isOn = false + switchAllowDevicesOnLocalNetwork.isEnabled = leakProtection case .safariContentBlocker: cell.textLabel?.text = L10n.Settings.ContentBlocker.title cell.detailTextLabel?.text = nil @@ -242,7 +267,7 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let section = PrivacyFeaturesSections.all()[indexPath.section] + let section = sections[indexPath.section] switch section { case .refresh: From a68290bed7be60a246bc0ad351edb313329e8cd2 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Tue, 8 Aug 2023 22:24:04 +0200 Subject: [PATCH 094/159] CXAPP-3162: Add logic to show and hide leak protection toggles --- .../PrivacyFeaturesSettingsViewController.swift | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index 8e1f91697..88cbb7286 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -37,8 +37,6 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { private var preferences: AppPreferences? private var sections = [PrivacyFeaturesSections]() - - private var leakProtection = true override func viewDidLoad() { @@ -134,12 +132,12 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { } @objc private func toggleLeakProtection(_ sender: UISwitch) { - leakProtection = sender.isOn + Client.preferences.leakProtection = sender.isOn tableView.reloadData() } @objc private func toggleAllowDevicesOnLocalNetwork(_ sender: UISwitch) { - print(sender.isOn) + Client.preferences.allowLocalDeviceAccess = sender.isOn } // MARK: Restylable @@ -222,18 +220,14 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie cell.detailTextLabel?.text = nil cell.accessoryView = switchLeakProtection cell.selectionStyle = .none - // TODO: Persist the state of this toggle - // To be done on ticket: CXAPP-3162 - switchLeakProtection.isOn = leakProtection + switchLeakProtection.isOn = Client.preferences.leakProtection case .allowAccessOnLocalNetwork: cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.title cell.detailTextLabel?.text = nil cell.accessoryView = switchAllowDevicesOnLocalNetwork cell.selectionStyle = .none - // TODO: Persist the state of this toggle - // To be done on ticket: CXAPP-3162 - switchAllowDevicesOnLocalNetwork.isOn = false - switchAllowDevicesOnLocalNetwork.isEnabled = leakProtection + switchAllowDevicesOnLocalNetwork.isEnabled = Client.preferences.leakProtection + switchAllowDevicesOnLocalNetwork.isOn = !Client.preferences.leakProtection ? false : Client.preferences.allowLocalDeviceAccess case .safariContentBlocker: cell.textLabel?.text = L10n.Settings.ContentBlocker.title cell.detailTextLabel?.text = nil From 9c386148a2b7ed7fe7060be4b6e1d0bb63ba8f35 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Tue, 8 Aug 2023 22:24:04 +0200 Subject: [PATCH 095/159] CXAPP-3162: Add logic to show and hide leak protection toggles --- PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index 88cbb7286..b3c9f9aef 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -41,7 +41,6 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { override func viewDidLoad() { super.viewDidLoad() - preferences = AppPreferences.shared if let preferences = preferences, preferences.showLeakProtection { sections = PrivacyFeaturesSections.all() From 4ea98c3d26e678e926c3dac9956d58a0b132114f Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Thu, 10 Aug 2023 14:42:50 +0200 Subject: [PATCH 096/159] CXAPP-3165: Add alert for leak protection changes when VPN is connected --- PIA VPN.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 6ced221e2..3d06c93a1 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -3337,8 +3337,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "git@github.com:xvpn/cpz_pia-mobile_ios_client-library-apple.git"; requirement = { - kind = revision; - revision = 197536330933dd1b393e6bf7d408d3f6fd185546; + branch = master; + kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ From b5e6ad13dcef7052e0690e0fa728738260e46f39 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Thu, 10 Aug 2023 14:42:50 +0200 Subject: [PATCH 097/159] CXAPP-3165: Update PIA iOS Library to the latest version --- .../PrivacyFeaturesSettingsViewController.swift | 15 +++++++++++++++ PIA VPN/SwiftGen+Strings.swift | 4 ++++ PIA VPN/en.lproj/Localizable.strings | 1 + 3 files changed, 20 insertions(+) diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index b3c9f9aef..c018cfc2a 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -37,11 +37,14 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { private var preferences: AppPreferences? private var sections = [PrivacyFeaturesSections]() + private var vpnProvider: VPNProvider? override func viewDidLoad() { super.viewDidLoad() preferences = AppPreferences.shared + vpnProvider = Client.providers.vpnProvider + if let preferences = preferences, preferences.showLeakProtection { sections = PrivacyFeaturesSections.all() } else { @@ -133,10 +136,22 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { @objc private func toggleLeakProtection(_ sender: UISwitch) { Client.preferences.leakProtection = sender.isOn tableView.reloadData() + presentUpdateSettingsAlertWhenConnected() } @objc private func toggleAllowDevicesOnLocalNetwork(_ sender: UISwitch) { Client.preferences.allowLocalDeviceAccess = sender.isOn + presentUpdateSettingsAlertWhenConnected() + } + + private func presentUpdateSettingsAlertWhenConnected() { + guard let vpnProvider = vpnProvider, vpnProvider.vpnStatus == .connected else { + return + } + + let sheet = Macros.alertController(L10n.Settings.ApplicationSettings.LeakProtectionAlert.title, nil) + sheet.addAction(UIAlertAction(title: L10n.Global.ok, style: .default, handler: nil)) + present(sheet, animated: true) } // MARK: Restylable diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index edee0394a..154cdd70d 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -822,6 +822,10 @@ internal enum L10n { /// PIA MACE™ internal static let title = L10n.tr("Localizable", "settings.application_settings.mace.title") } + internal enum LeakProtectionAlert { + /// VPN Leak Protection update settings alert + internal static let title = L10n.tr("Localizable", "settings.application_settings.leak_protection.alert.title") + } } internal enum Cards { internal enum History { diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index e0aeb9dfd..2f097bfa2 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -140,6 +140,7 @@ "settings.application_settings.allow_local_network.footer" = "Stay connected to local devices like printers or file servers while connected to the VPN. (Allow this only if you trust the people and devices on your network.)"; "settings.application_settings.mace.title" = "PIA MACE™"; "settings.application_settings.mace.footer" = "PIA MACE™ blocks ads, trackers, and malware while you're connected to the VPN."; +"settings.application_settings.leak_protection.alert.title" = "Changes to the VPN Settings will take effect on the next connection"; "settings.content_blocker.title" = "Safari Content Blocker state"; "settings.content_blocker.state.title" = "Current state"; From b3b2015aed92e51c3c3abf2bd46fbc7dbe9db4a8 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 14 Aug 2023 15:22:47 +0200 Subject: [PATCH 098/159] PIA-68: Add Network monitor to identify non-compliant Wifi --- PIA VPN.xcodeproj/project.pbxproj | 34 +++++++++++++++ PIA VPN/IPv4Address.swift | 60 +++++++++++++++++++++++++++ PIA VPN/NetworkMonitor.swift | 14 +++++++ PIA VPN/WifiNetworkMonitor.swift | 69 +++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 PIA VPN/IPv4Address.swift create mode 100644 PIA VPN/NetworkMonitor.swift create mode 100644 PIA VPN/WifiNetworkMonitor.swift diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 3d06c93a1..2ad84798d 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -413,6 +413,12 @@ DDFCFA9521E892130081F235 /* RegionTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFCFA9321E892130081F235 /* RegionTile.swift */; }; DDFCFA9721E8921F0081F235 /* RegionTile.xib in Resources */ = {isa = PBXBuildFile; fileRef = DDFCFA9621E8921F0081F235 /* RegionTile.xib */; }; DDFCFA9821E8921F0081F235 /* RegionTile.xib in Resources */ = {isa = PBXBuildFile; fileRef = DDFCFA9621E8921F0081F235 /* RegionTile.xib */; }; + E5F52A182A8A5D5300828883 /* WifiNetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F52A172A8A5D5300828883 /* WifiNetworkMonitor.swift */; }; + E5F52A192A8A5D5300828883 /* WifiNetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F52A172A8A5D5300828883 /* WifiNetworkMonitor.swift */; }; + E5F52A1B2A8A5E1E00828883 /* IPv4Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F52A1A2A8A5E1E00828883 /* IPv4Address.swift */; }; + E5F52A1C2A8A5E1E00828883 /* IPv4Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F52A1A2A8A5E1E00828883 /* IPv4Address.swift */; }; + E5F52A1F2A8A614900828883 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F52A1E2A8A614900828883 /* NetworkMonitor.swift */; }; + E5F52A202A8A614900828883 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5F52A1E2A8A614900828883 /* NetworkMonitor.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -802,6 +808,9 @@ DDFCFA8E21E892070081F235 /* RegionTileCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RegionTileCollectionViewCell.xib; sourceTree = ""; }; DDFCFA9321E892130081F235 /* RegionTile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionTile.swift; sourceTree = ""; }; DDFCFA9621E8921F0081F235 /* RegionTile.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RegionTile.xib; sourceTree = ""; }; + E5F52A172A8A5D5300828883 /* WifiNetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiNetworkMonitor.swift; sourceTree = ""; }; + E5F52A1A2A8A5E1E00828883 /* IPv4Address.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPv4Address.swift; sourceTree = ""; }; + E5F52A1E2A8A614900828883 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1013,6 +1022,7 @@ 0E76AE201D35447A00421248 /* Utils */ = { isa = PBXGroup; children = ( + E5F52A1D2A8A613400828883 /* Network */, 0EB0A849204F0CE2008BCF1D /* DataCounter.h */, 0EB0A84A204F0CE2008BCF1D /* DataCounter.m */, 0EB0A84B204F0CE2008BCF1D /* DataCounter.swift */, @@ -1510,6 +1520,24 @@ path = "PIA VPN WG Tunnel"; sourceTree = ""; }; + E5F52A162A8A5D3800828883 /* Wifi */ = { + isa = PBXGroup; + children = ( + E5F52A172A8A5D5300828883 /* WifiNetworkMonitor.swift */, + ); + name = Wifi; + sourceTree = ""; + }; + E5F52A1D2A8A613400828883 /* Network */ = { + isa = PBXGroup; + children = ( + E5F52A162A8A5D3800828883 /* Wifi */, + E5F52A1A2A8A5E1E00828883 /* IPv4Address.swift */, + E5F52A1E2A8A614900828883 /* NetworkMonitor.swift */, + ); + name = Network; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -2047,6 +2075,7 @@ 8272C6342657EE4E00D846A8 /* AutomationSettingsViewController.swift in Sources */, 3524670F26B4332E00E3F0AC /* DashboardViewController+ServerSelection.swift in Sources */, 0E9452A01FDB547D00891948 /* ExpirationCell.swift in Sources */, + E5F52A202A8A614900828883 /* NetworkMonitor.swift in Sources */, 0EFDC1F01FE4B9E6007C0B9B /* AppConfiguration.swift in Sources */, DD76292321ECCD5C0092DF50 /* UsageTile.swift in Sources */, DDD65314247E66AD00F0A897 /* CoordinatesFinder.swift in Sources */, @@ -2067,6 +2096,7 @@ 0E1F318720176A6300FC1000 /* Theme+DarkPalette.swift in Sources */, 8272C6312657D42700D846A8 /* PrivacyFeaturesSettingsViewController.swift in Sources */, 0ECC1E3F1FDB3F2F0039891D /* RegionsViewController.swift in Sources */, + E5F52A192A8A5D5300828883 /* WifiNetworkMonitor.swift in Sources */, 8272C62B26551F9B00D846A8 /* SettingPopoverSelectionView.swift in Sources */, 0EB9ED1B1FDA1C4D00D1214D /* SettingsViewController.swift in Sources */, 0EB966751FDF0D6E0086ABC2 /* ServerProvider+UI.swift in Sources */, @@ -2138,6 +2168,7 @@ 829EB51225359DBB003E74DD /* DedicatedIpRowViewCell.swift in Sources */, DDFCFA9521E892130081F235 /* RegionTile.swift in Sources */, 0EFDC1E71FE4ABAA007C0B9B /* Notification+App.swift in Sources */, + E5F52A1C2A8A5E1E00828883 /* IPv4Address.swift in Sources */, 827570E224DAC2C6008F9800 /* CustomNetworkCollectionViewCell.swift in Sources */, DD918577246BFA7F006B3A2B /* RatingManager.swift in Sources */, 82C0071425231F2800F21AF2 /* String+VPNType.swift in Sources */, @@ -2258,6 +2289,7 @@ DD9329A2237AFD0A0025B6BC /* ShowQuickSettingsViewController.swift in Sources */, 0E2215C920084CD700F5FB4D /* SwiftGen+Strings.swift in Sources */, 827570D824DAB6E4008F9800 /* NetworkFooterCollectionViewCell.swift in Sources */, + E5F52A1F2A8A614900828883 /* NetworkMonitor.swift in Sources */, DD3B504324B7576F0002F4B5 /* Card.swift in Sources */, 82CAB8F2255C0CD100BB08EF /* MessagesTileCollectionViewCell.swift in Sources */, DD606ABC21C904BB00E0781D /* PIAHotspotHelper.swift in Sources */, @@ -2272,6 +2304,7 @@ 0EA660081FEC7A9500CB2B0D /* PIATunnelProvider+UI.swift in Sources */, 0E7361EB1FD99A1000706BFF /* MenuViewController.swift in Sources */, DD1C139921E65F90004004B3 /* IPTileCollectionViewCell.swift in Sources */, + E5F52A182A8A5D5300828883 /* WifiNetworkMonitor.swift in Sources */, 827570CF24DAA128008F9800 /* NetworkCollectionViewCell.swift in Sources */, 0EB966771FDF11B80086ABC2 /* Server+UI.swift in Sources */, DD052AC32419003300AD3A24 /* ProtocolTableViewCell.swift in Sources */, @@ -2315,6 +2348,7 @@ DD4E84572243BD1200929B39 /* DashboardCollectionViewUtil.swift in Sources */, 829EB507253598BD003E74DD /* DedicatedIpViewController.swift in Sources */, DD74695A217F07AC00B7BD73 /* DNSList.swift in Sources */, + E5F52A1B2A8A5E1E00828883 /* IPv4Address.swift in Sources */, 0E9452AE1FDB5F7A00891948 /* PIAPageControl.swift in Sources */, DDD271F021D6718F00B6D20F /* RegionFilter.swift in Sources */, 82CAB87A255AEA3500BB08EF /* MessagesManager.swift in Sources */, diff --git a/PIA VPN/IPv4Address.swift b/PIA VPN/IPv4Address.swift new file mode 100644 index 000000000..f39a032f3 --- /dev/null +++ b/PIA VPN/IPv4Address.swift @@ -0,0 +1,60 @@ +// +// IPv4Address.swift +// PIA VPN +// +// Created by Said Rehouni on 11/8/23. +// Copyright © 2023 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import Network + +extension IPv4Address { + + /// https://datatracker.ietf.org/doc/html/rfc1918 + public var isRFC1918Compliant: Bool { + inRange("10.0.0.0"..."10.255.255.255") || inRange("172.16.0.0"..."172.31.255.255") + || inRange("192.168.0.0"..."192.168.255.255") + } + + /// Checks if IPAddress is in range of other address + /// - Parameter range: A range of IPAddress + /// - Returns: True if this address is within range + public func inRange(_ range: ClosedRange) -> Bool { + range.contains(self) + } +} + +extension IPv4Address: Comparable { + /// Comparison is done by converting Ipaddress to Integer + public static func < (lhs: IPv4Address, rhs: IPv4Address) -> Bool { + let lhsIntValue = representAsInteger(ipAddress: lhs) + let rhsIntValue = representAsInteger(ipAddress: rhs) + return lhsIntValue < rhsIntValue + } + + /// Converts IPAddress as integer values. Loops over each address section, shifts them and accumulate the result. + /// - Parameter ipAddress: IPAddress for conversion + /// - Returns: Integer representation + static func representAsInteger(ipAddress: IPv4Address) -> Int { + var result: Int = 0 + let octets = ipAddress.octets + for i in stride(from: 3, through: 0, by: -1) { + result += octets[3 - i] << (i * 8) + } + return result + } + + // IPAddress as an Integer array + var octets: [Int] { + return self.rawValue.map { Int($0) } + } +} + +extension IPv4Address: ExpressibleByStringLiteral { + /// Initialize from a Static String + /// Intentionally crashes when value is a bad IPaddresses + public init(stringLiteral value: StringLiteralType) { + self.init(value)! + } +} diff --git a/PIA VPN/NetworkMonitor.swift b/PIA VPN/NetworkMonitor.swift new file mode 100644 index 000000000..ecd302b38 --- /dev/null +++ b/PIA VPN/NetworkMonitor.swift @@ -0,0 +1,14 @@ +// +// NetworkMonitor.swift +// PIA VPN +// +// Created by Said Rehouni on 14/8/23. +// Copyright © 2023 Private Internet Access Inc. All rights reserved. +// + +import Foundation + +protocol NetworkMonitor { + func checkForRFC1918Vulnerability() -> Bool + func isConnected() -> Bool +} diff --git a/PIA VPN/WifiNetworkMonitor.swift b/PIA VPN/WifiNetworkMonitor.swift new file mode 100644 index 000000000..8a96c9b64 --- /dev/null +++ b/PIA VPN/WifiNetworkMonitor.swift @@ -0,0 +1,69 @@ +// +// WifiNetworkMonitor.swift +// PIA VPN +// +// Created by Said Rehouni on 11/8/23. +// Copyright © 2023 Private Internet Access Inc. All rights reserved. +// + +import Foundation +import Network +import NetworkExtension + +class WifiNetworkMonitor: NetworkMonitor { + + private func getWifiAndEthernetIpAddress() -> [IPv4Address] { + var addresses = [IPv4Address]() + // Get list of all interfaces + var ifaddr: UnsafeMutablePointer? + defer { + freeifaddrs(ifaddr) + } + guard getifaddrs(&ifaddr) == 0, let firstAddr = ifaddr else { + return [] + } + + // Loop every interface, retrieve IPAddress and interface name (en0, en1, tun1) + for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) { + let flags = Int32(ptr.pointee.ifa_flags) + // Find all active ifaddrs and avoid loopback interface. + if (flags & (IFF_UP | IFF_RUNNING | IFF_LOOPBACK)) == (IFF_UP | IFF_RUNNING) { + let addr = ptr.pointee.ifa_addr.pointee + if addr.sa_family == UInt8(AF_INET) { + // Convert interface address to a human readable string: + var ipAddressStr = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + let nameInfo = getnameinfo( + ptr.pointee.ifa_addr, + socklen_t(addr.sa_len), + &ipAddressStr, + socklen_t(ipAddressStr.count), + nil, + socklen_t(0), + NI_NUMERICHOST + ) + if nameInfo == 0, + let ipAddress = IPv4Address(.init(cString: ipAddressStr)) + { + let interfaceName = String(cString: ptr.pointee.ifa_name) + if ipAddress.isLinkLocal == false && interfaceName.starts(with: "en") { + addresses.append(ipAddress) + } + } + } + } + } + return addresses + } + + func checkForRFC1918Vulnerability() -> Bool { + let wifiIPAddresses = getWifiAndEthernetIpAddress() + return wifiIPAddresses.contains(where: { $0.isRFC1918Compliant == false }) + } + + func isConnected() -> Bool { + if let currentNetworks = NEHotspotHelper.supportedNetworkInterfaces() { + return currentNetworks.contains(where: { $0 is NEHotspotNetwork }) + } + return false + } +} From 68750f2c46c2cf4f6fb65c06a40049504535174a Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 14 Aug 2023 16:28:35 +0200 Subject: [PATCH 099/159] PIA-68: Integrate network monitor on HospotHelper --- PIA VPN/PIAHotspotHelper.swift | 43 +++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/PIA VPN/PIAHotspotHelper.swift b/PIA VPN/PIAHotspotHelper.swift index b96a0c152..6b2818772 100644 --- a/PIA VPN/PIAHotspotHelper.swift +++ b/PIA VPN/PIAHotspotHelper.swift @@ -42,9 +42,11 @@ public protocol PIAHotspotHelperDelegate: class { class PIAHotspotHelper { private var delegate: PIAHotspotHelperDelegate? + private let networkMonitor: NetworkMonitor - init(withDelegate delegate: PIAHotspotHelperDelegate? = nil) { + init(withDelegate delegate: PIAHotspotHelperDelegate? = nil, networkMonitor: NetworkMonitor = WifiNetworkMonitor()) { self.delegate = delegate + self.networkMonitor = networkMonitor } /** @@ -56,6 +58,17 @@ class PIAHotspotHelper { return UIDevice.current.WiFiSSID } + public private(set) var currentRFC1918VulnerableWifi: String? { + get { + guard networkMonitor.isConnected() else { return nil } + + return UserDefaults.standard.string(forKey: "currentRFC1918VulnerableWifi") + } + set { + UserDefaults.standard.set(newValue, forKey: "currentRFC1918VulnerableWifi") + } + } + /** Configures the HotspotHelper API to perform actions depending of the command. @@ -93,6 +106,13 @@ class PIAHotspotHelper { } response.deliver() } else if cmd.commandType == .evaluate { + + if AppPreferences.shared.showLeakProtectionNotifications { + weakSelf.checkForRFC1918VulnerableWifi(cmd: cmd) + } else { + weakSelf.currentRFC1918VulnerableWifi = nil + } + if let currentNetwork = cmd.network { if !currentNetwork.isSecure { // Open WiFi log.info("Evaluate") @@ -123,6 +143,18 @@ class PIAHotspotHelper { } } + private func checkForRFC1918VulnerableWifi(cmd: NEHotspotHelperCommand) { + if networkMonitor.checkForRFC1918Vulnerability() { + log.info("HotspotHelper: APIHotspotDidDetectRFC1918VulnerableWifi detected") + currentRFC1918VulnerableWifi = cmd.network?.ssid.trimmingCharacters(in: CharacterSet.whitespaces) + NotificationCenter.default.post(name: .DeviceDidConnectToRFC1918VulnerableWifi, object: nil) + } else { + log.info("HotspotHelper: APIHotspotDidDetectRFC1918VulnerableWifi NOT detected") + currentRFC1918VulnerableWifi = nil + NotificationCenter.default.post(name: .DeviceDidConnectToRFC1918CompliantWifi, object: nil) + } + } + private func hotspotHelperMessage() -> String { if Client.preferences.nmtRulesEnabled, Client.preferences.useWiFiProtection { @@ -201,3 +233,12 @@ class PIAHotspotHelper { } } + +public extension Notification.Name { + + /// Posted when device detects RFC1918 vulnerable Wifi + static let DeviceDidConnectToRFC1918VulnerableWifi: Notification.Name = Notification.Name("DeviceDidConnectToRFC1918VulnerableWifi") + + /// Posted when device detects RFC1918 compliant Wifi + static let DeviceDidConnectToRFC1918CompliantWifi: Notification.Name = Notification.Name("DeviceDidConnectToRFC1918CompliantWifi") +} From c59f69407e91308e9836b42b00d18bfa4312b73a Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 14 Aug 2023 22:04:00 +0200 Subject: [PATCH 100/159] PIA-54: Migrate currentRFC1918VulnerableWifi to Client preferences --- PIA VPN/PIAHotspotHelper.swift | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/PIA VPN/PIAHotspotHelper.swift b/PIA VPN/PIAHotspotHelper.swift index 6b2818772..f8e5728a0 100644 --- a/PIA VPN/PIAHotspotHelper.swift +++ b/PIA VPN/PIAHotspotHelper.swift @@ -58,17 +58,6 @@ class PIAHotspotHelper { return UIDevice.current.WiFiSSID } - public private(set) var currentRFC1918VulnerableWifi: String? { - get { - guard networkMonitor.isConnected() else { return nil } - - return UserDefaults.standard.string(forKey: "currentRFC1918VulnerableWifi") - } - set { - UserDefaults.standard.set(newValue, forKey: "currentRFC1918VulnerableWifi") - } - } - /** Configures the HotspotHelper API to perform actions depending of the command. @@ -110,7 +99,7 @@ class PIAHotspotHelper { if AppPreferences.shared.showLeakProtectionNotifications { weakSelf.checkForRFC1918VulnerableWifi(cmd: cmd) } else { - weakSelf.currentRFC1918VulnerableWifi = nil + Client.preferences.currentRFC1918VulnerableWifi = nil } if let currentNetwork = cmd.network { @@ -146,11 +135,11 @@ class PIAHotspotHelper { private func checkForRFC1918VulnerableWifi(cmd: NEHotspotHelperCommand) { if networkMonitor.checkForRFC1918Vulnerability() { log.info("HotspotHelper: APIHotspotDidDetectRFC1918VulnerableWifi detected") - currentRFC1918VulnerableWifi = cmd.network?.ssid.trimmingCharacters(in: CharacterSet.whitespaces) + Client.preferences.currentRFC1918VulnerableWifi = cmd.network?.ssid.trimmingCharacters(in: CharacterSet.whitespaces) NotificationCenter.default.post(name: .DeviceDidConnectToRFC1918VulnerableWifi, object: nil) } else { log.info("HotspotHelper: APIHotspotDidDetectRFC1918VulnerableWifi NOT detected") - currentRFC1918VulnerableWifi = nil + Client.preferences.currentRFC1918VulnerableWifi = nil NotificationCenter.default.post(name: .DeviceDidConnectToRFC1918CompliantWifi, object: nil) } } From 2d179d4f8d65a6f085642ccd44952de52f1fccdd Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 14 Aug 2023 22:06:28 +0200 Subject: [PATCH 101/159] PIA-54: Add english localization for non compliant alert --- PIA VPN/SwiftGen+Strings.swift | 8 ++++++++ PIA VPN/en.lproj/Localizable.strings | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index 154cdd70d..d5e4ab363 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -208,6 +208,14 @@ internal enum L10n { /// This network is untrusted. Do you really want to disconnect the VPN? internal static let untrusted = L10n.tr("Localizable", "dashboard.vpn.disconnect.untrusted") } + internal enum LeakProtectionAlert { + /// This network is untrusted. Do you really want to disconnect the VPN? + internal static let title = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.title") + internal static let message = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.message") + internal static let cta1 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta1") + internal static let cta2 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta2") + internal static let cta3 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta3") + } } } diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index 2f097bfa2..e62cc7dfd 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -220,6 +220,12 @@ "dashboard.accessibility.vpn.button.isOn" = "VPN Connection button. The VPN is currently connected"; "dashboard.accessibility.vpn.button.isOff" = "VPN Connection button. The VPN is currently disconnected"; +"dashboard.vpn.leakprotection.alert.title" = "Unsecured Wi-Fi detected"; +"dashboard.vpn.leakprotection.alert.message" = "To prevent data leaks, tap Disable Now to turn off “Allow access to devices on local network\" and automatically reconnect."; +"dashboard.vpn.leakprotection.alert.cta1" = "Disable Now"; +"dashboard.vpn.leakprotection.alert.cta2" = "Learn more"; +"dashboard.vpn.leakprotection.alert.cta3" = "Ignore"; + // VPN PERMISSION "vpn_permission.title" = "PIA"; From 86c32c64a415555328371b65adc3a1f85c0c960a Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 14 Aug 2023 22:08:04 +0200 Subject: [PATCH 102/159] PIA-54: Add logic to show the non compliant WIFI alert --- PIA VPN/DashboardViewController.swift | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index c7601fae4..7f8c9c8db 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -25,6 +25,7 @@ import PIALibrary import SideMenu import SwiftyBeaver import WidgetKit +import NetworkExtension private let log = SwiftyBeaver.self @@ -117,6 +118,10 @@ class DashboardViewController: AutolayoutViewController { nc.addObserver(self, selector: #selector(checkInternetConnection), name: .PIADaemonsDidUpdateConnectivity, object: nil) nc.addObserver(self, selector: #selector(checkVPNConnectingStatus(notification:)), name: .PIADaemonsConnectingVPNStatus, object: nil) + nc.addObserver(self, selector: #selector(checkConnectToRFC1918VulnerableWifi(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil) + nc.addObserver(self, selector: #selector(handleDidConnectToRFC1918CompliantWifi(_:)), name: NSNotification.Name.DeviceDidConnectToRFC1918CompliantWifi, object: nil) + nc.addObserver(self, selector: #selector(checkConnectToRFC1918VulnerableWifi(_:)), name: NSNotification.Name.DeviceDidConnectToRFC1918VulnerableWifi, object: nil) + self.viewContentHeight = self.viewContentHeightConstraint.constant } @@ -603,6 +608,60 @@ class DashboardViewController: AutolayoutViewController { } } + @objc func checkConnectToRFC1918VulnerableWifi(_ notification: Notification? = nil) { + + guard let currentRFC1918VulnerableWifiName = Client.preferences.currentRFC1918VulnerableWifi, + WifiNetworkMonitor().isConnected() else { return } + + guard Client.preferences.allowLocalDeviceAccess + && Client.preferences.leakProtection else { return } + + guard let connection = notification?.object as? NEVPNConnection else { + if Client.providers.vpnProvider.isVPNConnected { + DispatchQueue.main.async { + self.presentRFC1918VulnerableWifiAlert() + } + } + + return + } + + if connection.status == .connected { + DispatchQueue.main.async { + self.presentRFC1918VulnerableWifiAlert() + } + } + } + + private func presentRFC1918VulnerableWifiAlert() { + guard let window = UIApplication.shared.delegate?.window, + let presentedViewController = window?.rootViewController?.presentedViewController ?? window?.rootViewController + else { return } + + let title = L10n.Dashboard.Vpn.LeakProtectionAlert.title + + if let alertController = presentedViewController as? UIAlertController, + alertController.title == title { return } + + let sheet = Macros.alertController(title, L10n.Dashboard.Vpn.LeakProtectionAlert.message) + sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta1, style: .default, handler: { _ in + + })) + + sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta2, style: .default, handler: { _ in + + })) + + sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta3, style: .default, handler: nil)) + + presentedViewController.present(sheet, animated: true, completion: nil) + } + + @objc func handleDidConnectToRFC1918CompliantWifi(_ notification: Notification) { + // Remove non compliant wifi notification if it was present in notification center + //removeNonCompliantWifiLocalNotification() + } + // MARK: Helpers @objc private func vpnDidFail() { if !isDisconnecting { From 69a2e86f3797a56238527eb38e803ad77c70cf60 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 14 Aug 2023 22:23:04 +0200 Subject: [PATCH 103/159] PIA-54: Add logic to disable and reconnect CTA --- PIA VPN/DashboardViewController.swift | 47 +++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 7f8c9c8db..8a95ddb0a 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -118,7 +118,7 @@ class DashboardViewController: AutolayoutViewController { nc.addObserver(self, selector: #selector(checkInternetConnection), name: .PIADaemonsDidUpdateConnectivity, object: nil) nc.addObserver(self, selector: #selector(checkVPNConnectingStatus(notification:)), name: .PIADaemonsConnectingVPNStatus, object: nil) - nc.addObserver(self, selector: #selector(checkConnectToRFC1918VulnerableWifi(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil) + nc.addObserver(self, selector: #selector(connectionVPNStatusDidChange(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil) nc.addObserver(self, selector: #selector(handleDidConnectToRFC1918CompliantWifi(_:)), name: NSNotification.Name.DeviceDidConnectToRFC1918CompliantWifi, object: nil) nc.addObserver(self, selector: #selector(checkConnectToRFC1918VulnerableWifi(_:)), name: NSNotification.Name.DeviceDidConnectToRFC1918VulnerableWifi, object: nil) @@ -608,32 +608,38 @@ class DashboardViewController: AutolayoutViewController { } } + @objc func connectionVPNStatusDidChange(_ notification: Notification? = nil) { + guard let connection = notification?.object as? NEVPNConnection, + connection.status == .connected else { return } + + handleNonCompliantWifiConnection() + } + @objc func checkConnectToRFC1918VulnerableWifi(_ notification: Notification? = nil) { + guard Client.providers.vpnProvider.isVPNConnected else { return } + handleNonCompliantWifiConnection() + } + + @objc func handleDidConnectToRFC1918CompliantWifi(_ notification: Notification) { + // Remove non compliant wifi notification if it was present in notification center + //removeNonCompliantWifiLocalNotification() + } + + private func handleNonCompliantWifiConnection() { guard let currentRFC1918VulnerableWifiName = Client.preferences.currentRFC1918VulnerableWifi, WifiNetworkMonitor().isConnected() else { return } guard Client.preferences.allowLocalDeviceAccess && Client.preferences.leakProtection else { return } - guard let connection = notification?.object as? NEVPNConnection else { - if Client.providers.vpnProvider.isVPNConnected { - DispatchQueue.main.async { - self.presentRFC1918VulnerableWifiAlert() - } - } - - return - } - - if connection.status == .connected { - DispatchQueue.main.async { - self.presentRFC1918VulnerableWifiAlert() - } + DispatchQueue.main.async { + self.presentNonCompliantWifiAlert() + self.showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: currentRFC1918VulnerableWifiName) } } - private func presentRFC1918VulnerableWifiAlert() { + private func presentNonCompliantWifiAlert() { guard let window = UIApplication.shared.delegate?.window, let presentedViewController = window?.rootViewController?.presentedViewController ?? window?.rootViewController else { return } @@ -645,7 +651,9 @@ class DashboardViewController: AutolayoutViewController { let sheet = Macros.alertController(title, L10n.Dashboard.Vpn.LeakProtectionAlert.message) sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta1, style: .default, handler: { _ in - + Client.preferences.allowLocalDeviceAccess = false + Client.providers.vpnProvider.reconnect(after: nil, forceDisconnect: true, { error in + }) })) sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta2, style: .default, handler: { _ in @@ -657,9 +665,8 @@ class DashboardViewController: AutolayoutViewController { presentedViewController.present(sheet, animated: true, completion: nil) } - @objc func handleDidConnectToRFC1918CompliantWifi(_ notification: Notification) { - // Remove non compliant wifi notification if it was present in notification center - //removeNonCompliantWifiLocalNotification() + func showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: String) { + //TODO: Will be implemented on https://polymoon.atlassian.net/browse/PIA-57 } // MARK: Helpers From 6242a896c6936d99f927d849473fec7447b6b27a Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Thu, 17 Aug 2023 14:19:20 +0200 Subject: [PATCH 104/159] PIA-57: Update feature flags on dev builds from debug menu --- PIA VPN/SettingOptions.swift | 8 ++++- .../DevelopmentSettingsViewController.swift | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/PIA VPN/SettingOptions.swift b/PIA VPN/SettingOptions.swift index 58805a109..da95697ef 100644 --- a/PIA VPN/SettingOptions.swift +++ b/PIA VPN/SettingOptions.swift @@ -295,6 +295,8 @@ public enum DevelopmentSections: Int, SettingSection, EnumsBuilder { case resolveGoogleAdsDomain case deleteKeychain case crash + case leakProtectionFlag + case leakProtectionNotificationsFlag public func localizedTitleMessage() -> String { switch self { @@ -307,6 +309,8 @@ public enum DevelopmentSections: Int, SettingSection, EnumsBuilder { case .resolveGoogleAdsDomain: return "Resolve Google Ads Domain" case .deleteKeychain: return "Delete the Keychain" case .crash: return "Crash the app" + case .leakProtectionFlag: return "FF - Leak Protection" + case .leakProtectionNotificationsFlag: return "FF - Leak Protection Notifications" } } @@ -321,11 +325,13 @@ public enum DevelopmentSections: Int, SettingSection, EnumsBuilder { case .resolveGoogleAdsDomain: return "" case .deleteKeychain: return "" case .crash: return "" + case .leakProtectionFlag: return "" + case .leakProtectionNotificationsFlag: return "" } } public static func all() -> [Self] { - return [.stagingVersion, .customServers, .publicUsername, .username, .password, .environment, .resolveGoogleAdsDomain, .deleteKeychain, .crash] + return [.stagingVersion, .customServers, .publicUsername, .username, .password, .environment, .resolveGoogleAdsDomain, .deleteKeychain, .crash, .leakProtectionFlag, .leakProtectionNotificationsFlag] } } diff --git a/PIA VPN/Settings/DevelopmentSettingsViewController.swift b/PIA VPN/Settings/DevelopmentSettingsViewController.swift index 35ccc6292..3d0793ac5 100644 --- a/PIA VPN/Settings/DevelopmentSettingsViewController.swift +++ b/PIA VPN/Settings/DevelopmentSettingsViewController.swift @@ -32,6 +32,8 @@ class DevelopmentSettingsViewController: PIABaseSettingsViewController { @IBOutlet weak var tableView: UITableView! private lazy var switchEnvironment = UISwitch() + private lazy var switchLeakProtectionFlag = UISwitch() + private lazy var switchLeakProtectionNotificationsFlag = UISwitch() private var controller: OptionsViewController? override func viewDidLoad() { @@ -44,6 +46,8 @@ class DevelopmentSettingsViewController: PIABaseSettingsViewController { tableView.delegate = self tableView.dataSource = self switchEnvironment.addTarget(self, action: #selector(toggleEnv(_:)), for: .valueChanged) + + addFeatureFlagsTogglesActions() NotificationCenter.default.addObserver(self, selector: #selector(reloadSettings), name: .PIASettingsHaveChanged, object: nil) } @@ -145,6 +149,18 @@ extension DevelopmentSettingsViewController: UITableViewDelegate, UITableViewDat case .crash: cell.textLabel?.text = "Crash" cell.detailTextLabel?.text = nil + case .leakProtectionFlag: + cell.textLabel?.text = "FF - Leak Protection" + cell.detailTextLabel?.text = nil + cell.accessoryView = switchLeakProtectionFlag + cell.selectionStyle = .none + switchLeakProtectionFlag.isOn = AppPreferences.shared.showLeakProtection + case .leakProtectionNotificationsFlag: + cell.textLabel?.text = "FF - Leak Protection Notifications" + cell.detailTextLabel?.text = nil + cell.accessoryView = switchLeakProtectionNotificationsFlag + cell.selectionStyle = .none + switchLeakProtectionNotificationsFlag.isOn = AppPreferences.shared.showLeakProtectionNotifications } } @@ -346,3 +362,23 @@ extension DevelopmentSettingsViewController: OptionsViewControllerDelegate { } } + + +// MARK: - Feature Flags Toggles + +extension DevelopmentSettingsViewController { + @objc private func toggleLeakProtectionFlag(_ sender: UISwitch) { + AppPreferences.shared.showLeakProtection = sender.isOn + } + + @objc private func toggleLeakProtectionNotificationsFlag(_ sender: UISwitch) { + AppPreferences.shared.showLeakProtectionNotifications = sender.isOn + } + + private func addFeatureFlagsTogglesActions() { + switchLeakProtectionFlag.addTarget(self, action: #selector(toggleLeakProtectionFlag(_:)), for: .valueChanged) + switchLeakProtectionNotificationsFlag.addTarget(self, action: #selector(toggleLeakProtectionNotificationsFlag(_:)), for: .valueChanged) + + // Additional Feature Flags toggles actions here + } +} From da209f7266f6001143555cbe821d9ea9cfe6e829 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Fri, 18 Aug 2023 10:37:24 +0200 Subject: [PATCH 105/159] PIA-57: Skip updating feature flag values when pulling from CSI server on dev builds --- PIA VPN/Bootstrapper.swift | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index eb3968d08..0f5f4581a 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -41,6 +41,25 @@ class Bootstrapper { return false #endif } + + var isDevelopmentBuild: Bool { + #if PIA_DEV + return true + #else + return false + #endif + } + + /// Update the values of the flags from the CSI server + private func updateFeatureFlagsForReleaseIfNeeded() { + // Some feature flags like Leak Protection are controled from the Developer menu on Dev builds. + // So we skip updating the flag from the server on dev builds + guard !isDevelopmentBuild else { return } + + // Leak Protection feature flags + AppPreferences.shared.showLeakProtection = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtection) + AppPreferences.shared.showLeakProtectionNotifications = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtectionNotifications) + } func bootstrap() { let console = ConsoleDestination() @@ -130,8 +149,12 @@ class Bootstrapper { AppPreferences.shared.checksDipExpirationRequest = Client.configuration.featureFlags.contains(Client.FeatureFlags.checkDipExpirationRequest) AppPreferences.shared.disablesMultiDipTokens = Client.configuration.featureFlags.contains(Client.FeatureFlags.disableMultiDipTokens) AppPreferences.shared.showNewInitialScreen = Client.configuration.featureFlags.contains(Client.FeatureFlags.showNewInitialScreen) - AppPreferences.shared.showLeakProtection = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtection) - AppPreferences.shared.showLeakProtectionNotifications = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtectionNotifications) + + + /// Updates the feature flags values to the ones set on the server only on Release builds. + /// (like Leak protection feature) + self.updateFeatureFlagsForReleaseIfNeeded() + }) //FORCE THE MIGRATION TO GEN4 From 0fed40b3a05d05a606bc8b6bc3b87dd65123ea8e Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Mon, 21 Aug 2023 16:13:57 +0200 Subject: [PATCH 106/159] PIA-57: Show leak protection local notification when connected to a non-compliant Wi-Fi network --- PIA VPN/DashboardViewController.swift | 9 +++++++-- PIA VPN/SwiftGen+Strings.swift | 11 +++++++++++ PIA VPN/en.lproj/Localizable.strings | 3 +++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 8a95ddb0a..d8a5c7324 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -632,7 +632,8 @@ class DashboardViewController: AutolayoutViewController { guard Client.preferences.allowLocalDeviceAccess && Client.preferences.leakProtection else { return } - + guard AppPreferences.shared.showLeakProtectionNotifications else { return } + DispatchQueue.main.async { self.presentNonCompliantWifiAlert() self.showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: currentRFC1918VulnerableWifiName) @@ -666,7 +667,11 @@ class DashboardViewController: AutolayoutViewController { } func showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: String) { - //TODO: Will be implemented on https://polymoon.atlassian.net/browse/PIA-57 + // 1. Remove previous non-compliant wifi notification + Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) + + // 2. Show the local notification for the current non-compliant wifi + Macros.showLocalNotificationIfNotAlreadyPresent(NotificationCategory.nonCompliantWifi, type: NotificationCategory.nonCompliantWifi, body: L10n.LocalNotification.NonCompliantWifi.text, title: L10n.LocalNotification.NonCompliantWifi.title(currentRFC1918VulnerableWifiName), delay: 0) } // MARK: Helpers diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index d5e4ab363..9ce56a57f 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -513,6 +513,17 @@ internal enum L10n { } } } + + internal enum LocalNotification { + internal enum NonCompliantWifi { + /// Tap here to secure your device + internal static let text = L10n.tr("Localizable", "local_notification.non_compliant_wifi.text") + /// Unsecured Wi-Fi: %@ + internal static func title(_ p1: Any) -> String { + return L10n.tr("Localizable", "local_notification.non_compliant_wifi.title", String(describing: p1)) + } + } + } internal enum Menu { internal enum Accessibility { diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index e62cc7dfd..ec9eed8ec 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -38,6 +38,9 @@ "expiration.title" = "Renewal"; "expiration.message" = "Your subscription expires soon. Renew to stay protected."; +"local_notification.non_compliant_wifi.title" = "Unsecured Wi-Fi: %@"; +"local_notification.non_compliant_wifi.text" = "Tap here to secure your device"; + // SHORTCUTS "shortcuts.connect" = "Connect"; From 36a6f36dd6ad87ea626b48f619174a2bbab565bf Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Tue, 22 Aug 2023 16:00:33 +0200 Subject: [PATCH 107/159] PIA-61: Remove leak protection notification when the device connects to a compliant wifi --- PIA VPN/AppDelegate.swift | 2 ++ PIA VPN/DashboardViewController.swift | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/PIA VPN/AppDelegate.swift b/PIA VPN/AppDelegate.swift index c4a2b8882..60844de7a 100644 --- a/PIA VPN/AppDelegate.swift +++ b/PIA VPN/AppDelegate.swift @@ -198,6 +198,8 @@ class AppDelegate: NSObject, UIApplicationDelegate { func applicationDidBecomeActive(_ application: UIApplication) { application.applicationIconBadgeNumber = 0 + // Remove the Non compliant Wifi local notification as the app is in foreground now + Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) } private func refreshShortcutItems(in application: UIApplication) { diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index d8a5c7324..683dff8a9 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -623,7 +623,7 @@ class DashboardViewController: AutolayoutViewController { @objc func handleDidConnectToRFC1918CompliantWifi(_ notification: Notification) { // Remove non compliant wifi notification if it was present in notification center - //removeNonCompliantWifiLocalNotification() + Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) } private func handleNonCompliantWifiConnection() { From d98306ec4dc3ec8e449ac17ba50b4155a9e83b76 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Wed, 23 Aug 2023 10:01:08 +0200 Subject: [PATCH 108/159] PIA-62: Remove leak protection local notification when VPN is disconnected --- PIA VPN/DashboardViewController.swift | 29 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 683dff8a9..41a553c37 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -609,10 +609,16 @@ class DashboardViewController: AutolayoutViewController { } @objc func connectionVPNStatusDidChange(_ notification: Notification? = nil) { - guard let connection = notification?.object as? NEVPNConnection, - connection.status == .connected else { return } + guard let connection = notification?.object as? NEVPNConnection else { return } - handleNonCompliantWifiConnection() + switch connection.status { + case .connected: + handleNonCompliantWifiConnection() + case .disconnected: + removeNonCompliantWifiLocalNotification() + default: + break + } } @objc func checkConnectToRFC1918VulnerableWifi(_ notification: Notification? = nil) { @@ -623,7 +629,7 @@ class DashboardViewController: AutolayoutViewController { @objc func handleDidConnectToRFC1918CompliantWifi(_ notification: Notification) { // Remove non compliant wifi notification if it was present in notification center - Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) + removeNonCompliantWifiLocalNotification() } private func handleNonCompliantWifiConnection() { @@ -667,11 +673,16 @@ class DashboardViewController: AutolayoutViewController { } func showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: String) { - // 1. Remove previous non-compliant wifi notification - Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) - - // 2. Show the local notification for the current non-compliant wifi - Macros.showLocalNotificationIfNotAlreadyPresent(NotificationCategory.nonCompliantWifi, type: NotificationCategory.nonCompliantWifi, body: L10n.LocalNotification.NonCompliantWifi.text, title: L10n.LocalNotification.NonCompliantWifi.title(currentRFC1918VulnerableWifiName), delay: 0) + // 1. Remove previous non-compliant wifi notification + removeNonCompliantWifiLocalNotification() + + // 2. Show the local notification for the current non-compliant wifi + Macros.showLocalNotificationIfNotAlreadyPresent(NotificationCategory.nonCompliantWifi, type: NotificationCategory.nonCompliantWifi, body: L10n.LocalNotification.NonCompliantWifi.text, title: L10n.LocalNotification.NonCompliantWifi.title(currentRFC1918VulnerableWifiName), delay: 0) + } + + private func removeNonCompliantWifiLocalNotification() { + // Remove non compliant wifi notification if it was present in notification center + Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) } // MARK: Helpers From aa8ac0a3e2765bda6c93e9234b5a5fc784132bd3 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Fri, 25 Aug 2023 14:26:02 +0200 Subject: [PATCH 109/159] PIA-325: Add logic to make sure we reconnect after disconnection was completed --- PIA VPN/DashboardViewController.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 41a553c37..aa1182c0c 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -77,6 +77,8 @@ class DashboardViewController: AutolayoutViewController { self.updateTileLayout() } } + + private var shouldReconnect = false deinit { NotificationCenter.default.removeObserver(self) @@ -616,6 +618,10 @@ class DashboardViewController: AutolayoutViewController { handleNonCompliantWifiConnection() case .disconnected: removeNonCompliantWifiLocalNotification() + if shouldReconnect { + Client.providers.vpnProvider.connect { _ in } + shouldReconnect = false + } default: break } @@ -659,8 +665,9 @@ class DashboardViewController: AutolayoutViewController { let sheet = Macros.alertController(title, L10n.Dashboard.Vpn.LeakProtectionAlert.message) sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta1, style: .default, handler: { _ in Client.preferences.allowLocalDeviceAccess = false - Client.providers.vpnProvider.reconnect(after: nil, forceDisconnect: true, { error in - }) + Client.providers.vpnProvider.disconnect { _ in + self.shouldReconnect = true + } })) sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta2, style: .default, handler: { _ in From 033e98322aa2a0f758c2fd4bec62904091a8d80d Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Fri, 25 Aug 2023 15:21:00 +0200 Subject: [PATCH 110/159] PIA-326: Show alert whencurrent vpn status is not connected --- PIA VPN/DashboardViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index aa1182c0c..fa7d5e5a6 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -615,7 +615,9 @@ class DashboardViewController: AutolayoutViewController { switch connection.status { case .connected: - handleNonCompliantWifiConnection() + if !Client.providers.vpnProvider.isVPNConnected { + handleNonCompliantWifiConnection() + } case .disconnected: removeNonCompliantWifiLocalNotification() if shouldReconnect { From a1d3c2c7199c6de782bf13c798e5af78857d84f9 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Mon, 28 Aug 2023 11:25:22 +0200 Subject: [PATCH 111/159] PIA-56: Handle learn more action on leak protection alert --- PIA VPN/AppConstants.swift | 2 ++ PIA VPN/DashboardViewController.swift | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/PIA VPN/AppConstants.swift b/PIA VPN/AppConstants.swift index 49a940b15..bd3dd4da1 100644 --- a/PIA VPN/AppConstants.swift +++ b/PIA VPN/AppConstants.swift @@ -89,6 +89,8 @@ struct AppConstants { static let csEmail = "helpdesk+vpnpermissions.ios@privateinternetaccess.com" static let ovpnMigrationURL = URL(string: "https://www.privateinternetaccess.com/helpdesk/kb/articles/removing-openvpn-handshake-and-authentication-settings")! + + static let leakProtectionURL = URL(string: "\(Self.supportURL.absoluteString)/kb/articles/what-is-pia-s-leak-protection-feature-on-ios")! static var stagingEndpointURL: URL? = { guard let path = Bundle.main.path(forResource: "staging", ofType: "endpoint") else { diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index fa7d5e5a6..411009fc6 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -672,8 +672,14 @@ class DashboardViewController: AutolayoutViewController { } })) + // Learn More action sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta2, style: .default, handler: { _ in + let application = UIApplication.shared + let learnMoreURL = AppConstants.Web.leakProtectionURL + if application.canOpenURL(learnMoreURL) { + application.open(learnMoreURL) + } })) sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta3, style: .default, handler: nil)) From df409bb87675038a13fb53f1c30448bf6f57e333 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Mon, 28 Aug 2023 16:33:30 +0200 Subject: [PATCH 112/159] PIA-63: Handle 'More info' action from the Leak Protection settings description --- ...rivacyFeaturesSettingsViewController.swift | 24 +++++++++++++++---- PIA VPN/SwiftGen+Strings.swift | 2 ++ PIA VPN/en.lproj/Localizable.strings | 3 ++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index c018cfc2a..84a386f5b 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -199,7 +199,12 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie cell.textLabel?.text = L10n.Settings.ApplicationSettings.KillSwitch.footer return cell case .leakProtection: - cell.textLabel?.text = L10n.Settings.ApplicationSettings.LeakProtection.footer + let leakProtectionDescription = L10n.Settings.ApplicationSettings.LeakProtection.footer + let attributtedDescription = NSMutableAttributedString(string: leakProtectionDescription) + let moreInfoText = L10n.Settings.ApplicationSettings.LeakProtection.moreInfo + let moreInfoTextRange = (leakProtectionDescription as NSString).range(of: moreInfoText) + attributtedDescription.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: moreInfoTextRange) + cell.textLabel?.attributedText = attributtedDescription return cell case .allowAccessOnLocalNetwork: cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.footer @@ -276,11 +281,20 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let section = sections[indexPath.section] - + switch section { - case .refresh: - refreshContentBlockerRules() - default: break + case .refresh: + refreshContentBlockerRules() + case .leakProtection: + let application = UIApplication.shared + let learnMoreURL = AppConstants.Web.leakProtectionURL + + // Open the Learn more url when the user taps on the Leak Protection cell + if application.canOpenURL(learnMoreURL) { + application.open(learnMoreURL) + } + + default: break } tableView.deselectRow(at: indexPath, animated: true) diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index 9ce56a57f..7f40a704f 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -829,6 +829,8 @@ internal enum L10n { internal static let footer = L10n.tr("Localizable", "settings.application_settings.leak_protection.footer") /// VPN Leak Protection internal static let title = L10n.tr("Localizable", "settings.application_settings.leak_protection.title") + /// More info + internal static let moreInfo = L10n.tr("Localizable", "settings.application_settings.leak_protection.more_info") } internal enum AllowAccessOnLocalNetwork { internal static let footer = L10n.tr("Localizable", "settings.application_settings.allow_local_network.footer") diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index ec9eed8ec..8d0c2ac64 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -138,7 +138,8 @@ "settings.application_settings.kill_switch.title" = "VPN Kill Switch"; "settings.application_settings.kill_switch.footer" = "The VPN kill switch prevents access to the Internet if the VPN connection is reconnecting. This excludes disconnecting manually."; "settings.application_settings.leak_protection.title" = "Leak Protection"; -"settings.application_settings.leak_protection.footer" = "When enabled, all traffic will be routed over the VPN including iOS System services including AirDrop, CarPlay, Airplay and Personal Hotspots. These systems may not function as expected while network protection is enabled. Learn More."; +"settings.application_settings.leak_protection.footer" = "iOS includes features designed to operate outside the VPN by default, such as AirDrop, CarPlay, AirPlay, and Personal Hotspots. Enabling custom leak protection routes this traffic through the VPN but may affect how these features function. More info"; +"settings.application_settings.leak_protection.more_info" = "More info"; "settings.application_settings.allow_local_network.title" = "Allow access to devices on local network"; "settings.application_settings.allow_local_network.footer" = "Stay connected to local devices like printers or file servers while connected to the VPN. (Allow this only if you trust the people and devices on your network.)"; "settings.application_settings.mace.title" = "PIA MACE™"; From d5366fc5bd76a3b079c1c147ed5f1a0b6d4181e5 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Tue, 29 Aug 2023 12:09:37 +0200 Subject: [PATCH 113/159] PIA-337: Show leak protection content in English if no translations are available in other languages --- PIA VPN/DashboardViewController.swift | 10 ++-- PIA VPN/SettingOptions.swift | 2 +- ...rivacyFeaturesSettingsViewController.swift | 6 +-- PIA VPN/SwiftGen+Strings.swift | 46 ++++++++++++------- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 411009fc6..cac799b88 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -659,13 +659,13 @@ class DashboardViewController: AutolayoutViewController { let presentedViewController = window?.rootViewController?.presentedViewController ?? window?.rootViewController else { return } - let title = L10n.Dashboard.Vpn.LeakProtectionAlert.title + let title = L10n.Dashboard.Vpn.Leakprotection.Alert.title if let alertController = presentedViewController as? UIAlertController, alertController.title == title { return } - let sheet = Macros.alertController(title, L10n.Dashboard.Vpn.LeakProtectionAlert.message) - sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta1, style: .default, handler: { _ in + let sheet = Macros.alertController(title, L10n.Dashboard.Vpn.Leakprotection.Alert.message) + sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta1, style: .default, handler: { _ in Client.preferences.allowLocalDeviceAccess = false Client.providers.vpnProvider.disconnect { _ in self.shouldReconnect = true @@ -673,7 +673,7 @@ class DashboardViewController: AutolayoutViewController { })) // Learn More action - sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta2, style: .default, handler: { _ in + sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta2, style: .default, handler: { _ in let application = UIApplication.shared let learnMoreURL = AppConstants.Web.leakProtectionURL @@ -682,7 +682,7 @@ class DashboardViewController: AutolayoutViewController { } })) - sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.LeakProtectionAlert.cta3, style: .default, handler: nil)) + sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta3, style: .default, handler: nil)) presentedViewController.present(sheet, animated: true, completion: nil) } diff --git a/PIA VPN/SettingOptions.swift b/PIA VPN/SettingOptions.swift index da95697ef..d72c74427 100644 --- a/PIA VPN/SettingOptions.swift +++ b/PIA VPN/SettingOptions.swift @@ -199,7 +199,7 @@ public enum PrivacyFeaturesSections: Int, SettingSection, EnumsBuilder { switch self { case .killswitch: return L10n.Settings.ApplicationSettings.KillSwitch.title case .leakProtection: return L10n.Settings.ApplicationSettings.LeakProtection.title - case .allowAccessOnLocalNetwork: return L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.title + case .allowAccessOnLocalNetwork: return L10n.Settings.ApplicationSettings.AllowLocalNetwork.title case .safariContentBlocker: return L10n.Settings.ContentBlocker.title case .refresh: return L10n.Settings.ContentBlocker.Refresh.title } diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index 84a386f5b..f6fe859bb 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -149,7 +149,7 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { return } - let sheet = Macros.alertController(L10n.Settings.ApplicationSettings.LeakProtectionAlert.title, nil) + let sheet = Macros.alertController(L10n.Settings.ApplicationSettings.LeakProtection.Alert.title, nil) sheet.addAction(UIAlertAction(title: L10n.Global.ok, style: .default, handler: nil)) present(sheet, animated: true) } @@ -207,7 +207,7 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie cell.textLabel?.attributedText = attributtedDescription return cell case .allowAccessOnLocalNetwork: - cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.footer + cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowLocalNetwork.footer return cell case .safariContentBlocker: cell.textLabel?.text = L10n.Settings.ContentBlocker.footer @@ -241,7 +241,7 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie cell.selectionStyle = .none switchLeakProtection.isOn = Client.preferences.leakProtection case .allowAccessOnLocalNetwork: - cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowAccessOnLocalNetwork.title + cell.textLabel?.text = L10n.Settings.ApplicationSettings.AllowLocalNetwork.title cell.detailTextLabel?.text = nil cell.accessoryView = switchAllowDevicesOnLocalNetwork cell.selectionStyle = .none diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index 7f40a704f..9194bf1f9 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -208,13 +208,19 @@ internal enum L10n { /// This network is untrusted. Do you really want to disconnect the VPN? internal static let untrusted = L10n.tr("Localizable", "dashboard.vpn.disconnect.untrusted") } - internal enum LeakProtectionAlert { - /// This network is untrusted. Do you really want to disconnect the VPN? - internal static let title = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.title") - internal static let message = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.message") - internal static let cta1 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta1") - internal static let cta2 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta2") - internal static let cta3 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta3") + internal enum Leakprotection { + internal enum Alert { + /// Disable Now + internal static let cta1 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta1", fallback: "Disable Now") + /// Learn more + internal static let cta2 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta2", fallback: "Learn more") + /// Ignore + internal static let cta3 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.cta3", fallback: "Ignore") + /// To prevent data leaks, tap Disable Now to turn off “Allow access to devices on local network" and automatically reconnect. + internal static let message = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.message", fallback: "To prevent data leaks, tap Disable Now to turn off “Allow access to devices on local network\" and automatically reconnect.") + /// Unsecured Wi-Fi detected + internal static let title = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.title", fallback: "Unsecured Wi-Fi detected") + } } } } @@ -826,16 +832,22 @@ internal enum L10n { internal static let title = L10n.tr("Localizable", "settings.application_settings.kill_switch.title") } internal enum LeakProtection { - internal static let footer = L10n.tr("Localizable", "settings.application_settings.leak_protection.footer") - /// VPN Leak Protection - internal static let title = L10n.tr("Localizable", "settings.application_settings.leak_protection.title") + /// iOS includes features designed to operate outside the VPN by default, such as AirDrop, CarPlay, AirPlay, and Personal Hotspots. Enabling custom leak protection routes this traffic through the VPN but may affect how these features function. More info + internal static let footer = L10n.tr("Localizable", "settings.application_settings.leak_protection.footer", fallback: "iOS includes features designed to operate outside the VPN by default, such as AirDrop, CarPlay, AirPlay, and Personal Hotspots. Enabling custom leak protection routes this traffic through the VPN but may affect how these features function. More info") /// More info - internal static let moreInfo = L10n.tr("Localizable", "settings.application_settings.leak_protection.more_info") - } - internal enum AllowAccessOnLocalNetwork { - internal static let footer = L10n.tr("Localizable", "settings.application_settings.allow_local_network.footer") + internal static let moreInfo = L10n.tr("Localizable", "settings.application_settings.leak_protection.more_info", fallback: "More info") + /// Leak Protection + internal static let title = L10n.tr("Localizable", "settings.application_settings.leak_protection.title", fallback: "Leak Protection") + internal enum Alert { + /// Changes to the VPN Settings will take effect on the next connection + internal static let title = L10n.tr("Localizable", "settings.application_settings.leak_protection.alert.title", fallback: "Changes to the VPN Settings will take effect on the next connection") + } + } + internal enum AllowLocalNetwork { + /// Stay connected to local devices like printers or file servers while connected to the VPN. (Allow this only if you trust the people and devices on your network.) + internal static let footer = L10n.tr("Localizable", "settings.application_settings.allow_local_network.footer", fallback: "Stay connected to local devices like printers or file servers while connected to the VPN. (Allow this only if you trust the people and devices on your network.)") /// Allow access to devices on local network - internal static let title = L10n.tr("Localizable", "settings.application_settings.allow_local_network.title") + internal static let title = L10n.tr("Localizable", "settings.application_settings.allow_local_network.title", fallback: "Allow access to devices on local network") } internal enum Mace { /// PIA MACE™ blocks ads, trackers, and malware while you're connected to the VPN. @@ -1305,8 +1317,8 @@ internal enum L10n { // MARK: - Implementation Details extension L10n { - private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String { - let format = BundleToken.bundle.localizedString(forKey: key, value: nil, table: table) + private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String? = nil) -> String { + let format = BundleToken.bundle.localizedString(forKey: key, value: value, table: table) return String(format: format, locale: Locale.current, arguments: args) } } From 150608c3ffd6cbdc03528cbe5bc1a7dd67669d60 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Tue, 29 Aug 2023 12:06:16 +0200 Subject: [PATCH 114/159] PIA-314: Add non-compliant check for current WIFI --- PIA VPN/DashboardViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 411009fc6..4eff7e1be 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -641,12 +641,17 @@ class DashboardViewController: AutolayoutViewController { } private func handleNonCompliantWifiConnection() { - guard let currentRFC1918VulnerableWifiName = Client.preferences.currentRFC1918VulnerableWifi, - WifiNetworkMonitor().isConnected() else { return } + guard WifiNetworkMonitor().isConnected() else { return } + + guard Client.preferences.currentRFC1918VulnerableWifi != nil + || WifiNetworkMonitor().checkForRFC1918Vulnerability() else { return } guard Client.preferences.allowLocalDeviceAccess && Client.preferences.leakProtection else { return } + guard AppPreferences.shared.showLeakProtectionNotifications else { return } + + let currentRFC1918VulnerableWifiName = Client.preferences.currentRFC1918VulnerableWifi ?? "" DispatchQueue.main.async { self.presentNonCompliantWifiAlert() From f93d4da5af6b722c6f4278dd0f140876b4caa841 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Tue, 29 Aug 2023 14:36:14 +0200 Subject: [PATCH 115/159] PIA-62: Remove leak proteciton notification when vpn is disconnected and app in the foreground --- PIA VPN/DashboardViewController.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index cac799b88..03a07ff82 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -619,7 +619,14 @@ class DashboardViewController: AutolayoutViewController { handleNonCompliantWifiConnection() } case .disconnected: - removeNonCompliantWifiLocalNotification() + + let state = UIApplication.shared.applicationState + + // Only remove the notification if the app is on the foreground + if state == .active { + removeNonCompliantWifiLocalNotification() + } + if shouldReconnect { Client.providers.vpnProvider.connect { _ in } shouldReconnect = false From 9e730b4cc169236b6c53e1da7dfc73ddeea2a45c Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Wed, 30 Aug 2023 14:34:33 +0200 Subject: [PATCH 116/159] PIA-335: Dismiss leak protection alert when disconnecting from quick action --- PIA VPN/AppDelegate.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/PIA VPN/AppDelegate.swift b/PIA VPN/AppDelegate.swift index 60844de7a..350977a1a 100644 --- a/PIA VPN/AppDelegate.swift +++ b/PIA VPN/AppDelegate.swift @@ -257,6 +257,8 @@ class AppDelegate: NSObject, UIApplicationDelegate { case .disconnect: if Client.providers.vpnProvider.isVPNConnected { + // Dismiss the Leak Protection alert if present when disconnecting from a Quick Action + dismissLeakProtectionAlert() // this time delay seems to fix a strange issue of the VPN disconnecting and // then automatically reconnecting when it's done from a fresh launch @@ -319,3 +321,17 @@ extension AppDelegate { } } + + +extension AppDelegate { + + private func dismissLeakProtectionAlert() { + if let presentedAlert = window?.rootViewController?.presentedViewController as? UIAlertController { + let leakProtectionAlertTitle = L10n.Dashboard.Vpn.Leakprotection.Alert.title + + if presentedAlert.title == leakProtectionAlertTitle { + presentedAlert.dismiss(animated: true) + } + } + } +} From a1a83c25c99faef08229cae2d2dc9ee8a7c01b63 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Thu, 31 Aug 2023 13:42:10 +0200 Subject: [PATCH 117/159] PIA-350: Show leak protection local notification in English when untranslated --- PIA VPN/SwiftGen+Strings.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index 9194bf1f9..aa1043fa6 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -523,10 +523,10 @@ internal enum L10n { internal enum LocalNotification { internal enum NonCompliantWifi { /// Tap here to secure your device - internal static let text = L10n.tr("Localizable", "local_notification.non_compliant_wifi.text") + internal static let text = L10n.tr("Localizable", "local_notification.non_compliant_wifi.text", fallback: "Tap here to secure your device") /// Unsecured Wi-Fi: %@ internal static func title(_ p1: Any) -> String { - return L10n.tr("Localizable", "local_notification.non_compliant_wifi.title", String(describing: p1)) + return L10n.tr("Localizable", "local_notification.non_compliant_wifi.title", String(describing: p1), fallback: "Unsecured Wi-Fi: \(p1)") } } } From c92ae0ab86c1a01f5e37260da306b55b1da123e5 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Thu, 31 Aug 2023 15:40:28 +0200 Subject: [PATCH 118/159] PIA-362: Remove leak protection alert when user connects to a compliant Wi-Fi --- PIA VPN/DashboardViewController.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index b24e87226..dfa6fe800 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -645,6 +645,9 @@ class DashboardViewController: AutolayoutViewController { @objc func handleDidConnectToRFC1918CompliantWifi(_ notification: Notification) { // Remove non compliant wifi notification if it was present in notification center removeNonCompliantWifiLocalNotification() + + // Remove leak protection alert when connecting to a compliant Wi-Fi + removeLeakProtectionAlert() } private func handleNonCompliantWifiConnection() { @@ -711,6 +714,14 @@ class DashboardViewController: AutolayoutViewController { // Remove non compliant wifi notification if it was present in notification center Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) } + + private func removeLeakProtectionAlert() { + guard let presentedLeakProtectionAlert = UIApplication.shared.delegate?.window??.rootViewController?.presentedViewController as? UIAlertController, + presentedLeakProtectionAlert.title == L10n.Dashboard.Vpn.Leakprotection.Alert.title else { return } + + presentedLeakProtectionAlert.dismiss(animated: true) + } + // MARK: Helpers @objc private func vpnDidFail() { From cdecb33aea99c075565fd5bb628fb2479ede2c32 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Fri, 8 Sep 2023 09:45:37 +0200 Subject: [PATCH 119/159] Bump version to 3.23.1 --- PIA VPN.xcodeproj/project.pbxproj | 63 ++++++++++++++++++------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 2ad84798d..466da903f 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2532,7 +2532,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN Tunnel/PIA VPN Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2543,7 +2543,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2566,9 +2566,10 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN Tunnel/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.1; @@ -2577,12 +2578,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; PROVISIONING_PROFILE = "b5b9e54d-7aba-4fc6-9320-adbce64c544a"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN.Tunnel"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.privateinternetaccess.ios.PIA-VPN.Tunnel"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -2598,7 +2600,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2618,7 +2620,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2642,7 +2644,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2662,7 +2664,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2754,7 +2756,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2766,7 +2768,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2791,9 +2793,10 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN AdBlocker/Info.plist"; @@ -2803,12 +2806,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "5aba703f-4bee-46e6-a5e4-42b785d1db55"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -2934,7 +2938,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2952,7 +2956,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -2971,10 +2975,12 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "Apple Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEVELOPMENT_TEAM = 5357M5NW9W; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -2991,10 +2997,11 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.privateinternetaccess.ios.PIA-VPN"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.privateinternetaccess.ios.PIA-VPN"; SWIFT_OBJC_BRIDGING_HEADER = ""; TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = app; @@ -3091,7 +3098,7 @@ CODE_SIGN_ENTITLEMENTS = PIAWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3103,7 +3110,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3133,9 +3140,10 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = PIAWidget/Info.plist; @@ -3145,12 +3153,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -3169,7 +3178,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN WG Tunnel/PIA_VPN_WG_Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3181,7 +3190,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3208,9 +3217,10 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20034; + CURRENT_PROJECT_VERSION = 20035; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "PIA VPN WG Tunnel/Info.plist"; @@ -3220,12 +3230,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.0; + MARKETING_VERSION = 3.23.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; From d20867ff1e515c540362ac19476fb4ffd7930f87 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 11 Sep 2023 12:57:10 +0200 Subject: [PATCH 120/159] PIA-454: Disable UI tests and failing tests from target --- PIA VPN.xcodeproj/project.pbxproj | 6 ----- .../xcshareddata/xcschemes/PIA VPN.xcscheme | 24 +++++++++++-------- .../Core/Utils/PIAHotspotHelperTests.swift | 3 ++- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 466da903f..11df20e0d 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -250,7 +250,6 @@ 82CAB87B255AEA3500BB08EF /* MessagesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB879255AEA3500BB08EF /* MessagesManager.swift */; }; 82CAB8B0255B050000BB08EF /* MessagesCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB8AF255B050000BB08EF /* MessagesCommands.swift */; }; 82CAB8B1255B050000BB08EF /* MessagesCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB8AF255B050000BB08EF /* MessagesCommands.swift */; }; - 82CAB8DA255BEC7000BB08EF /* PIACommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB8D9255BEC7000BB08EF /* PIACommandTests.swift */; }; 82CAB8E8255C0CB000BB08EF /* MessagesTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB8E7255C0CB000BB08EF /* MessagesTile.swift */; }; 82CAB8E9255C0CB000BB08EF /* MessagesTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB8E7255C0CB000BB08EF /* MessagesTile.swift */; }; 82CAB8F2255C0CD100BB08EF /* MessagesTileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB8F0255C0CD100BB08EF /* MessagesTileCollectionViewCell.swift */; }; @@ -1754,9 +1753,7 @@ }; 0EEE1BE61E4F6EF400397DE2 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 5357M5NW9W; LastSwiftMigration = 1010; - ProvisioningStyle = Automatic; TestTargetID = 291C637B183EBC210039EC03; }; 0EFB606F203D7A2C0095398C = { @@ -2208,7 +2205,6 @@ DD606AC821C9344100E0781D /* AppTests.swift in Sources */, 82A1AD2324B7C0020003DD02 /* PIACardTests.swift in Sources */, 8221922B24CECFE700C24F1C /* NMTTests.swift in Sources */, - 82CAB8DA255BEC7000BB08EF /* PIACommandTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2720,7 +2716,6 @@ CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -2739,7 +2734,6 @@ "PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = ""; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME)-Swift.h"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PIA VPN.app/PIA VPN"; diff --git a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN.xcscheme b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN.xcscheme index c8602f4fa..fbb06cfcc 100644 --- a/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN.xcscheme +++ b/PIA VPN.xcodeproj/xcshareddata/xcschemes/PIA VPN.xcscheme @@ -48,6 +48,20 @@ ReferencedContainer = "container:PIA VPN.xcodeproj"> + + + + - - - - Date: Wed, 23 Aug 2023 14:12:35 +0200 Subject: [PATCH 121/159] PIA-65: Hide leak protection settings when Wireguard is selected --- PIA VPN/DashboardViewController.swift | 7 ++++++ ...rivacyFeaturesSettingsViewController.swift | 24 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index dfa6fe800..26b38770a 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -663,6 +663,13 @@ class DashboardViewController: AutolayoutViewController { let currentRFC1918VulnerableWifiName = Client.preferences.currentRFC1918VulnerableWifi ?? "" + let selectedProtocol = Client.preferences.vpnType.vpnProtocol + let isWireguardSelected = selectedProtocol == PIAWGTunnelProfile.vpnType.vpnProtocol + let isOpenVPNSelected = selectedProtocol == PIATunnelProfile.vpnType.vpnProtocol + + guard !isWireguardSelected, + !isOpenVPNSelected else { return } + DispatchQueue.main.async { self.presentNonCompliantWifiAlert() self.showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: currentRFC1918VulnerableWifiName) diff --git a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift index f6fe859bb..2639af4f9 100644 --- a/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift +++ b/PIA VPN/Settings/PrivacyFeaturesSettingsViewController.swift @@ -45,7 +45,11 @@ class PrivacyFeaturesSettingsViewController: PIABaseSettingsViewController { preferences = AppPreferences.shared vpnProvider = Client.providers.vpnProvider - if let preferences = preferences, preferences.showLeakProtection { + // Show Leak protection settings when: + // - The feature flag is ON (`showLeakProtection`) + // - Wireguard or OpenVPN is NOT selected + if let preferences = preferences, preferences.showLeakProtection, + !isCurrentProtocolWireguardOrOpenVPN() { sections = PrivacyFeaturesSections.all() } else { sections = PrivacyFeaturesSections.all().filter { $0 != .leakProtection && $0 != .allowAccessOnLocalNetwork } @@ -321,3 +325,21 @@ extension PrivacyFeaturesSettingsViewController: UITableViewDelegate, UITableVie } } + + +extension PrivacyFeaturesSettingsViewController { + func isCurrentProtocolWireguardOrOpenVPN() -> Bool { + + // Selected protocol is OpenVPN + if pendingPreferences.vpnType == PIATunnelProfile.vpnType { + return true + } + + // Selected protocol is Wireguard + if pendingPreferences.vpnType == PIAWGTunnelProfile.vpnType { + return true + } + + return false + } +} From ca2fa8a7a3703d0de46ae8599613fb98f827041d Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 11 Sep 2023 22:11:37 +0200 Subject: [PATCH 122/159] PIA-454: Add Fastlane setup --- Gemfile | 3 +++ fastlane/Appfile | 6 ++++++ fastlane/Fastfile | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 Gemfile create mode 100644 fastlane/Appfile create mode 100644 fastlane/Fastfile diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..7a118b49b --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 000000000..180306309 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,6 @@ +# app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app +# apple_id("[[APPLE_ID]]") # Your Apple email address + + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 000000000..5dbd6ed50 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,22 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:ios) + +platform :ios do + lane :tests do + run_tests(scheme: "PIA VPN") + end +end From 77e46f271162db1f98aba4efdfa46f4d2b91f19d Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 11 Sep 2023 22:12:09 +0200 Subject: [PATCH 123/159] PIA-454: Add CI congifuration --- .github/workflows/vpn-ios.yml | 269 +++------------------------------- 1 file changed, 20 insertions(+), 249 deletions(-) diff --git a/.github/workflows/vpn-ios.yml b/.github/workflows/vpn-ios.yml index dbb57814a..984976cab 100644 --- a/.github/workflows/vpn-ios.yml +++ b/.github/workflows/vpn-ios.yml @@ -1,256 +1,27 @@ -name: pia-mobile/ios/vpn-ios +name: pia_vpn_ios on: - push: + pull_request: workflow_dispatch: concurrency: group: "${{ github.ref }}" cancel-in-progress: true -env: - PIA_STAGING_ENDPOINT: "${{ secrets.PIA_STAGING_ENDPOINT }}" - PIA_CUSTOM_SERVERS: chipotle251:US:chipotle251.londontrustmedia.com:108.61.57.211:8080:500 sharingan:GB:sharingan.londontrustmedia.com:185.195.200.20:8080:500 - PIA_FIREBASE_PLIST: CLIENT_ID599746893663-naub4m5ppoos0tuodrjuk67t0q5saj3m.apps.googleusercontent.comREVERSED_CLIENT_IDcom.googleusercontent.apps.599746893663-naub4m5ppoos0tuodrjuk67t0q5saj3mAPI_KEYAIzaSyD_9Gdzi4WgNfAwl9PPupph1eWnf0zstA4GCM_SENDER_ID599746893663PLIST_VERSION1BUNDLE_IDcom.privateinternetaccess.ios.PIA-VPNPROJECT_IDpia-ios-e7da0STORAGE_BUCKETpia-ios-e7da0.appspot.comIS_ADS_ENABLEDIS_ANALYTICS_ENABLEDIS_APPINVITE_ENABLEDIS_GCM_ENABLEDIS_SIGNIN_ENABLEDGOOGLE_APP_ID1:599746893663:ios:9a64d6b91b33eb1f3088eaDATABASE_URLhttps://pia-ios-e7da0.firebaseio.com - APPCENTER_API_TOKEN: "${{ secrets.APPCENTER_API_TOKEN }}" - APPCENTER_OWNER_NAME: Kape - APPCENTER_APP_NAME: PIA-VPN - APPCENTER_DISTRIBUTE_DESTINATIONS: QA,Developers - APPSTORE_USERNAME: "${{ secrets.APPSTORE_USERNAME }}" - APPSTORE_SPECIFIC_PWD: "${{ secrets.APPSTORE_SPECIFIC_PWD }}" - APPSTORE_PASSWORD: 7Uyz@VouY3pvn!gdU7Zr - APP_IDENTIFIER: "${{ secrets.APP_IDENTIFIER }}" - APPSTORE_CONNECT_TEAM_ID: '609225' - APPSTORE_DEVELOPER_TEAM_ID: "${{ secrets.APPSTORE_DEVELOPER_TEAM_ID }}" - FASTLANE_USER: "${{ secrets.FASTLANE_USER }}" - FASTLANE_PASSWORD: 7Uyz@VouY3pvn!gdU7Zr - MATCH_PASSWORD: "${{ secrets.MATCH_PASSWORD }}" - SLACK_URL: "${{ secrets.SLACK_URL }}" - DELIVER_USER: "${{ secrets.DELIVER_USER }}" jobs: - qa_archive: - runs-on: - - self-hosted - - ios - if: (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads//^release.*$/') - timeout-minutes: 60 - env: - LC_ALL: en_US.UTF-8 - LANG: en_US.UTF-8 - STAGE_BUILD_PATH: build - STAGE_ARTIFACTS_PATH: dist - STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist - STAGE_ARCHIVE_NAME: pia-vpn - SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" - MATCH_TYPE: adhoc - GYM_SCHEME: PIA VPN dev - GYM_EXPORT_METHOD: ad-hoc + build: + runs-on: macos-latest steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 50 - lfs: true - - run: gem install bundler --no-ri --no-rdoc - - run: bundle update - - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" - - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" - - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" - - run: bundle exec fastlane create_archive - - uses: actions/upload-artifact@v2 - if: success() - with: - name: "${{ github.job }}" - retention-days: 7 - path: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.*" - beta_manual_archive: - runs-on: - - self-hosted - - ios - if: github.event_name == 'workflow_dispatch' - timeout-minutes: 60 - env: - LC_ALL: en_US.UTF-8 - LANG: en_US.UTF-8 - STAGE_BUILD_PATH: build - STAGE_ARTIFACTS_PATH: dist - STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist - STAGE_ARCHIVE_NAME: pia-vpn - SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" - MATCH_TYPE: appstore - GYM_SCHEME: PIA VPN - GYM_EXPORT_METHOD: app-store - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 50 - lfs: true - - run: gem install bundler --no-ri --no-rdoc - - run: bundle update - - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" - - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" - - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" - - run: bundle exec fastlane create_beta_archive - - uses: actions/upload-artifact@v2 - if: success() - with: - name: "${{ github.job }}" - retention-days: 7 - path: "$STAGE_TESTFLIGHT_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.*" - manual_qa_archive: - runs-on: - - self-hosted - - ios - if: github.event_name == 'workflow_dispatch' - timeout-minutes: 60 - env: - LC_ALL: en_US.UTF-8 - LANG: en_US.UTF-8 - STAGE_BUILD_PATH: build - STAGE_ARTIFACTS_PATH: dist - STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist - STAGE_ARCHIVE_NAME: pia-vpn - SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" - MATCH_TYPE: adhoc - GYM_SCHEME: PIA VPN dev - GYM_EXPORT_METHOD: ad-hoc - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 50 - lfs: true - - run: gem install bundler --no-ri --no-rdoc - - run: bundle update - - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" - - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" - - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" - - run: bundle exec fastlane create_archive - - uses: actions/upload-artifact@v2 - if: success() - with: - name: "${{ github.job }}" - retention-days: 7 - path: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.*" - qa_deploy: - needs: - - qa_archive - - beta_manual_archive - - manual_qa_archive - runs-on: - - self-hosted - - ios - if: (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads//^release.*$/') - environment: - name: hockey - url: "$HOCKEY_URL" - timeout-minutes: 60 - env: - LC_ALL: en_US.UTF-8 - LANG: en_US.UTF-8 - STAGE_BUILD_PATH: build - STAGE_ARTIFACTS_PATH: dist - STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist - STAGE_ARCHIVE_NAME: pia-vpn - SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" - IPA_OUTPUT_PATH: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" - FL_HOCKEY_IPA: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" - FL_HOCKEY_COMMIT_SHA: "${{ github.sha }}" - FL_HOCKEY_BUILD_SERVER_URL: "${{ github.server_url }}/${{ github.repository }}/-/jobs/${{ github.job }}" - FL_HOCKEY_REPOSITORY_URL: "${{ github.server_url }}/${{ github.repository }}" - FL_HOCKEY_NOTIFY: 'false' - FL_HOCKEY_STRATEGY: replace - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 50 - lfs: true - - uses: actions/download-artifact@v2 - - run: gem install bundler --no-ri --no-rdoc - - run: bundle update - - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" - - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" - - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" - - run: bundle exec fastlane qa_deploy - - uses: actions/upload-artifact@v2 - if: success() - with: - name: "${{ github.job }}" - retention-days: 7 - path: "$SERIALIZED_ARCHIVE_JSON" - beta_manual_deploy: - needs: - - qa_archive - - beta_manual_archive - - manual_qa_archive - runs-on: - - self-hosted - - ios - if: github.event_name == 'workflow_dispatch' - environment: - name: testflight - timeout-minutes: 60 - env: - LC_ALL: en_US.UTF-8 - LANG: en_US.UTF-8 - STAGE_BUILD_PATH: build - STAGE_ARTIFACTS_PATH: dist - STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist - STAGE_ARCHIVE_NAME: pia-vpn - SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" - PILOT_IPA: "$STAGE_TESTFLIGHT_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" - PILOT_DISTRIBUTE_EXTERNAL: 'true' - DEMO_ACCOUNT_REQUIRED: 'true' - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 50 - lfs: true - - uses: actions/download-artifact@v2 - - run: gem install bundler --no-ri --no-rdoc - - run: bundle update - - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" - - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" - - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" - - run: bundle exec fastlane beta_deploy - manual_qa_deploy: - needs: - - qa_archive - - beta_manual_archive - - manual_qa_archive - runs-on: - - self-hosted - - ios - if: github.event_name == 'workflow_dispatch' - environment: - name: hockey - url: "$HOCKEY_URL" - timeout-minutes: 60 - env: - LC_ALL: en_US.UTF-8 - LANG: en_US.UTF-8 - STAGE_BUILD_PATH: build - STAGE_ARTIFACTS_PATH: dist - STAGE_TESTFLIGHT_ARTIFACTS_PATH: apple_dist - STAGE_ARCHIVE_NAME: pia-vpn - SERIALIZED_ARCHIVE_JSON: "$STAGE_ARTIFACTS_PATH/notify.json" - IPA_OUTPUT_PATH: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" - FL_HOCKEY_IPA: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa" - FL_HOCKEY_COMMIT_SHA: "${{ github.sha }}" - FL_HOCKEY_BUILD_SERVER_URL: "${{ github.server_url }}/${{ github.repository }}/-/jobs/${{ github.job }}" - FL_HOCKEY_REPOSITORY_URL: "${{ github.server_url }}/${{ github.repository }}" - FL_HOCKEY_NOTIFY: 'false' - FL_HOCKEY_STRATEGY: replace - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 50 - lfs: true - - uses: actions/download-artifact@v2 - - run: gem install bundler --no-ri --no-rdoc - - run: bundle update - - run: echo "$PIA_STAGING_ENDPOINT" >"Resources/staging.endpoint" - - run: echo "$PIA_CUSTOM_SERVERS" >"Resources/custom.servers" - - run: echo "$PIA_FIREBASE_PLIST" >"Resources/GoogleService-Info.plist" - - run: bundle exec fastlane qa_deploy - - uses: actions/upload-artifact@v2 - if: success() - with: - name: "${{ github.job }}" - retention-days: 7 - path: "$SERIALIZED_ARCHIVE_JSON" + - name: Setup Git credentials + run: | + git config --global url."https://${{ secrets.ORG_GITHUB_USERNAME }}:${{ secrets.ORG_GITHUB_TOKEN }}@github.com/".insteadOf "git@github.com:" + + - uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + + - name: Install Fastlane + run: gem install fastlane + + - name: Run tests + run: bundle exec fastlane tests \ No newline at end of file From 23f1f36deaa86cfa1d4eb651dd0b26ca2595f592 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Tue, 12 Sep 2023 12:13:26 +0200 Subject: [PATCH 124/159] PIA-415: Connection Live Activity and Dynamic Island POC --- PIA VPN.xcodeproj/project.pbxproj | 34 +++++++++- PIA VPN/DashboardViewController.swift | 38 ++++++++++- PIA VPN/Info.plist | 2 + .../ios-widget.imageset/Contents.json | 15 +++++ .../ios-widget.imageset/ios-widget.pdf | Bin 0 -> 9606 bytes PIAWidget/Domain/UI/PIACircleIcon.swift | 41 ++++++++++++ PIAWidget/Domain/UI/PIACircleIndicator.swift | 44 +++++++++++++ PIAWidget/Domain/UI/PIAConnectionView.swift | 62 ++++++++++++++++++ .../Widget/PIAConnectionActivityWidget.swift | 56 ++++++++++++++++ PIAWidget/Domain/Widget/PIAWidget.swift | 3 +- .../Domain/Widget/PIAWidgetAttributes.swift | 15 +++++ PIAWidget/Domain/Widget/PIAWidgetBundle.swift | 14 ++++ 12 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json create mode 100644 PIAWidget/Assets.xcassets/ios-widget.imageset/ios-widget.pdf create mode 100644 PIAWidget/Domain/UI/PIACircleIcon.swift create mode 100644 PIAWidget/Domain/UI/PIACircleIndicator.swift create mode 100644 PIAWidget/Domain/UI/PIAConnectionView.swift create mode 100644 PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift create mode 100644 PIAWidget/Domain/Widget/PIAWidgetAttributes.swift create mode 100644 PIAWidget/Domain/Widget/PIAWidgetBundle.swift diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 11df20e0d..747fadc05 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -145,6 +145,16 @@ 3545E98226AADB2B00B812CC /* ServerSelectingTileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */; }; 3545E98326AADC7C00B812CC /* ServerSelectingTileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */; }; 3545E98426AADC7E00B812CC /* ServerSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3545E97F26AAD60C00B812CC /* ServerSelectionDelegate.swift */; }; + 6924831A2AB045A5002A0407 /* PIAWidgetAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483192AB045A5002A0407 /* PIAWidgetAttributes.swift */; }; + 6924831B2AB045A5002A0407 /* PIAWidgetAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483192AB045A5002A0407 /* PIAWidgetAttributes.swift */; }; + 6924831C2AB045A5002A0407 /* PIAWidgetAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483192AB045A5002A0407 /* PIAWidgetAttributes.swift */; }; + 6924831E2AB04FFD002A0407 /* PIAWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831D2AB04FFD002A0407 /* PIAWidgetBundle.swift */; }; + 692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */; }; + 692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483212AB05F37002A0407 /* PIACircleIcon.swift */; }; + 692483242AB05F67002A0407 /* PIACircleIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483232AB05F67002A0407 /* PIACircleIndicator.swift */; }; + 692483262AB05F85002A0407 /* PIAConnectionActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */; }; + 692483272AB06720002A0407 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */; }; + 692483282AB06721002A0407 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */; }; 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */; }; @@ -549,7 +559,7 @@ 0E257AC41DA45D2F0000D3C3 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; 0E325DA62093277F0020BEDB /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = ""; }; 0E392DA51FE3283C0002160D /* TransientState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransientState.swift; sourceTree = ""; }; - 0E3A35271FD9A960000B0F99 /* DashboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardViewController.swift; sourceTree = ""; }; + 0E3A35271FD9A960000B0F99 /* DashboardViewController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = DashboardViewController.swift; sourceTree = ""; tabWidth = 4; }; 0E3A352B1FD9CDC5000B0F99 /* Theme+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+App.swift"; sourceTree = ""; }; 0E3A35341FD9EBDA000B0F99 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0E3C9A5D20EC004D00B199F9 /* custom.servers */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = custom.servers; sourceTree = ""; }; @@ -662,6 +672,12 @@ 3524670D26B432ED00E3F0AC /* DashboardViewController+ServerSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DashboardViewController+ServerSelection.swift"; sourceTree = ""; }; 3545E97F26AAD60C00B812CC /* ServerSelectionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionDelegate.swift; sourceTree = ""; }; 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectingTileCell.swift; sourceTree = ""; }; + 692483192AB045A5002A0407 /* PIAWidgetAttributes.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAWidgetAttributes.swift; sourceTree = ""; tabWidth = 4; }; + 6924831D2AB04FFD002A0407 /* PIAWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAWidgetBundle.swift; sourceTree = ""; }; + 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAConnectionView.swift; sourceTree = ""; }; + 692483212AB05F37002A0407 /* PIACircleIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIACircleIcon.swift; sourceTree = ""; }; + 692483232AB05F67002A0407 /* PIACircleIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIACircleIndicator.swift; sourceTree = ""; }; + 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAConnectionActivityWidget.swift; sourceTree = ""; }; 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; @@ -1369,6 +1385,9 @@ AAE878A628E473A400557F26 /* PIAWidgetVpnDetailsView.swift */, AAE878A828E4765F00557F26 /* PIAIconView.swift */, AA52C59E28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift */, + 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */, + 692483212AB05F37002A0407 /* PIACircleIcon.swift */, + 692483232AB05F67002A0407 /* PIACircleIndicator.swift */, ); path = UI; sourceTree = ""; @@ -1379,6 +1398,9 @@ 8269A6D9251CB5E0000B4DBF /* PIAWidget.swift */, AAE8789E28E4696300557F26 /* PIAWidgetProvider.swift */, AAE878A028E46C1600557F26 /* PIAWidgetPreview.swift */, + 692483192AB045A5002A0407 /* PIAWidgetAttributes.swift */, + 6924831D2AB04FFD002A0407 /* PIAWidgetBundle.swift */, + 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */, ); path = Widget; sourceTree = ""; @@ -1878,6 +1900,7 @@ files = ( 82A1AD2A24B87CCD0003DD02 /* PIACard.xib in Resources */, 82183D9925014FDC0033023F /* CustomNetworkCollectionViewCell.xib in Resources */, + 692483272AB06720002A0407 /* Assets.xcassets in Resources */, DD172AA02254C39300071CFB /* FavoriteServersTile.xib in Resources */, 0ED66BD020A9918000333B35 /* staging.endpoint in Resources */, DD76292021ECCD510092DF50 /* UsageTileCollectionViewCell.xib in Resources */, @@ -1966,6 +1989,7 @@ DD172A9C2254C36D00071CFB /* FavoriteServersTileCollectionViewCell.xib in Resources */, DD51F8B92372E494009FEED3 /* PIA-RSA-4096.pem in Resources */, 82183D9C25014FDC0033023F /* PIAHeaderCollectionViewCell.xib in Resources */, + 692483282AB06721002A0407 /* Assets.xcassets in Resources */, 291C6398183EBC210039EC03 /* Images.xcassets in Resources */, 82183D9625014FDC0033023F /* NetworkCollectionViewCell.xib in Resources */, 82183D9825014FDC0033023F /* CustomNetworkCollectionViewCell.xib in Resources */, @@ -2156,6 +2180,7 @@ 0E2215CD2008C01D00F5FB4D /* SwiftGen+Assets.swift in Sources */, 8272C62E2657B46100D846A8 /* NetworkSettingsViewController.swift in Sources */, DDFCFA9021E892070081F235 /* RegionTileCollectionViewCell.swift in Sources */, + 6924831B2AB045A5002A0407 /* PIAWidgetAttributes.swift in Sources */, DD9706A5224262BF00630220 /* QuickSettingsTile.swift in Sources */, 82CAB8E9255C0CB000BB08EF /* MessagesTile.swift in Sources */, DD3B504424B7576F0002F4B5 /* Card.swift in Sources */, @@ -2252,6 +2277,7 @@ 0ECF5C082017EBAD0047596C /* ThemeCode.swift in Sources */, 3545E98026AAD60C00B812CC /* ServerSelectionDelegate.swift in Sources */, 0E392DA61FE3283C0002160D /* TransientState.swift in Sources */, + 6924831A2AB045A5002A0407 /* PIAWidgetAttributes.swift in Sources */, 82F41376264E8AC20098FF4B /* SettingOptions.swift in Sources */, 824C531524C04796003DB740 /* ConnectionTileCollectionViewCell.swift in Sources */, 0E3A35351FD9EBDA000B0F99 /* AppDelegate.swift in Sources */, @@ -2366,16 +2392,22 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 692483242AB05F67002A0407 /* PIACircleIndicator.swift in Sources */, 8269A6FE251CBB36000B4DBF /* WidgetInformation.swift in Sources */, 822F97B4251DD53100644EF2 /* WidgetUserDefaultsDatasource.swift in Sources */, 8269A6DA251CB5E0000B4DBF /* PIAWidget.swift in Sources */, + 692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */, AAE878A928E4765F00557F26 /* PIAIconView.swift in Sources */, + 6924831E2AB04FFD002A0407 /* PIAWidgetBundle.swift in Sources */, AAE8789F28E4696300557F26 /* PIAWidgetProvider.swift in Sources */, AAE878A128E46C1600557F26 /* PIAWidgetPreview.swift in Sources */, + 692483262AB05F85002A0407 /* PIAConnectionActivityWidget.swift in Sources */, AAE878A528E4723B00557F26 /* PIACircleVpnButton.swift in Sources */, 82BAACFB25B09C9200B3C733 /* PIAWidget.intentdefinition in Sources */, AAE8789C28E4679500557F26 /* WidgetPersistenceDatasource.swift in Sources */, AAE878A328E46D2B00557F26 /* PIAWidgetView.swift in Sources */, + 6924831C2AB045A5002A0407 /* PIAWidgetAttributes.swift in Sources */, + 692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */, AAE878A728E473A400557F26 /* PIAWidgetVpnDetailsView.swift in Sources */, AA52C59F28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift in Sources */, ); diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 26b38770a..d47245904 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -26,6 +26,7 @@ import SideMenu import SwiftyBeaver import WidgetKit import NetworkExtension +import ActivityKit private let log = SwiftyBeaver.self @@ -68,6 +69,8 @@ class DashboardViewController: AutolayoutViewController { private var currentPageIndex = 0 private var isDisconnecting = false private var isUnauthorized = false + private var activity: Any? = nil + private var isActivityStarted: Bool = false private var currentStatus: VPNStatus = .disconnected private var connectingStatus: DashboardVPNConnectingStatus = .none @@ -185,7 +188,10 @@ class DashboardViewController: AutolayoutViewController { // check account email checkAccountEmail() - + + // Start the live activities (and DynamicIsland) + startLiveActivityIfNeeded() + } // MARK: Menu @@ -390,6 +396,7 @@ class DashboardViewController: AutolayoutViewController { } @IBAction func vpnButtonClicked(_ sender: Any?) { + NSLog(">>> >>> VPN button clicked...") if canConnectVPN() { manuallyConnect() @@ -1133,3 +1140,32 @@ extension DashboardViewController: UICollectionViewDelegate, UICollectionViewDat } } } + + +// MARK: Live Activities + +extension DashboardViewController { + func startLiveActivityIfNeeded() { + if #available(iOS 16.1, *) { + if isActivityStarted { + NSLog("Will stop live activity") + let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", vpnProtocol: "IKEv2") + Task { + guard let act = activity as? Activity else { + NSLog("No conn activity found to stop..") + return + } + await act.end(using: state, dismissalPolicy: .immediate) + } + } else { + NSLog("Will start live activity") + let attributes = PIAConnectionAttributes() + let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Spain", vpnProtocol: "IKEv2") + activity = try? Activity.request(attributes: attributes, contentState: state) + } + + isActivityStarted.toggle() + + } + } +} diff --git a/PIA VPN/Info.plist b/PIA VPN/Info.plist index 53cb1dce7..cbeba7e2c 100644 --- a/PIA VPN/Info.plist +++ b/PIA VPN/Info.plist @@ -51,6 +51,8 @@ We need camera access to scan a code from an gift card NSFaceIDUsageDescription Authenticate to reveal + NSSupportsLiveActivities + NSUserActivityTypes PIAConfigurationIntent diff --git a/PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json b/PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json new file mode 100644 index 000000000..4386364a4 --- /dev/null +++ b/PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "ios-widget.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/PIAWidget/Assets.xcassets/ios-widget.imageset/ios-widget.pdf b/PIAWidget/Assets.xcassets/ios-widget.imageset/ios-widget.pdf new file mode 100644 index 0000000000000000000000000000000000000000..53e1b8df6ebade9b5cb2bdd63ddbb44599abea4f GIT binary patch literal 9606 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 + + init(context: ActivityViewContext) { + self.context = context + } + + var body: some View { + let connected = context.state.connected ? "Connected" : "Disconnected" + return HStack(spacing: 0) { + VStack { + Text("Region") + .italic() + .font(.caption) + .foregroundColor(Color(uiColor: UIColor.lightGray)) + Divider() + .cornerRadius(0.0) + .frame(height: 2.0) + .background( + LinearGradient(colors: [Color.clear, Color("AccentColor")], startPoint: .leading, endPoint: .trailing) + ) + Text("\(context.state.regionName)") + .foregroundColor(Color.white) + .font(.caption) + .bold() + } + VStack { + Text("\(connected)") + .font(.caption) + .frame(maxWidth: .infinity, maxHeight: 30.0) + .foregroundColor(Color.white) + .background(Color("AccentColor")) + .cornerRadius(30.0 / 2.0) + .textCase(.uppercase) + .bold() + } + VStack { + Text("Protocol") + .italic() + .font(.caption) + .foregroundColor(Color(uiColor: UIColor.lightGray)) + Divider() + .cornerRadius(0.0) + .frame(height: 2.0) + .background( + LinearGradient(colors: [Color.clear, Color("AccentColor")], startPoint: .trailing, endPoint: .leading) + ) + Text("\(context.state.vpnProtocol)") + .foregroundColor(Color.white) + .font(.caption) + .bold() + } + } + } +} + diff --git a/PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift b/PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift new file mode 100644 index 000000000..0a8efaeba --- /dev/null +++ b/PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift @@ -0,0 +1,56 @@ + +import WidgetKit +import SwiftUI + +@available(iOSApplicationExtension 16.1, *) +struct PIAConnectionActivityWidget: Widget { + var body: some WidgetConfiguration { + ActivityConfiguration(for: PIAConnectionAttributes.self) { context in + // Create the view that appears on the Lock Screen and as a + // banner on the Home Screen of devices that don't support the + // Dynamic Island. + VStack { + HStack { + PIACircleIcon(size: 25.0, strokeWidth: 1.0) + Text("Private Internet Access") + .foregroundColor(Color.white) + .font(.title3) + } + .padding(.bottom) + PIAConnectionView(context: context) + } + .padding() + + } dynamicIsland: { context in + // Create the views that appear in the Dynamic Island. + DynamicIsland { + // This content will be shown when user expands the island + DynamicIslandExpandedRegion(.leading) { + // Empty + } + + DynamicIslandExpandedRegion(.trailing) { + // Empty + } + + DynamicIslandExpandedRegion(.center) { + // Empty + } + + DynamicIslandExpandedRegion(.bottom) { + PIAConnectionView(context: context) + } + } compactLeading: { + // When the island is wider than the display cutout + PIACircleIcon(size: 25.0, strokeWidth: 1.0) + } compactTrailing: { + // When the island is wider than the display cutout + PIACircleIndicator(size: 25.0, strokeWidth: 1.0, color: Color("AccentColor")) + } minimal: { + // This is used when there are multiple activities + PIACircleIcon(size: 25.0, strokeWidth: 1.0) + } + } + } +} + diff --git a/PIAWidget/Domain/Widget/PIAWidget.swift b/PIAWidget/Domain/Widget/PIAWidget.swift index 35268132b..b92986256 100644 --- a/PIAWidget/Domain/Widget/PIAWidget.swift +++ b/PIAWidget/Domain/Widget/PIAWidget.swift @@ -22,8 +22,9 @@ import WidgetKit import SwiftUI import Intents +import ActivityKit -@main +//@main struct PIAWidget: Widget { let kind: String = "PIAWidget" diff --git a/PIAWidget/Domain/Widget/PIAWidgetAttributes.swift b/PIAWidget/Domain/Widget/PIAWidgetAttributes.swift new file mode 100644 index 000000000..b7a171333 --- /dev/null +++ b/PIAWidget/Domain/Widget/PIAWidgetAttributes.swift @@ -0,0 +1,15 @@ + +import Foundation +import ActivityKit + +public struct PIAConnectionAttributes: ActivityAttributes { + public typealias PIAConnectionStatus = ContentState + + public struct ContentState: Codable, Hashable { + var connected: Bool + var regionName: String + var vpnProtocol: String + } + +} + diff --git a/PIAWidget/Domain/Widget/PIAWidgetBundle.swift b/PIAWidget/Domain/Widget/PIAWidgetBundle.swift new file mode 100644 index 000000000..4c2613b53 --- /dev/null +++ b/PIAWidget/Domain/Widget/PIAWidgetBundle.swift @@ -0,0 +1,14 @@ +import WidgetKit +import SwiftUI + +@main +struct PIAWidgetBundle: WidgetBundle { + var body: some Widget { + PIAWidget() + + if #available(iOS 16.1, *) { + PIAConnectionActivityWidget() + } + } +} + From 350eb979675e23722d111decdfbe45b3b251d129 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Wed, 13 Sep 2023 17:49:29 +0200 Subject: [PATCH 125/159] PIA-490: Set feature flag for Dynamic Island Live Activity --- PIA VPN/AppPreferences.swift | 10 +++++++ PIA VPN/Bootstrapper.swift | 4 +++ PIA VPN/DashboardViewController.swift | 19 ++++++++---- PIA VPN/SettingOptions.swift | 5 +++- .../DevelopmentSettingsViewController.swift | 30 ++++++++++++++----- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index faa821d9d..003c49a30 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -104,6 +104,7 @@ class AppPreferences { static let showNewInitialScreen = "showNewInitialScreen" static let showLeakProtection = "showLeakProtection" static let showLeakProtectionNotifications = "showLeakProtectionNotifications" + static let showDynamicIslandLiveActivity = "showDynamicIslandLiveActivity" // Survey static let userInteractedWithSurvey = "userInteractedWithSurvey" @@ -537,6 +538,15 @@ class AppPreferences { defaults.set(newValue, forKey: Entries.showLeakProtectionNotifications) } } + + var showDynamicIslandLiveActivity: Bool { + get { + return defaults.bool(forKey: Entries.showDynamicIslandLiveActivity) + } + set { + defaults.set(newValue, forKey: Entries.showDynamicIslandLiveActivity) + } + } var checksDipExpirationRequest: Bool { get { diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index 0f5f4581a..eab31cb73 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -59,6 +59,10 @@ class Bootstrapper { // Leak Protection feature flags AppPreferences.shared.showLeakProtection = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtection) AppPreferences.shared.showLeakProtectionNotifications = Client.configuration.featureFlags.contains(Client.FeatureFlags.showLeakProtectionNotifications) + + // DynamicIsland LiveActivity + AppPreferences.shared.showDynamicIslandLiveActivity = Client.configuration.featureFlags.contains(Client.FeatureFlags.showDynamicIslandLiveActivity) + } func bootstrap() { diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index d47245904..74b54cf9d 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -396,8 +396,6 @@ class DashboardViewController: AutolayoutViewController { } @IBAction func vpnButtonClicked(_ sender: Any?) { - NSLog(">>> >>> VPN button clicked...") - if canConnectVPN() { manuallyConnect() } else { @@ -1147,6 +1145,9 @@ extension DashboardViewController: UICollectionViewDelegate, UICollectionViewDat extension DashboardViewController { func startLiveActivityIfNeeded() { if #available(iOS 16.1, *) { + // Start Live Activity only if the Feature Flag for it is enabled + guard AppPreferences.shared.showDynamicIslandLiveActivity else { return } + if isActivityStarted { NSLog("Will stop live activity") let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", vpnProtocol: "IKEv2") @@ -1156,16 +1157,22 @@ extension DashboardViewController { return } await act.end(using: state, dismissalPolicy: .immediate) + startLiveActivity() } } else { - NSLog("Will start live activity") - let attributes = PIAConnectionAttributes() - let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Spain", vpnProtocol: "IKEv2") - activity = try? Activity.request(attributes: attributes, contentState: state) + startLiveActivity() } isActivityStarted.toggle() } } + + @available(iOS 16.1, *) + private func startLiveActivity() { + NSLog("Will start live activity") + let attributes = PIAConnectionAttributes() + let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", vpnProtocol: "IKEv2") + activity = try? Activity.request(attributes: attributes, contentState: state) + } } diff --git a/PIA VPN/SettingOptions.swift b/PIA VPN/SettingOptions.swift index d72c74427..a287a6800 100644 --- a/PIA VPN/SettingOptions.swift +++ b/PIA VPN/SettingOptions.swift @@ -297,6 +297,7 @@ public enum DevelopmentSections: Int, SettingSection, EnumsBuilder { case crash case leakProtectionFlag case leakProtectionNotificationsFlag + case dynamicIslandLiveActivityFlag public func localizedTitleMessage() -> String { switch self { @@ -311,6 +312,7 @@ public enum DevelopmentSections: Int, SettingSection, EnumsBuilder { case .crash: return "Crash the app" case .leakProtectionFlag: return "FF - Leak Protection" case .leakProtectionNotificationsFlag: return "FF - Leak Protection Notifications" + case .dynamicIslandLiveActivityFlag: return "FF - Dynamic Island Live Activity" } } @@ -327,11 +329,12 @@ public enum DevelopmentSections: Int, SettingSection, EnumsBuilder { case .crash: return "" case .leakProtectionFlag: return "" case .leakProtectionNotificationsFlag: return "" + case .dynamicIslandLiveActivityFlag: return "" } } public static func all() -> [Self] { - return [.stagingVersion, .customServers, .publicUsername, .username, .password, .environment, .resolveGoogleAdsDomain, .deleteKeychain, .crash, .leakProtectionFlag, .leakProtectionNotificationsFlag] + return [.stagingVersion, .customServers, .publicUsername, .username, .password, .environment, .resolveGoogleAdsDomain, .deleteKeychain, .crash, .leakProtectionFlag, .leakProtectionNotificationsFlag, .dynamicIslandLiveActivityFlag] } } diff --git a/PIA VPN/Settings/DevelopmentSettingsViewController.swift b/PIA VPN/Settings/DevelopmentSettingsViewController.swift index 3d0793ac5..91f746153 100644 --- a/PIA VPN/Settings/DevelopmentSettingsViewController.swift +++ b/PIA VPN/Settings/DevelopmentSettingsViewController.swift @@ -34,6 +34,7 @@ class DevelopmentSettingsViewController: PIABaseSettingsViewController { private lazy var switchEnvironment = UISwitch() private lazy var switchLeakProtectionFlag = UISwitch() private lazy var switchLeakProtectionNotificationsFlag = UISwitch() + private lazy var switchDynamicIslandLiveActivityFlag = UISwitch() private var controller: OptionsViewController? override func viewDidLoad() { @@ -150,18 +151,26 @@ extension DevelopmentSettingsViewController: UITableViewDelegate, UITableViewDat cell.textLabel?.text = "Crash" cell.detailTextLabel?.text = nil case .leakProtectionFlag: - cell.textLabel?.text = "FF - Leak Protection" - cell.detailTextLabel?.text = nil - cell.accessoryView = switchLeakProtectionFlag - cell.selectionStyle = .none + cell.textLabel?.text = "FF - Leak Protection" + cell.detailTextLabel?.text = nil + cell.accessoryView = switchLeakProtectionFlag + cell.selectionStyle = .none switchLeakProtectionFlag.isOn = AppPreferences.shared.showLeakProtection case .leakProtectionNotificationsFlag: - cell.textLabel?.text = "FF - Leak Protection Notifications" - cell.detailTextLabel?.text = nil - cell.accessoryView = switchLeakProtectionNotificationsFlag - cell.selectionStyle = .none + cell.textLabel?.text = "FF - Leak Protection Notifications" + cell.detailTextLabel?.text = nil + cell.accessoryView = switchLeakProtectionNotificationsFlag + cell.selectionStyle = .none switchLeakProtectionNotificationsFlag.isOn = AppPreferences.shared.showLeakProtectionNotifications + case .dynamicIslandLiveActivityFlag: + cell.textLabel?.text = "FF - Dynamic Island Live Activity" + cell.detailTextLabel?.text = nil + cell.accessoryView = switchDynamicIslandLiveActivityFlag + cell.selectionStyle = .none + switchDynamicIslandLiveActivityFlag.isOn = AppPreferences.shared.showDynamicIslandLiveActivity + } + } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -375,9 +384,14 @@ extension DevelopmentSettingsViewController { AppPreferences.shared.showLeakProtectionNotifications = sender.isOn } + @objc private func toggleDynamicIslandLiveActivityFlag(_ sender: UISwitch) { + AppPreferences.shared.showDynamicIslandLiveActivity = sender.isOn + } + private func addFeatureFlagsTogglesActions() { switchLeakProtectionFlag.addTarget(self, action: #selector(toggleLeakProtectionFlag(_:)), for: .valueChanged) switchLeakProtectionNotificationsFlag.addTarget(self, action: #selector(toggleLeakProtectionNotificationsFlag(_:)), for: .valueChanged) + switchDynamicIslandLiveActivityFlag.addTarget(self, action: #selector(toggleDynamicIslandLiveActivityFlag(_:)), for: .valueChanged) // Additional Feature Flags toggles actions here } From 3609f2f0940ba6477ecb00413eda40e22b405351 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Thu, 14 Sep 2023 13:02:35 +0200 Subject: [PATCH 126/159] PIA-66: Add strings for non IKEV2 protocol alert --- PIA VPN/SwiftGen+Strings.swift | 6 ++++++ PIA VPN/en.lproj/Localizable.strings | 3 +++ 2 files changed, 9 insertions(+) diff --git a/PIA VPN/SwiftGen+Strings.swift b/PIA VPN/SwiftGen+Strings.swift index aa1043fa6..ede3096c3 100644 --- a/PIA VPN/SwiftGen+Strings.swift +++ b/PIA VPN/SwiftGen+Strings.swift @@ -220,6 +220,12 @@ internal enum L10n { internal static let message = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.message", fallback: "To prevent data leaks, tap Disable Now to turn off “Allow access to devices on local network\" and automatically reconnect.") /// Unsecured Wi-Fi detected internal static let title = L10n.tr("Localizable", "dashboard.vpn.leakprotection.alert.title", fallback: "Unsecured Wi-Fi detected") + internal enum IKEV2 { + /// Switch now + internal static let cta1 = L10n.tr("Localizable", "dashboard.vpn.leakprotection.ikev2.alert.cta1", fallback: "Switch Now") + /// To prevent data leaks, tap Switch Now to change to the IKEv2 VPN protocol and automatically reconnect. + internal static let message = L10n.tr("Localizable", "dashboard.vpn.leakprotection.ikev2.alert.message", fallback: "To prevent data leaks, tap Switch Now to change to the IKEv2 VPN protocol and automatically reconnect.") + } } } } diff --git a/PIA VPN/en.lproj/Localizable.strings b/PIA VPN/en.lproj/Localizable.strings index 8d0c2ac64..20880a06a 100644 --- a/PIA VPN/en.lproj/Localizable.strings +++ b/PIA VPN/en.lproj/Localizable.strings @@ -230,6 +230,9 @@ "dashboard.vpn.leakprotection.alert.cta2" = "Learn more"; "dashboard.vpn.leakprotection.alert.cta3" = "Ignore"; +"dashboard.vpn.leakprotection.ikev2.alert.message" = "To prevent data leaks, tap Switch Now to change to the IKEv2 VPN protocol and automatically reconnect."; +"dashboard.vpn.leakprotection.ikev2.alert.cta1" = "Switch Now"; + // VPN PERMISSION "vpn_permission.title" = "PIA"; From 82b6e90fb7e116a167c63e10761570fe9f6715c7 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Thu, 14 Sep 2023 13:03:36 +0200 Subject: [PATCH 127/159] PIA-66: Show non compliant alert for non IKEV2 protocol --- PIA VPN/DashboardViewController.swift | 134 ++++++++++++++++++++------ 1 file changed, 107 insertions(+), 27 deletions(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 26b38770a..eebbd3178 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -656,9 +656,6 @@ class DashboardViewController: AutolayoutViewController { guard Client.preferences.currentRFC1918VulnerableWifi != nil || WifiNetworkMonitor().checkForRFC1918Vulnerability() else { return } - guard Client.preferences.allowLocalDeviceAccess - && Client.preferences.leakProtection else { return } - guard AppPreferences.shared.showLeakProtectionNotifications else { return } let currentRFC1918VulnerableWifiName = Client.preferences.currentRFC1918VulnerableWifi ?? "" @@ -668,7 +665,17 @@ class DashboardViewController: AutolayoutViewController { let isOpenVPNSelected = selectedProtocol == PIATunnelProfile.vpnType.vpnProtocol guard !isWireguardSelected, - !isOpenVPNSelected else { return } + !isOpenVPNSelected else { + DispatchQueue.main.async { + self.presentNonCompliantWireguardWifiAlert() + self.showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: currentRFC1918VulnerableWifiName) + } + + return + } + + guard Client.preferences.allowLocalDeviceAccess + && Client.preferences.leakProtection else { return } DispatchQueue.main.async { self.presentNonCompliantWifiAlert() @@ -676,37 +683,110 @@ class DashboardViewController: AutolayoutViewController { } } - private func presentNonCompliantWifiAlert() { - guard let window = UIApplication.shared.delegate?.window, + //MARK: Non compliant Wifi alert + + private struct WifiAlertAction { + let title: String + let style: UIAlertAction.Style + let action: ((UIAlertAction) -> Void)? + } + + private func showNonCompliantWifiAlert(title: String, message: String, actions: [WifiAlertAction]) { + guard + let window = UIApplication.shared.delegate?.window, let presentedViewController = window?.rootViewController?.presentedViewController ?? window?.rootViewController else { return } - let title = L10n.Dashboard.Vpn.Leakprotection.Alert.title + if let alertController = presentedViewController as? UIAlertController, alertController.title == title { return } - if let alertController = presentedViewController as? UIAlertController, - alertController.title == title { return } + let sheet = Macros.alertController(title, message) - let sheet = Macros.alertController(title, L10n.Dashboard.Vpn.Leakprotection.Alert.message) - sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta1, style: .default, handler: { _ in - Client.preferences.allowLocalDeviceAccess = false - Client.providers.vpnProvider.disconnect { _ in - self.shouldReconnect = true - } - })) + for action in actions { + let alertAction = UIAlertAction(title: action.title, + style: action.style, + handler: action.action) + sheet.addAction(alertAction) + } - // Learn More action - sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta2, style: .default, handler: { _ in - let application = UIApplication.shared - let learnMoreURL = AppConstants.Web.leakProtectionURL - - if application.canOpenURL(learnMoreURL) { - application.open(learnMoreURL) - } - })) + presentedViewController.present(sheet, animated: true, completion: nil) + } + + private func presentNonCompliantWifiAlert() { + let title = L10n.Dashboard.Vpn.Leakprotection.Alert.title + let message = L10n.Dashboard.Vpn.Leakprotection.Alert.message + + var alertActions = [WifiAlertAction]() + let reconnectAction = WifiAlertAction( + title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta1, + style: .default, + action: handleDisconnectAndReconnectAction) + alertActions.append(reconnectAction) + + let learnMoreAction = WifiAlertAction( + title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta2, + style: .default, + action: handleLearnMoreAction) + alertActions.append(learnMoreAction) - sheet.addAction(UIAlertAction(title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta3, style: .default, handler: nil)) + let cancelAction = WifiAlertAction( + title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta3, + style: .cancel, + action: nil) + alertActions.append(cancelAction) + + showNonCompliantWifiAlert(title: title, message: message, actions: alertActions) + } - presentedViewController.present(sheet, animated: true, completion: nil) + private func presentNonCompliantWireguardWifiAlert() { + let title = L10n.Dashboard.Vpn.Leakprotection.Alert.title + let message = L10n.Dashboard.Vpn.Leakprotection.Alert.IKEV2.message + + var alertActions = [WifiAlertAction]() + let reconnectAction = WifiAlertAction( + title: L10n.Dashboard.Vpn.Leakprotection.Alert.IKEV2.cta1, + style: .default, + action: handleSwitchProtocolAction) + alertActions.append(reconnectAction) + + let learnMoreAction = WifiAlertAction( + title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta2, + style: .default, + action: handleLearnMoreAction) + alertActions.append(learnMoreAction) + + let cancelAction = WifiAlertAction( + title: L10n.Dashboard.Vpn.Leakprotection.Alert.cta3, + style: .cancel, + action: nil) + alertActions.append(cancelAction) + + showNonCompliantWifiAlert(title: title, message: message, actions: alertActions) + } + + private func handleDisconnectAndReconnectAction(_ action: UIAlertAction) { + Client.preferences.allowLocalDeviceAccess = false + Client.providers.vpnProvider.disconnect { _ in + self.shouldReconnect = true + } + } + + private func handleLearnMoreAction(_ action: UIAlertAction) { + let application = UIApplication.shared + let learnMoreURL = AppConstants.Web.leakProtectionURL + + if application.canOpenURL(learnMoreURL) { + application.open(learnMoreURL) + } + } + + private func handleSwitchProtocolAction(_ action: UIAlertAction) { + let editable = Client.preferences.editable() + editable.vpnType = IKEv2Profile.vpnType + editable.commit() + + Client.providers.vpnProvider.disconnect { _ in + self.shouldReconnect = true + } } func showNonCompliantWifiLocalNotification(currentRFC1918VulnerableWifiName: String) { From fc296411af823a66e185528b30e11a69846dacf5 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Fri, 15 Sep 2023 12:03:25 +0200 Subject: [PATCH 128/159] PIA-66: Enable leak protection and disable allow local devices --- PIA VPN/DashboardViewController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index eebbd3178..964cdc6c3 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -783,6 +783,8 @@ class DashboardViewController: AutolayoutViewController { let editable = Client.preferences.editable() editable.vpnType = IKEv2Profile.vpnType editable.commit() + Client.preferences.leakProtection = true + Client.preferences.allowLocalDeviceAccess = false Client.providers.vpnProvider.disconnect { _ in self.shouldReconnect = true From 34e88fae3dbbb125a6a4fea974458a7612fae91b Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Thu, 14 Sep 2023 13:39:04 +0200 Subject: [PATCH 129/159] Bump version to 3.23.2 --- PIA VPN.xcodeproj/project.pbxproj | 48 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 11df20e0d..f9ae2c1e0 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -2528,7 +2528,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN Tunnel/PIA VPN Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; @@ -2539,7 +2539,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2562,7 +2562,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; @@ -2574,7 +2574,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.Tunnel"; PRODUCT_NAME = "PIA VPN Tunnel"; @@ -2596,7 +2596,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2616,7 +2616,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2640,7 +2640,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -2660,7 +2660,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN"; PRODUCT_MODULE_NAME = PIA_VPN_dev; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2750,7 +2750,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2762,7 +2762,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2787,7 +2787,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; @@ -2800,7 +2800,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.AdBlocker"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2932,7 +2932,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN/PIA VPN.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEVELOPMENT_TEAM = 5357M5NW9W; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2950,7 +2950,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.privateinternetaccess.ios.PIA-VPN"; @@ -2972,7 +2972,7 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEVELOPMENT_TEAM = 5357M5NW9W; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; ENABLE_BITCODE = NO; @@ -2991,7 +2991,7 @@ "$(inherited)", "$(SRCROOT)", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.privateinternetaccess.ios.PIA-VPN"; @@ -3092,7 +3092,7 @@ CODE_SIGN_ENTITLEMENTS = PIAWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3104,7 +3104,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3134,7 +3134,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; @@ -3147,7 +3147,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.PIAWidget"; @@ -3172,7 +3172,7 @@ CODE_SIGN_ENTITLEMENTS = "PIA VPN WG Tunnel/PIA_VPN_WG_Tunnel.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5357M5NW9W; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3184,7 +3184,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; @@ -3211,7 +3211,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 20035; + CURRENT_PROJECT_VERSION = 20038; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 5357M5NW9W; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5357M5NW9W; @@ -3224,7 +3224,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.23.1; + MARKETING_VERSION = 3.23.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN.WG-Tunnel"; From 6a29f4aca3db79223a04a74c0382e260d2540a90 Mon Sep 17 00:00:00 2001 From: Said Rehouni Date: Mon, 18 Sep 2023 12:51:50 +0200 Subject: [PATCH 130/159] PIA-509: Execute pending actions when switching to IKEV2 --- PIA VPN/DashboardViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 964cdc6c3..e18ff4ccc 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -782,11 +782,13 @@ class DashboardViewController: AutolayoutViewController { private func handleSwitchProtocolAction(_ action: UIAlertAction) { let editable = Client.preferences.editable() editable.vpnType = IKEv2Profile.vpnType + let action = editable.requiredVPNAction() editable.commit() + Client.preferences.leakProtection = true Client.preferences.allowLocalDeviceAccess = false - Client.providers.vpnProvider.disconnect { _ in + action?.execute { _ in self.shouldReconnect = true } } From 0c19c5476eac657f80fe7aa507cec3961d5f5845 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Tue, 19 Sep 2023 09:33:18 +0200 Subject: [PATCH 131/159] PIA-504: Update connection live activity and dynamic islanc UI --- PIA VPN.xcodeproj/project.pbxproj | 18 ++-- PIA VPN/DashboardViewController.swift | 4 +- .../PIA-logo.imageset/Contents.json | 15 ++++ .../PIA-logo.imageset/Group.pdf | Bin 0 -> 7194 bytes .../connect-button.imageset/Contents.json | 15 ++++ .../connect-button.imageset/Group 13.pdf | Bin 0 -> 3654 bytes .../green-checkmark.imageset/Contents.json | 15 ++++ .../green-checkmark.imageset/Icon.pdf | Bin 0 -> 1783 bytes .../ios-widget.imageset/Badge.pdf | Bin 0 -> 14438 bytes .../ios-widget.imageset/Contents.json | 2 +- .../ios-widget.imageset/ios-widget.pdf | Bin 9606 -> 0 bytes PIAWidget/Domain/UI/PIACircleIcon.swift | 39 +++----- PIAWidget/Domain/UI/PIACircleImageView.swift | 24 +++++ PIAWidget/Domain/UI/PIACircleIndicator.swift | 44 ---------- PIAWidget/Domain/UI/PIAConnectionView.swift | 83 ++++++++---------- .../Widget/PIAConnectionActivityWidget.swift | 28 +++--- .../Domain/Widget/PIAWidgetAttributes.swift | 2 + 17 files changed, 148 insertions(+), 141 deletions(-) create mode 100644 PIAWidget/Assets.xcassets/PIA-logo.imageset/Contents.json create mode 100644 PIAWidget/Assets.xcassets/PIA-logo.imageset/Group.pdf create mode 100644 PIAWidget/Assets.xcassets/connect-button.imageset/Contents.json create mode 100644 PIAWidget/Assets.xcassets/connect-button.imageset/Group 13.pdf create mode 100644 PIAWidget/Assets.xcassets/green-checkmark.imageset/Contents.json create mode 100644 PIAWidget/Assets.xcassets/green-checkmark.imageset/Icon.pdf create mode 100644 PIAWidget/Assets.xcassets/ios-widget.imageset/Badge.pdf delete mode 100644 PIAWidget/Assets.xcassets/ios-widget.imageset/ios-widget.pdf create mode 100644 PIAWidget/Domain/UI/PIACircleImageView.swift delete mode 100644 PIAWidget/Domain/UI/PIACircleIndicator.swift diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 747fadc05..6dd5d9f9e 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -151,10 +151,12 @@ 6924831E2AB04FFD002A0407 /* PIAWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831D2AB04FFD002A0407 /* PIAWidgetBundle.swift */; }; 692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */; }; 692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483212AB05F37002A0407 /* PIACircleIcon.swift */; }; - 692483242AB05F67002A0407 /* PIACircleIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483232AB05F67002A0407 /* PIACircleIndicator.swift */; }; 692483262AB05F85002A0407 /* PIAConnectionActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */; }; 692483272AB06720002A0407 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */; }; 692483282AB06721002A0407 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */; }; + 698F4F2B2AB8A2080010B2B0 /* PIAWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8269A6D5251CB5E0000B4DBF /* PIAWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 698F4F2D2AB978BF0010B2B0 /* PIACircleImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698F4F2C2AB978BF0010B2B0 /* PIACircleImageView.swift */; }; + 698F4F2E2AB97BAD0010B2B0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 291C6397183EBC210039EC03 /* Images.xcassets */; }; 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */; }; @@ -541,6 +543,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + 698F4F2B2AB8A2080010B2B0 /* PIAWidgetExtension.appex in Embed App Extensions */, DD1AB0E923F2993600396E74 /* PIA VPN WG Tunnel.appex in Embed App Extensions */, 0EFB6079203D7A2C0095398C /* PIA VPN AdBlocker.appex in Embed App Extensions */, 0EE220741F4EF307002805AE /* PIA VPN Tunnel.appex in Embed App Extensions */, @@ -674,10 +677,10 @@ 3545E98126AADB2B00B812CC /* ServerSelectingTileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectingTileCell.swift; sourceTree = ""; }; 692483192AB045A5002A0407 /* PIAWidgetAttributes.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAWidgetAttributes.swift; sourceTree = ""; tabWidth = 4; }; 6924831D2AB04FFD002A0407 /* PIAWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAWidgetBundle.swift; sourceTree = ""; }; - 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAConnectionView.swift; sourceTree = ""; }; - 692483212AB05F37002A0407 /* PIACircleIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIACircleIcon.swift; sourceTree = ""; }; - 692483232AB05F67002A0407 /* PIACircleIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIACircleIndicator.swift; sourceTree = ""; }; - 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAConnectionActivityWidget.swift; sourceTree = ""; }; + 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionView.swift; sourceTree = ""; tabWidth = 4; }; + 692483212AB05F37002A0407 /* PIACircleIcon.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleIcon.swift; sourceTree = ""; tabWidth = 4; }; + 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionActivityWidget.swift; sourceTree = ""; tabWidth = 4; }; + 698F4F2C2AB978BF0010B2B0 /* PIACircleImageView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleImageView.swift; sourceTree = ""; tabWidth = 4; }; 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; @@ -1387,7 +1390,7 @@ AA52C59E28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift */, 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */, 692483212AB05F37002A0407 /* PIACircleIcon.swift */, - 692483232AB05F67002A0407 /* PIACircleIndicator.swift */, + 698F4F2C2AB978BF0010B2B0 /* PIACircleImageView.swift */, ); path = UI; sourceTree = ""; @@ -2021,6 +2024,7 @@ buildActionMask = 2147483647; files = ( 8269A6DD251CB5E3000B4DBF /* Assets.xcassets in Resources */, + 698F4F2E2AB97BAD0010B2B0 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2392,8 +2396,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 692483242AB05F67002A0407 /* PIACircleIndicator.swift in Sources */, 8269A6FE251CBB36000B4DBF /* WidgetInformation.swift in Sources */, + 698F4F2D2AB978BF0010B2B0 /* PIACircleImageView.swift in Sources */, 822F97B4251DD53100644EF2 /* WidgetUserDefaultsDatasource.swift in Sources */, 8269A6DA251CB5E0000B4DBF /* PIAWidget.swift in Sources */, 692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */, diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 11cc8776b..69043778b 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -1234,7 +1234,7 @@ extension DashboardViewController { if isActivityStarted { NSLog("Will stop live activity") - let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", vpnProtocol: "IKEv2") + let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", regionFlag: "flag-es", vpnProtocol: "IKEv2") Task { guard let act = activity as? Activity else { NSLog("No conn activity found to stop..") @@ -1256,7 +1256,7 @@ extension DashboardViewController { private func startLiveActivity() { NSLog("Will start live activity") let attributes = PIAConnectionAttributes() - let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", vpnProtocol: "IKEv2") + let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", regionFlag: "flag-es", vpnProtocol: "IKEv2") activity = try? Activity.request(attributes: attributes, contentState: state) } } diff --git a/PIAWidget/Assets.xcassets/PIA-logo.imageset/Contents.json b/PIAWidget/Assets.xcassets/PIA-logo.imageset/Contents.json new file mode 100644 index 000000000..02b5d87ae --- /dev/null +++ b/PIAWidget/Assets.xcassets/PIA-logo.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/PIAWidget/Assets.xcassets/PIA-logo.imageset/Group.pdf b/PIAWidget/Assets.xcassets/PIA-logo.imageset/Group.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9936158f29a6f515a1838b9bbb5cf834072fb9be GIT binary patch literal 7194 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 E0b~x300000 literal 0 HcmV?d00001 diff --git a/PIAWidget/Assets.xcassets/connect-button.imageset/Contents.json b/PIAWidget/Assets.xcassets/connect-button.imageset/Contents.json new file mode 100644 index 000000000..84d9e024a --- /dev/null +++ b/PIAWidget/Assets.xcassets/connect-button.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Group 13.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/PIAWidget/Assets.xcassets/connect-button.imageset/Group 13.pdf b/PIAWidget/Assets.xcassets/connect-button.imageset/Group 13.pdf new file mode 100644 index 0000000000000000000000000000000000000000..af5a8894394f174787700f8308a924c5213a3f51 GIT binary patch literal 3654 zcmeHKO>Y}F5WVwP@M0h-5Q?1PClCZ^Y$qt%qK;jlhoA>_y|G=$QX?sa+h5-|%hhsa z8%2O#D;xIe?UFO|_~v6zF3#S)awaL|wAGv6epgz*eytbh5Bv95`{ll`-`w!8TWV{Z zm5+Y&rv9paeJ#rr9j*VNU*GI+H3PiFgKD?m_RD*-x+0Z}sWb{9CtEpVi{r z>6@K$W-wu4Y`l0iT|K>LP4L!o(cQhmwGBDL6_X<+A8_Gai8kw9w^o6TkCuYd6is4l zqu2K{)HzckRweD7@r6=M&4;?gk?c9T)v0ks_6?lww%sPEneE`~xJ$Ja6X~{F_0&-r zJr%PJ>s0fvp)I<$W5edAZ`CKYRv*Cl=dUF@a7kqzx{xi*ri{?ZxfJCt3H$zkvL|_U z>Ogkz!-4yFEWQbCa`9v=Q-~ttQ6rQL#ZqXN6A&Lk?uk-q&mjToLABi`Xs&<>Y*J~v zHAywI4TJb5{~Fq&Ydbb`Zs78QT0DuaL-xHFG)|Hs@pVQCxq|;+#(Tr&hAN4XtzN0-XhRR~!A?CE^r(i-37H&f;4$ybfqT^y($IP(`l@#& zq=vO!JN4YjuV!d-wP~q^RCgLm|DY-f{)m!6K|-GRUrV|a^`|}Znf~d#VYb;h={mvZ z?D;D?C`q|uW)v|p`?-a`vRkw=Jt&=X6F5F<{`uoMJjY%~l%;W{A(o6ti=(&R&oI$|jW%pf2px?rP_ zgkH-Go-`X7VA8OMXRtJbV2i25;^s>c4mxtMTup!}oSip_z!J>Dlaj6QQN#k^EDC00lGX^?`(;=u0%5_W;ZLgw9&KqaNkW?1TK|K%V6Vy&=&yc0;&T{Kq#=J$GRlv0`eCS-`Mtgr;Ba}G^)c?w z-W9%GuQ&Uh{`f229q?KF{MR47UR*41`VsiCzrI_(-8|@z^GRhKZN@19a;sOUBl@t@ zWxv~e*>?R-%iGE2xZUYyz3Ev>hbz6F zpaehVGePWd_KzWgW5-WH?2jR-l!vGG+vVM=-zs_2`|b>Myx48-`-o3zsa`X|jWFKR9=NyCX+ws_(h3h<-7u-g zfG9_6J)wpxCyiHto=10dc?;PiCuGSXv1B~|lq1x%(VfJcDLEY4Wo9+YE}3TQ(AR0^ zdv?nj_UT{CoGO-#zn3|Sv!l#4!(?Y`HKECAm5XzTO}+n9OWJ|sQfTWD1}#sUEoF@@ zgRzsalqFD0&`RTgv>-<*rSk;66^?bO7T4v!j78)1>wDN{P7AtI}c zcHnsF(RdM~Q4lO6a+0ztAXkFhc#d1+r1r34nrnwz#70+Am8b_M6^h{&2v1266!%7! z(#LkT25GtC#I+Dar1jcZNQRn4WMya#lf_lVu)I%A*K_i;a0qAnzrxY<_irwdWz4#z6U6- zs=KfQAHrf;owtv0LwcUJQ@)zn8Y9g^*FJ3O&_Nuh^If=E+w`HKjB%EZ@T%Gn13Ksd zO(t$cX%ZLjmJ8Y{Lv0haLWWZ zwv``$fsRiZAiR5!x-CA?Bt-4Mk5DR7y|)UbA&L7)$6nq?Mr6l+Q!Q6v!{X_^y`XX& tckMcy!0~c%vMow6Z`&TC;Gw|9_40S1-4B0J)utchps~n~j$VJf`~w$qbt?b> literal 0 HcmV?d00001 diff --git a/PIAWidget/Assets.xcassets/ios-widget.imageset/Badge.pdf b/PIAWidget/Assets.xcassets/ios-widget.imageset/Badge.pdf new file mode 100644 index 0000000000000000000000000000000000000000..10ca08c1076dd31668836440fd26f8d376edd72c GIT binary patch literal 14438 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 Cx#T|p literal 0 HcmV?d00001 diff --git a/PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json b/PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json index 4386364a4..c9eb8c559 100644 --- a/PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json +++ b/PIAWidget/Assets.xcassets/ios-widget.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "ios-widget.pdf", + "filename" : "Badge.pdf", "idiom" : "universal" } ], diff --git a/PIAWidget/Assets.xcassets/ios-widget.imageset/ios-widget.pdf b/PIAWidget/Assets.xcassets/ios-widget.imageset/ios-widget.pdf deleted file mode 100644 index 53e1b8df6ebade9b5cb2bdd63ddbb44599abea4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9606 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 - - init(context: ActivityViewContext) { + internal let showProtocol: Bool + + init(context: ActivityViewContext, showProtocol: Bool = false) { self.context = context + self.showProtocol = showProtocol } - + var body: some View { - let connected = context.state.connected ? "Connected" : "Disconnected" - return HStack(spacing: 0) { - VStack { - Text("Region") - .italic() - .font(.caption) - .foregroundColor(Color(uiColor: UIColor.lightGray)) - Divider() - .cornerRadius(0.0) - .frame(height: 2.0) - .background( - LinearGradient(colors: [Color.clear, Color("AccentColor")], startPoint: .leading, endPoint: .trailing) - ) - Text("\(context.state.regionName)") - .foregroundColor(Color.white) - .font(.caption) - .bold() - } - VStack { - Text("\(connected)") - .font(.caption) - .frame(maxWidth: .infinity, maxHeight: 30.0) - .foregroundColor(Color.white) - .background(Color("AccentColor")) - .cornerRadius(30.0 / 2.0) - .textCase(.uppercase) - .bold() - } - VStack { - Text("Protocol") - .italic() - .font(.caption) - .foregroundColor(Color(uiColor: UIColor.lightGray)) - Divider() - .cornerRadius(0.0) - .frame(height: 2.0) - .background( - LinearGradient(colors: [Color.clear, Color("AccentColor")], startPoint: .trailing, endPoint: .leading) - ) - Text("\(context.state.vpnProtocol)") - .foregroundColor(Color.white) - .font(.caption) - .bold() + HStack { + HStack { + PIACircleImageView(size: 24, image: context.state.regionFlag) + VStack(alignment: .leading) { + Text("Region") + .font(.caption) + Text(context.state.regionName) + .font(.caption) + .bold() + } + if showProtocol { + + HStack { + Spacer() + PIACircleImageView(size: 24, image: "green-checkmark") + VStack(alignment: .leading) { + Text("Protocol") + .font(.caption) + Text(context.state.vpnProtocol) + .font(.caption) + .bold() + } + Spacer() + } + } else { + Spacer() + } + + PIACircleImageView(size: 44, image: "connect-button") } } + } } diff --git a/PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift b/PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift index 0a8efaeba..121ee9491 100644 --- a/PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift +++ b/PIAWidget/Domain/Widget/PIAConnectionActivityWidget.swift @@ -10,17 +10,19 @@ struct PIAConnectionActivityWidget: Widget { // banner on the Home Screen of devices that don't support the // Dynamic Island. VStack { - HStack { - PIACircleIcon(size: 25.0, strokeWidth: 1.0) - Text("Private Internet Access") - .foregroundColor(Color.white) - .font(.title3) + HStack(alignment: .bottom, spacing: 4) { + Image("ios-widget") + .resizable() + .frame(width: 16, height: 22) + Image("PIA-logo") + .resizable() + .frame(width: 30, height: 14) } .padding(.bottom) - PIAConnectionView(context: context) + PIAConnectionView(context: context, showProtocol: true) } .padding() - + } dynamicIsland: { context in // Create the views that appear in the Dynamic Island. DynamicIsland { @@ -28,27 +30,27 @@ struct PIAConnectionActivityWidget: Widget { DynamicIslandExpandedRegion(.leading) { // Empty } - + DynamicIslandExpandedRegion(.trailing) { // Empty } - + DynamicIslandExpandedRegion(.center) { // Empty } - + DynamicIslandExpandedRegion(.bottom) { PIAConnectionView(context: context) } } compactLeading: { // When the island is wider than the display cutout - PIACircleIcon(size: 25.0, strokeWidth: 1.0) + PIACircleIcon(size: 28.0) } compactTrailing: { // When the island is wider than the display cutout - PIACircleIndicator(size: 25.0, strokeWidth: 1.0, color: Color("AccentColor")) + PIACircleImageView(size: 24.0, image: "green-checkmark") } minimal: { // This is used when there are multiple activities - PIACircleIcon(size: 25.0, strokeWidth: 1.0) + PIACircleIcon(size: 30.0) } } } diff --git a/PIAWidget/Domain/Widget/PIAWidgetAttributes.swift b/PIAWidget/Domain/Widget/PIAWidgetAttributes.swift index b7a171333..cca8d72ab 100644 --- a/PIAWidget/Domain/Widget/PIAWidgetAttributes.swift +++ b/PIAWidget/Domain/Widget/PIAWidgetAttributes.swift @@ -8,7 +8,9 @@ public struct PIAConnectionAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { var connected: Bool var regionName: String + var regionFlag: String var vpnProtocol: String + } } From d45d936541e66eb87866bf98dff6c3f0c03a08ee Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Tue, 19 Sep 2023 22:43:13 +0200 Subject: [PATCH 132/159] PIA-417: Hook Connection state to Live Activity and Dynamic Island --- PIA VPN.xcodeproj/project.pbxproj | 16 ++-- PIA VPN/AppDelegate.swift | 21 ++++- PIA VPN/Bootstrapper.swift | 2 +- PIA VPN/DashboardViewController.swift | 64 ++++++------- .../Contents.json | 0 .../Group 13.pdf | Bin .../connecting-button.imageset/Contents.json | 15 ++++ .../State=Connecting.pdf | Bin 0 -> 1837808 bytes .../Contents.json | 15 ++++ .../State=Disconnected.pdf | Bin 0 -> 3654 bytes .../orange-cross.imageset/Contents.json | 15 ++++ .../orange-cross.imageset/Icon (1).pdf | Bin 0 -> 2044 bytes PIAWidget/Domain/UI/PIACircleImageView.swift | 6 +- PIAWidget/Domain/UI/PIAConnectionView.swift | 16 +++- .../Widget/PIAConnectionActivityWidget.swift | 46 +++++++--- .../PIAConnectionLiveActivityManager.swift | 85 ++++++++++++++++++ 16 files changed, 241 insertions(+), 60 deletions(-) rename PIAWidget/Assets.xcassets/{connect-button.imageset => connected-button.imageset}/Contents.json (100%) rename PIAWidget/Assets.xcassets/{connect-button.imageset => connected-button.imageset}/Group 13.pdf (100%) create mode 100644 PIAWidget/Assets.xcassets/connecting-button.imageset/Contents.json create mode 100644 PIAWidget/Assets.xcassets/connecting-button.imageset/State=Connecting.pdf create mode 100644 PIAWidget/Assets.xcassets/disconnected-button.imageset/Contents.json create mode 100644 PIAWidget/Assets.xcassets/disconnected-button.imageset/State=Disconnected.pdf create mode 100644 PIAWidget/Assets.xcassets/orange-cross.imageset/Contents.json create mode 100644 PIAWidget/Assets.xcassets/orange-cross.imageset/Icon (1).pdf create mode 100644 PIAWidget/Domain/Widget/PIAConnectionLiveActivityManager.swift diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 6dd5d9f9e..a8c81c977 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -152,11 +152,12 @@ 692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */; }; 692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483212AB05F37002A0407 /* PIACircleIcon.swift */; }; 692483262AB05F85002A0407 /* PIAConnectionActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */; }; - 692483272AB06720002A0407 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */; }; - 692483282AB06721002A0407 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8269A6DC251CB5E3000B4DBF /* Assets.xcassets */; }; 698F4F2B2AB8A2080010B2B0 /* PIAWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8269A6D5251CB5E0000B4DBF /* PIAWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 698F4F2D2AB978BF0010B2B0 /* PIACircleImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698F4F2C2AB978BF0010B2B0 /* PIACircleImageView.swift */; }; 698F4F2E2AB97BAD0010B2B0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 291C6397183EBC210039EC03 /* Images.xcassets */; }; + 698F4F302ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698F4F2F2ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift */; }; + 698F4F312ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698F4F2F2ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift */; }; + 698F4F322ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698F4F2F2ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift */; }; 7EB8D11F27CE2B020030B060 /* PIAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */; }; 7EB8D12127CE2B5D0030B060 /* PIALaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */; }; 7EB8D12327CE7D4C0030B060 /* PIALoginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB8D12227CE7D4C0030B060 /* PIALoginTests.swift */; }; @@ -564,7 +565,7 @@ 0E392DA51FE3283C0002160D /* TransientState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransientState.swift; sourceTree = ""; }; 0E3A35271FD9A960000B0F99 /* DashboardViewController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = DashboardViewController.swift; sourceTree = ""; tabWidth = 4; }; 0E3A352B1FD9CDC5000B0F99 /* Theme+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+App.swift"; sourceTree = ""; }; - 0E3A35341FD9EBDA000B0F99 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0E3A35341FD9EBDA000B0F99 /* AppDelegate.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; tabWidth = 4; }; 0E3C9A5D20EC004D00B199F9 /* custom.servers */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = custom.servers; sourceTree = ""; }; 0E441E252055AEDF007528D5 /* ThemeStrategy+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThemeStrategy+App.swift"; sourceTree = ""; }; 0E492C661FE60907007F23DF /* Flags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Flags.swift; sourceTree = ""; }; @@ -622,7 +623,7 @@ 0E9452AA1FDB5EF600891948 /* UINavigationItem+Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Shortcuts.swift"; sourceTree = ""; }; 0E9452AD1FDB5F7A00891948 /* PIAPageControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAPageControl.swift; sourceTree = ""; }; 0E9785851DA82FF000711A24 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; - 0E98BB6D1FD5BC6200B41D6B /* Bootstrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bootstrapper.swift; sourceTree = ""; }; + 0E98BB6D1FD5BC6200B41D6B /* Bootstrapper.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = Bootstrapper.swift; sourceTree = ""; tabWidth = 4; }; 0E9AEA6120683FDF00B6E59A /* AboutComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutComponent.swift; sourceTree = ""; }; 0EA660071FEC7A9500CB2B0D /* PIATunnelProvider+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PIATunnelProvider+UI.swift"; sourceTree = ""; }; 0EB0A849204F0CE2008BCF1D /* DataCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataCounter.h; sourceTree = ""; }; @@ -681,6 +682,7 @@ 692483212AB05F37002A0407 /* PIACircleIcon.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleIcon.swift; sourceTree = ""; tabWidth = 4; }; 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionActivityWidget.swift; sourceTree = ""; tabWidth = 4; }; 698F4F2C2AB978BF0010B2B0 /* PIACircleImageView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleImageView.swift; sourceTree = ""; tabWidth = 4; }; + 698F4F2F2ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionLiveActivityManager.swift; sourceTree = ""; tabWidth = 4; }; 7EB8D11327CCF4C20030B060 /* PIA VPN UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 7EB8D11E27CE2B020030B060 /* PIAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAUITests.swift; sourceTree = ""; }; 7EB8D12027CE2B5D0030B060 /* PIALaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIALaunchTests.swift; sourceTree = ""; }; @@ -1404,6 +1406,7 @@ 692483192AB045A5002A0407 /* PIAWidgetAttributes.swift */, 6924831D2AB04FFD002A0407 /* PIAWidgetBundle.swift */, 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */, + 698F4F2F2ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift */, ); path = Widget; sourceTree = ""; @@ -1903,7 +1906,6 @@ files = ( 82A1AD2A24B87CCD0003DD02 /* PIACard.xib in Resources */, 82183D9925014FDC0033023F /* CustomNetworkCollectionViewCell.xib in Resources */, - 692483272AB06720002A0407 /* Assets.xcassets in Resources */, DD172AA02254C39300071CFB /* FavoriteServersTile.xib in Resources */, 0ED66BD020A9918000333B35 /* staging.endpoint in Resources */, DD76292021ECCD510092DF50 /* UsageTileCollectionViewCell.xib in Resources */, @@ -1992,7 +1994,6 @@ DD172A9C2254C36D00071CFB /* FavoriteServersTileCollectionViewCell.xib in Resources */, DD51F8B92372E494009FEED3 /* PIA-RSA-4096.pem in Resources */, 82183D9C25014FDC0033023F /* PIAHeaderCollectionViewCell.xib in Resources */, - 692483282AB06721002A0407 /* Assets.xcassets in Resources */, 291C6398183EBC210039EC03 /* Images.xcassets in Resources */, 82183D9625014FDC0033023F /* NetworkCollectionViewCell.xib in Resources */, 82183D9825014FDC0033023F /* CustomNetworkCollectionViewCell.xib in Resources */, @@ -2105,6 +2106,7 @@ DD76292321ECCD5C0092DF50 /* UsageTile.swift in Sources */, DDD65314247E66AD00F0A897 /* CoordinatesFinder.swift in Sources */, DDD271DF21D616AA00B6D20F /* Server+Favorite.swift in Sources */, + 698F4F312ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift in Sources */, DD125DC621E77046004ECCB6 /* QuickConnectTile.swift in Sources */, 82A1AD2724B86AF60003DD02 /* PIACardsViewController.swift in Sources */, 829EB508253598BD003E74DD /* DedicatedIpViewController.swift in Sources */, @@ -2366,6 +2368,7 @@ 8272C62726540B2100D846A8 /* ProtocolSettingsViewController.swift in Sources */, 82CAB8B0255B050000BB08EF /* MessagesCommands.swift in Sources */, DDE432DF2498EADB0095B197 /* Array+Group.swift in Sources */, + 698F4F302ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift in Sources */, DDB6B95321C95CD400DE8C5F /* EnumsBuilder.swift in Sources */, 3545E98226AADB2B00B812CC /* ServerSelectingTileCell.swift in Sources */, 829EB5322535AD27003E74DD /* DedicatedIpEmptyHeaderViewCell.swift in Sources */, @@ -2411,6 +2414,7 @@ AAE8789C28E4679500557F26 /* WidgetPersistenceDatasource.swift in Sources */, AAE878A328E46D2B00557F26 /* PIAWidgetView.swift in Sources */, 6924831C2AB045A5002A0407 /* PIAWidgetAttributes.swift in Sources */, + 698F4F322ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift in Sources */, 692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */, AAE878A728E473A400557F26 /* PIAWidgetVpnDetailsView.swift in Sources */, AA52C59F28E5ECD400D025AF /* PIAWidgetVpnDetaislRow.swift in Sources */, diff --git a/PIA VPN/AppDelegate.swift b/PIA VPN/AppDelegate.swift index 350977a1a..c921da89c 100644 --- a/PIA VPN/AppDelegate.swift +++ b/PIA VPN/AppDelegate.swift @@ -42,6 +42,7 @@ class AppDelegate: NSObject, UIApplicationDelegate { var window: UIWindow? private var hotspotHelper: PIAHotspotHelper! + private (set) var liveActivityManager: PIAConnectionLiveActivityManagerType? deinit { NotificationCenter.default.removeObserver(self) @@ -55,12 +56,27 @@ class AppDelegate: NSObject, UIApplicationDelegate { application.shortcutItems = [] hotspotHelper = PIAHotspotHelper() _ = hotspotHelper.configureHotspotHelper() - + + instantiateLiveActivityManagerIfNeeded() return true } + + private func instantiateLiveActivityManagerIfNeeded() { + if #available(iOS 16.2, *) { + // Only instantiates the LiveActivities if the Feature Flag for it is enabled + guard AppPreferences.shared.showDynamicIslandLiveActivity else { + liveActivityManager = nil + return + } + + liveActivityManager = PIAConnectionLiveActivityManager.shared + } + } func applicationWillTerminate(_ application: UIApplication) { Bootstrapper.shared.dispose() + + liveActivityManager?.endLiveActivities() } // MARK: Orientations @@ -200,6 +216,9 @@ class AppDelegate: NSObject, UIApplicationDelegate { application.applicationIconBadgeNumber = 0 // Remove the Non compliant Wifi local notification as the app is in foreground now Macros.removeLocalNotification(NotificationCategory.nonCompliantWifi) + + instantiateLiveActivityManagerIfNeeded() + } private func refreshShortcutItems(in application: UIApplication) { diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index eab31cb73..0ef950e81 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -35,7 +35,7 @@ class Bootstrapper { } private var isSimulator: Bool { - #if arch(i386) || arch(x86_64) + #if targetEnvironment(simulator) return true #else return false diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 69043778b..7642d5f20 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -69,10 +69,14 @@ class DashboardViewController: AutolayoutViewController { private var currentPageIndex = 0 private var isDisconnecting = false private var isUnauthorized = false - private var activity: Any? = nil - private var isActivityStarted: Bool = false - private var currentStatus: VPNStatus = .disconnected + private var currentStatus: VPNStatus = .disconnected { + didSet { + if #available(iOS 16.2, *) { + startConnectionLiveActivityIfNeeded() + } + } + } private var connectingStatus: DashboardVPNConnectingStatus = .none private var tileModeStatus: TileStatus = .normal { @@ -188,10 +192,7 @@ class DashboardViewController: AutolayoutViewController { // check account email checkAccountEmail() - - // Start the live activities (and DynamicIsland) - startLiveActivityIfNeeded() - + } // MARK: Menu @@ -867,6 +868,10 @@ class DashboardViewController: AutolayoutViewController { guard Client.providers.accountProvider.isLoggedIn else { return } + + if #available(iOS 16.2, *) { + startConnectionLiveActivityIfNeeded() + } currentStatus = Client.providers.vpnProvider.vpnStatus @@ -1227,36 +1232,23 @@ extension DashboardViewController: UICollectionViewDelegate, UICollectionViewDat // MARK: Live Activities extension DashboardViewController { - func startLiveActivityIfNeeded() { - if #available(iOS 16.1, *) { - // Start Live Activity only if the Feature Flag for it is enabled - guard AppPreferences.shared.showDynamicIslandLiveActivity else { return } - - if isActivityStarted { - NSLog("Will stop live activity") - let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", regionFlag: "flag-es", vpnProtocol: "IKEv2") - Task { - guard let act = activity as? Activity else { - NSLog("No conn activity found to stop..") - return - } - await act.end(using: state, dismissalPolicy: .immediate) - startLiveActivity() - } - } else { - startLiveActivity() - } - - isActivityStarted.toggle() - - } + @available(iOS 16.2, *) + private func makeLiveActivityStateForCurrentConnection() -> PIAConnectionAttributes.ContentState { + let vpnProvider = Client.providers.vpnProvider + let currentServer = Client.preferences.displayedServer + + let vpnProtocol = vpnProvider.currentVPNType.vpnProtocol + + let state = PIAConnectionAttributes.ContentState(connected: vpnProvider.isVPNConnected, regionName: currentServer.name, regionFlag: "flag-\(currentServer.country.lowercased())", vpnProtocol: vpnProtocol) + return state } + - @available(iOS 16.1, *) - private func startLiveActivity() { - NSLog("Will start live activity") - let attributes = PIAConnectionAttributes() - let state = PIAConnectionAttributes.ContentState(connected: true, regionName: "Barcelona", regionFlag: "flag-es", vpnProtocol: "IKEv2") - activity = try? Activity.request(attributes: attributes, contentState: state) + @available(iOS 16.2, *) + private func startConnectionLiveActivityIfNeeded() { + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, + let liveActivityManager = appDelegate.liveActivityManager else { return } + let connState = makeLiveActivityStateForCurrentConnection() + liveActivityManager.startLiveActivity(with: connState) } } diff --git a/PIAWidget/Assets.xcassets/connect-button.imageset/Contents.json b/PIAWidget/Assets.xcassets/connected-button.imageset/Contents.json similarity index 100% rename from PIAWidget/Assets.xcassets/connect-button.imageset/Contents.json rename to PIAWidget/Assets.xcassets/connected-button.imageset/Contents.json diff --git a/PIAWidget/Assets.xcassets/connect-button.imageset/Group 13.pdf b/PIAWidget/Assets.xcassets/connected-button.imageset/Group 13.pdf similarity index 100% rename from PIAWidget/Assets.xcassets/connect-button.imageset/Group 13.pdf rename to PIAWidget/Assets.xcassets/connected-button.imageset/Group 13.pdf diff --git a/PIAWidget/Assets.xcassets/connecting-button.imageset/Contents.json b/PIAWidget/Assets.xcassets/connecting-button.imageset/Contents.json new file mode 100644 index 000000000..065a20d84 --- /dev/null +++ b/PIAWidget/Assets.xcassets/connecting-button.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "State=Connecting.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/PIAWidget/Assets.xcassets/connecting-button.imageset/State=Connecting.pdf b/PIAWidget/Assets.xcassets/connecting-button.imageset/State=Connecting.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c80d3069a183cbaa2eff843109b6e092dc0e2233 GIT binary patch literal 1837808 zcma&PbySw!y1p%TcPDndc6WDox6&O?mmnZ5As~W)ASqI!2uKP@2qGm2BA|kxh}iA- z-q*XId!P3nzdyb;#v0=s$2!NneAnL7dot&tYy8f&>!;0{zMyN@S*kNt4Nhx!RaITO zRCUHyHGK_JP1V`|!rlL(fB!XOje)L#@gBodXVg?@tX1P{ol)DpVfFv~XREbLO?Ijo zuh%`Lp}O#Y*dEPOXSMV-_KFPEROfPgYYYsG|NYNSnX0MInf-tNBRxX{eKmbkcAd3q zXAI7&sUA_CHgo!%dGqEiTBJHl8U7z==Jc8LuYXJb6aN2@WB>jtmM`yWVrr~*O0R1h z#oxbwnSa;EwrO*&&1K8~M^-tvbgIVxaBdc)Zels+TYJ-3j``i|0G2~Gn<7=tRpDgy z-t1g6%-610E7vi@{A^(=%ORT&e(0~{oV@p>1oP79Y3d=FO3$L*pb5V~WEQf5>!|$h(bMmIS>|86%7TN4vE6k}!*|}Djf3^;0 z*N1#zfyECc=VX(?RV>FG5uVI)%uTE2vmCPZ_)>N*C!f+}=UQX-{>9F<#$4{aSGm45 zWSjou*}0s&Bc7dWgV}EDcjY=Zn6qjsSq|B@EpLyKbMo>o^H`2qbafERG2foa&b7t- zKBJQDL$;fGh@H#H8qMrnJIn!Q>|8s{&-=4;?I7C^4K-BG<>dX#cC#F_^W(WJ$DFS= zkmZmaI)8Uma!y|Bz|M8Rd~P&5*8y|lZFa5$=C7OBxekyWXFqdL&gEo%BLkLW4(qUs z<(OZ2&S5#^i=(FvP;yQ_n#Rt(i23q9cJ4*Y#f|LTi;$gq$#RtIbMlrxH&~9@Dv+J) zggJc?JJ$*G?|gQy6J+N_r?QpnbF%5@FqUJEvax45=C>pCSq|A{V)RZW=j1c%*tss4 zeJa?wE|{O{v2$G@yAD)g=W_BcH+HToW{1fSl)oNV%-KnyEQfrl{VqEt=j0Xj?O2Xk zB6`Jg%yB)~xtB11xW>-C1leu+Ja#T8Yvs08uJ499@YqY1W3Fkh6YXm#j9rL4A>|A%q9$ib>xtzRSd%1Fb56ou2*|{E=lU&%j9+-?|u3-KfHj?F#y_PJebMU`sU=>d07YUVpFU&FOhgpuf z`3F1K3$piQM|LhJtBqmjdSmvxBT%mIjk#*`A(lhFI;eVyl5_GN;}I;!e6hn{mSfJn z!p^-4*{9=lb}lEcN@wT#V3zG?=lWp2+sMxK!Td?i&h>%pJF_o4my>mZ*tx!#gBQmr z|G9iI*A-l1Ib^?)r};|G$%noiWI1Lx+r=!${CFfg*AMcw?lJ6KPTshloqG-Q`KRpM zYnW5?*}2y+|7;tr{PkRe>_6Y#RmnNocnXi@m~SQ@U^(W--HTWbIbd8pJC~DBirKjV zn7wO@hr#uW=1EL zL%um7y-~?I`SbyH?oG^B->`FUVy;lIb8kY97|_pKxjrZF3}WX-V76a!lI>%D@Mty5 zAxE}5Gfv4ldHGj%ZX{;0?Q6D=`OYYIZY1W`TkPCO$WhZauyZ+Cv(ihseiY{G1~Qgo zu4#9I<&dL?c|1{aPCoF@Se9dUN%3Mi=7K%!+-S%#T^iWAoV-rL&W*ur){C7RgZW+n zJ2wXNw*~Cn7|6Hg#%b|4EEvIpjNwcX%o}C-Yvi zbMIh|793;yn47z?bMHWoo8-&R<>a$-*tv0-eY1Nj*N?+odE^7jA>SR?QljLXy!-qe zmSc7tbeZLtbHdrVcOl1jSi#QaD*OjA_a~ zCvP~&&b^1({0%$z9_C~PJNF*uAN@D8>qAbO7cy1JIoW7wPnKg2FSKGg=GSM6SPnUP z>^F8UC!erq=O$zJQf22RV=jxeRIZ;4Ii>Fgb}lDxtJ$Z7D7!^!Y=lZN@)b@rb(4Kuvmq(M#}{E(f? z$?$fQjv3x=(lO^*Z&d#GmJazoyyI~4>ZtC@{(a2wc5@$de7QQ?$NX8BoqHd0#;i6e z%04ISUb@C|%psG`vm7(L-DE({9JO#@7cLokRQN14kyFg%>&HvcJlyp(@H<(e?Jc(XOAy2S8`5%$MzxTcmBrC|9QUx0?dYVIA1H z1(@OOrU3Gz(No#EoP0F(tMb?X2s6ChJi=VuSkLw$7xt3PQF2b+(p$lD%vRUgxrLb1 z7wNNo%)j&5xrLA)FFG|_+2>@_Pwd>sn4_$}uzk$%cJml=(ZuLi%04HbS;x*T!t7HK z%Jwlo)n(@vK`tJsqNnV0@-8=aZZT$u$+Or#W_Y_PhFsEq=VxV~lUKZA=ayiGx0@2o zaXpW+>tKern-a*S)912tIaw=*om+|--fl`U*L+~-mO?IrcN|VWIGCMVhS~L|uJYGk zh8f;&${;`KT6RR)=j8QTAuPvi_UjYNF()~*bDv=TKK=#Uhg?24;fRuRvf;L1mSetA z!_F?`Tbr|U zpJL7!5XAN|!`sbM$dyZ$X)F7j%zwgW4kxRPX6II6_Pfo_ zt-=g%H&u|I4XS>u{PlA(yxly*e6a&N_Zj9~&sugJ$kp(U!^x}C3zhw9%;KVa=W??8pL}J%9<#qI zJGUNl^#o_O54mATJUf?@_if*&>^ESBx0?pc4+ZSp2FR~FcV*{t@|vs9lRdCEQ~ z!`sbU%#mIRY#%ec-Mod|JRyCLvd_t<_p@`GF<*VNn(bq*P_T2GA-@~YucNZh$vcDC zx$iLBFV1EAnBnc_9psjFXV|%%y!vU6K7!`n>@cciBGXdjag+5179#kg|QqALrz#b+7eaN5S z9fy;T4J=poKmBiR!?6G1|Hpy%c(3^TKA-V|I1c=hx|xa}Lp~6P`_glQX&n@w)(j*L z_oe4z{{4OUo$g}d;Ju$~P9LUt?RK2u9PfQ{_alnBWOL$hUut&tz$`^oLm%Skz5Dui zR6OZDk2u_ynmv8HLGgIeA%=6j_kjzBEAmduh{JuU`7X1gicC9y;&5MTZvSw$A|*D9 zIC$^o*#nIg@z3@$oa4Q>JGD^}+fhUuz4zretQ8S6t`dj)((|G(#}pw4(}<(@etYFr zg}>P=;^4iXe{W}^@V?Q7;T-S%pR!E~_u|XM(Rk^0iQmeaN?G*dne-a1p z-Rk4sJjKq`Q4HsJ@6&z#6q_5@5QqEH3tEk}igkT8h{Jv9g}}LY6f2h45eM)6LXG^3 zV$s+jF30N>6Q5fV2k+fFKQvA;rt=Mk zbG-M?AHOPw&w4~0z4x`t^A!URe{S4=L@7%r*9gha@lDpN$6G!jeqt#jN)b$y0xG!}qUaBFtouk8Wj`!ZvDp+oD)QLEH z@0%XW9EuiKF*^(?0}`9k9*#L;`-dZk1@+v@~z z^xiML3X@OGI8PkBcbEG!n&cCjgBgzAyUQPOs(kdo`NYwCUzFKKK5WHd;^@8edX>os zXv&GheW`2I;eqmA&h#Bb@7?vy^*8b^2@jb5CG_4WwEQD)_u>t4xG%kQda;V^Pj~tb za=iDe7Eff~=J`+`?n^I~KN=+aa6Fyi9PhpVkhik87wUSi0+ zR#sC%`^ycycel*%t7VmKlbAlodskUISXTN^EphbTmpUGlJ=)iSIC}4bifOVOIeiDw zdw0JzuAS_D@OGw;-n;ud{WY?rM^(hpd!HOXL>BkG4RQ3|)!H4B#f%wG9KCnH?bBu9 zTQ(5~@7<&Fvc4?HRFB~t?|tBlwKBh}&cxAs-#yJjb|s7U*JbqH9fXHvms-|RAH4U= z+4pD2E)LdYILCW$-@{O5v&x=0dhaU^u9KbDrtcufdl&m#%4Dvk)Ccd~^LFzQnc&_T zhNJiH*}A|>X7uU;arEA&nj6Vq-VG;jmzoQgQXjncfEy-`Qt7pm3`g%hpg#GGl%H!s9KH83T^30VKZX#8`_k*j z_K2i9!ygg{@BRAatMO8`bw?PE-uw09*B7NH^cBS6zBI7c95v}7j{xH6y>FHUO82B@ z6G!jeGFvR&*7%k@MSUPG} z9reL`5BAasmJT`6k>TjQ2bV=RNc&k#A&%aApI`T+J#OwGj^6vW4KC77WhTVozBI(z zd8t(8uL^PS-b3zJ^_BdZMBhR5-b4OQcrE#|YctaiMelvFQIh1nSf4n0@4Up{lBNI` z;^4i9Mt58;dHL`T!_j*WeY>llr1~@MFOK&<(W_BXF;a{Aa9_3e=WFEql!LIo`X<`<0R~_0PoNzBGK*)3A&%a=rbu71#P|hq@ZO^W?ng-Gc^NYtz4xf+JuM_N zGTe!y_dfJshGcRxeFvk_d*2suMKW&SbLzu=X|&Tj1Ifsh?HG>Ud-TJFLnVW?CK5;Q zy_0#1q^~pWFOK)VIzLm=J>eneX?Hs;wgOxW6^sb zF=n{9x}7@p(R)9r+bS-fYD*lv_gL4s2jZfA5e!G~J@!%CPU44h+Fu;+z3bKu;;i6P z)JN}qoyQ1qYM~`@^xn_aeh?>o4sE^*ee)o~$fUDPugZF+XbpI#u)hzlBqW6C1rJs-3qotYYbG-LaZ%oB5L;4U$ z@BPU9O=A1i^N54@9_Ma0N^GS~`wPAIxW{=X#OGXJGyS{hz4sX4D;6hpCyw6x#*;j; zY5h#%;Jx2He`B-Qpx0i8qxXI{`HQMpYXR*q^xp6OSdl9}bE*^d(R-g~>nA>D`wwyS z-VICn;sdwnJBZ$Ue7Ncs@vbUUrjOoxe1o=~cx#8t#L;^n6PqVqKYapm^xlvCy(V6H zU<+~Z-V;1G3&e}h8895Z_kU?%uEf!M?^XR(JiRE6IC}3}Cg+PM{j4Mo-g}~z zslRyaIND$6y(gxn2*o3|IWm3p-V=X!xho#Tk0FlU`@%in#C?3rh@y3y$>*6DsulVq&|A@JMT$F&J(S zOW{l6@Q#z-*88_e;ZHxea=iCt^&uirt_StOdrue5l8H<{CNdno_w-ol9+CcV`VOM^ zp5Bt>D$-ovp8D{PbN`<{TB6eilZd1Du6`s`boBC8;^4jCzZN7H?N6osh2H!9s@A=t zosEB)eg=B)gO^SbZRt0bIC}4UEK5Y|mTn*p?>HHbg<+x!@Tm_oG3(TO;` z<79SFOA*b98%Z4AaWYp%xrwILtR@cLd#2<^sc2Fc`VOM^o*B39hG@)eJEo7`d*%nH zbE4r#B8kI0PS&)isiJ`v^d01Q?^@$kM7?jGrapM@S=SB9L|x0Q7>?e1*7JmLQTxAP z#Ni$1!LSZy!oQOWh{HS1gZ(?wh2M95AP(O91LrGkg&!sKb89v@@DCood?IWP2xR&k z@4fSko5F^NImF=|Cwq;kSy=m-zJuA|y=TiarU|P?^=10#y=N!%Y9}n)IG;Fp@7bRZ zl?w|E={v~r-e+Eq5axO|QXk%Na&+Du6lUD-!Ep56bAlI57bd@*MI7F7a_TJF3*!gu zBaYtth=R4kTgyen;TC7Qvu+vnAqxYWsC}M{2TKrDp=)HIS z-a+V9N8dq?_r8A3Q=xk|PwIpBo@eG@DRi2f#BlW9^X`=&7TO-8?;v{bdEds)6k1pr zQXk%N9?sG4D3nKDA`agBL;bszLSgw`hNJiXFs!YW&_tz*IK1OLe6{VUQ172M#L;_K zy*x{(vF9Rjc*n^|JBZ$ULCUol;d0}z%sS}37yNi@EnMh5j5xgG zJet4YxNvsn3gY0sKQc0(Bb@r~48zfTe-xfqEu1*$0&(!(AH5!UOE`MvK;r1Vk3D50 z9HzCHIC}5LZ_E)6a5+vKy!S%SuXBaH66rgL-g{x`%ICr^FN2vr$9wN>Ya?vmgU*?c z(R<%gW-ItJ{}A=z9p~`{)p>$%C*%xA@BMMQPFKMPYk%V4y+8hayGHOfI*T}Z?~B^p z7Sum&A`aerk?9sYL2WzQU+BFTMY_!wR8GCh^wE1SYO3ibDBYJv9KH7mQ)&f|S~fy>Iue z6U1!oKpft2N^BbK1>wBO3`g(1By*mFAjoGMarEBXC>9F*9vBgacbw8C*>!>|?`eOb z_g*UK|3ct4WE|5+@4YnUxP##0>W##~doOJcJtMHu(It-F`=n2c1m`c&cM!ezva>6C z3S>!i&P4CM%-80nKv2JiS?39O?`2Pm9RD`3K5*n&f$l?e1`R$3X1Zxg_Bo5wt`FoRk!7?-Y4syKrf0CR93qpga58iu)MyDl$Sw*=F zNAJDDf6r3EKR@3Q2k*V&nRmTl!uWo~(R&}qxZgc!T^5UEEVG5y;my?-|(Xkk7qb~@6`#3 zsr(z}vBbf9ul~}}oga9koH%;#vv#iJ`Stt1ZKdrjft zWo`HQ`_yj2j6IfHF5Ob*SF~Lb=K4Vss-=8)_n15zM8=jrjOoxZIb0+{)x*c zh=ccD`|Z&?{-Lz<#Ni#MZtjrF{5?$}#KC*7GdQcy-`?*b!_j-M3yWOC-?;QWad^k6 zd-d%df3+HY2RYvR=(R2UrH%pA2k-sGQAbby{I~}UNALZGNBLU*%$hgE!FzvEJZ>%j zpDwh&INp0ty%zrX*|fjVdw;p>0Zd=Fi7txhC zc<(QNdamd9ctYR7SLnUZuY1q$+-48;!FzvYJS~ggc2X_F(R+V&Q|QI}wVS?!=)J#s zecy=pMKXo@=)I5Yxqrz1N>O(8_DdH)c3`@AX&wAMjp&q5Xy4dwprMH}Cl< z+Fu;+z0ZOTyo!yRXdUq08@8U`$SXF|V>o*64Hq7M;N@R&CJx?v!~KEYya)Gh69@0T z;m_%-ytKDZiNib2>qX%kdG`ir5(n@7wdvOpygSS3JBZ%<>!?*9c~KhCOdq}X*Kh26 zcwtVZ#L;`7@Wh9AJ^nj!@ZK9wkKV-dsiXac-h1O!-BCQxZsANHz4ylQJD+&2a|?;X zJ5E!7m0X^~u}{RodvDsY)t7hS!eEA@_ugddzL{qhwUjt`?@gJtsys=>DdOn8tNins z$5&|~j^6uHo*&QfANmfW_x?tZc8#aKcOlb9@BPiKZd-U~Wk-p_JIS?mJmnpePGTP z-YOGc;^4hE?;h}#x5WEC!_j+hb~xeBo0qwpIC$^P*_`eR#)d zUEM`uTE1VK;pn}$%J%LwEt1nY6TSD=yH|gjJ`5Q_eem8}KfW$B&3e3wIC}3h<^-9h z{!k+h-unk_*-q2Mu{I1x@BKqy&Mwp2TW=Bv@BKq<-(RMYyvM}Rdmnx@$TZaFBysTG zKOP7PHVt@S!Ep56Ke~L_WqS2JeFwpNSNWJ9IN$X0B<4Kue;jz$d;iymcl-D6@Tr@{ zta%Q-_a*IP6`xkqUgLQ0Jd?$W*8%j;&hg$O>-H(0wYx>@fcJi`X_ukm@mc!+88h_W z$EDgSGBX;OKF51MF{8I4Vf;Me=)HUT{G*7lqF+M zze^mw_l->r3cKKr#L;^{f4HsUT&I1+!FxAP${C?B)m+T*^WeRke_Oavp_xs;C!a^} zeRfci;@G4o)JN}KZ$PU=5%hOfz#}PHe(R-h= z>8L!b*9_w5y`PP%l_wa;h@i+qVP0v@ZPN|+y}`6X3{tJ0($TLJIt1QIQ?b% z9PfR*={C7tE1gw1-n(^OvHaX_`gWlAe&PPEcXHDhb7mcD@ZK-{PU|Aq>Pz2Tj`zNB zhNJwL37s)G-n+3+sC-vRds+v)ck7!yGUV&$(zn9~ym#vc>2vvFw*^e!2EF&uZ_di6 zf3_ly-uuzR3i;T5bjCpM-Nrq~OFrn%Sn7lKZu5Ads=WK4&J5>x@7;oz$lD0$TWO2l z`}%?VWuKk|Q6IhcbCyQ3#)Y=T!F#t&_+&48cA0+9w*&9p_VbxOvd7;iF?~Dq-e(j| zm1P~GZ>1f2@7k+2$PyChXJqu=?XE}W%OZz{G3(fa_ip!mL)7b#Mcl+$8$uiU5^fL>`dvCX?LZ)>rf?3A_ zz4v8zPsolX(Kn9cy$dIbWxGdir9OD?4l(X-vh|9Y40iH1#$G=Cz|HT zW~?Y6j^6vJ7dvHR{bGoN_wMMuTSqonh0fj=!FzWsOS6)7KedzTUqtV{&&=+!Hfg@Z z(R<(GJ5l<1%z5JAy z^xoI3%9I}OOh0R&_wFK#+9KVpHJSS0y}R5Ur7m5cBV@QMc<(MB9L=OlrqG$$6}|VV zzrCe1Z0W8Hy?0j)-OQa$YtarEBX@HR<`9Jdii z?|t!$0!h|8Z{py+yYY56N)mU_xy2p4celv&_L9iRM@-)xymz;znYNPay+;y9?|qza zpyaXZclYAAUnD%|7UJl=_dFtzXnmk_ ziwAn|8*|+y$M=k(K6vjQ=NAo=?7n5h@XO%6dnAR-mu%=y=a$Rhy?cBcv|F;o^g8v? zd!KEkCz(<5kvMqomvui|OUBJx&2Uff-Y*BA?I9WLPUjX+@ZK-i7Eh9N|H7j_dhf$m zuaT(i&ma!oyXXF>Z1LwfI=7(r?&&=0g}7 zCh^3V6E_nF@7?RHhm+W0m?y)%!F%`e?RZ0MCY2Kh@7=3{mnG&^JtU6ad%u@k#oEh; z5eM(xd;1;@@o}Fv#KC*_woW$}@BXuc;pn}4-=EbV1^-hI!VwG$=2q;m^;@4gAeL87R&pP9ZNc<;WSSEq@t2d*Iw-n-w7 z==CDcj&yE8@7+&Z^_0j#UBL9Of%oor{i0N4mX$>uym!CqKOQ3f#Qwz5dmo|~BhvoQ zLI_SLz zOx*KIr1GYbIC$>?r|!2Ae%VUrmh0fX2YAo26gJ%)!SvC44=D5V6F%>uOB}rS>wS79 z3XApW+=AZw^)2!;;e!G?x1jfa-LmE6D?!Fvy0qxxJpGnURRA>h3S%P#&9jvug&>4$*#9vt^~fN%)!5^?a}gFon9 z5%wsfa|?R!Aycbv3){}8yL9y4Lo~K55q$9&$gC3z-h0Tkgg1hwuXO(q3f_B2)np^V z^MjkH58iv|z{~c6;=7lLgZCb~t5dKbdobNUgn{=SYR69(+!N8c1-!9}@)@JW%LEvKMjztB%_prtHWdhGDcNu;Iy!SBP>~;c&pLA}y0p5F9+qd;5@sJ*)Pt{af2fX*2v(<+S+M4xZcm#OwH+4&{@xRp4xg`R;_nX0M z?(yHOrgIB=?>B2>R`Z|xJJUMgy+;fmeT-kywua%6;JruecM|fm&(OIg61?{ar@yZJ zdl~(x58iu3o_+*BdfaK^;Jrt7cs7S0Xi4W5^xh*^Zr#rJeC5XUqriKQ6epDM9oM}i z4&Hm@?a3{C^PoAz!F!Kv@$AYMbfR+$dhbzFI*;e;Xs9#&Xz<>n&I(rYPdvCy9K81^ zpI4duy_4wNg5G;n#oikJM(fwq2k$+)-+gud(#8c0j{)yJdi!iM{;W+_#KC)yw!Y@g zA0MVo9K84F`@Q4%L%YTk2k$-lx1xyOQ^$umc<(U_-Zk^v=Fz$37I^P5#>b3#U#Bi% z`nSM)j|tEF#(QILOB}rSn1&_&c+Z;+h=ccjYjoIjUdgs3hR1^Ue(UIvO}y-g?!>`+ zzvXUymY3ACl{k3sw+g>1c+m!QZb9!owwwADUf?4~rhgl}_tBPZ%k4=b4<(aqkCJx?v?B~&yJi)FL#KC*NJ;Uh>PbX$A!|#Ciep|auAKuBn zF2uomzkS_cDsQiGEphPPZ&z1u;B73PNgTZQJ43cU%7k7>zex_^iV@BQv6{~@N? z-*i!zniIJWf$V$y~n36aWXx>gw8GKy~qCuyJ0HurgKXoc<%}GhGd!Q{IX}( zNd)gb!N9uK^yE>xe?ad&A@u7n)4fS_Zb9!o;ibkP(@i6L(mLS1Cyp$=YPw8*l;QWl zdrv&Hc8uw)=X7ql2i|+4>#b#`6IRf<1-@5z}Nr%cXU&8I$i?%L)zlVwL`eNeXy{GW{#F(56rgIB=??gGQp;tIC$@=#WyCK3~i!w3wrNq zJ%_F}>9wUB(@zKQJ#C}S36pj=4ig9OJ$}jR-$o^Bb{5&druD@Q)Zm>?l`Rj-g|njbE|R8 z_L~gP0Pp?&aFy=HL6LNB$=LJ1-uwOih7*mwdeONBz4!Z0&sQ5?G@L=}fcJhsZ(FwU z`9e9v(Rpa|?R!S=MG-jK1xsa|?R!S?TZ9 zjo#iVW!A|C?>+1H@i#`bgT@gD@BP7of{sR|!p_9Odw*cO)ZQqk`~Y$A-XDbD2sTPy z7(^Vr_XiC_(~V+0>D-b7-h1|Fn`cHr-z%6tdhgjszI`+DIy{Luc<)P)?6Q%?up>-A7rghJZtF%G2_<2~!F$hH7rV$vxAHr2@ZNLIjooW>YAKyt(0k8G za4|62cXcY$&jas0=d+5f(Wc)L;^4jK&M*u#T7I0)E$F@HYCWG~G&?zh`ry6iUf&jF zG;!o_;^4jKR^Q7r8m6dYIC}4SL#FjH>QzJM7WCfp_IRlnwO^^AK6vkWj$MZve)B6J z4&HlSwrGK2vkIMC^1*w5*!J~q!`jm-Odq}XhszG=8J4E+Bo5yDLqX;-!`!j+h=ce3 zFlMgMF!?;4TMEE?fA}`Q)iCBo5z{XK?>&EF-w4CtwWEoH_nv>sEXU9*kj^dWz2|$i z#2Y$w*h_uz-t)^&{57;t3t%{U?*+Z{TMUI+*2KYkFW9nto}u1^GUDL97g&amH#~J= zJaO>e3sQ%zG~Cx<%J4$)-V1)%9x>d!@epzF-XG2V&No~h5=%0wzg5G=4R4;`=u;X859rWIdG`e0f@M_sc z9K83UYogl*PCMz`g5G;kWy5s?izqXuUjp8H@xTLb3`D&PiG%lEyfgEufu7Mw;^4g( z+sz+saH@#TEv4YS7iV7YZLoj#Zl;gkdr6yqQw=t|_z?&1y=1ZZ27~1v>D+?edx>f5 zX@fa?N~jOsdr9O;nZd-^u?#N*@4ck4K-XY+e4dVI^)ul+ia`ry5nZPe47X`;*z@CHlb?>xqN+ z{zTVRTi<&zomqp*@8$b< zl@Xz4yv~<{5f*s|<*P z_g=ZJ^|@Y|Kb>39d#|)U`9m+Stv}O$2HtyRdO?$3%IQ|(;JsJ=UNKYe)_pp+p!Z(2 z;HIix$T&K;p!Z&7JVIU1+rord2fg>IaJ&6_PA}7mgZExl|HDYnavhyp(0hM2TFYKf z6!@9?;JrUPQXZ_Q-*GK*@ZO)fZ%EfWtwHCO=it3RE4&?}xBr2F>7(~v-ECaH-j<13 z#KC*7Ug!E+Z-w;$;^4hkpKI4nZ_aBvx72|5UY%e(Qg70xjZ7cC_v%kIJ@tl%(YXb^ z_vbTq?$ztlmCh~by+7AVUaQwZJC|7pz4zzWr`zg&&l^Gn*md#~9ej?yiA>qQ*A_Zo-BJl(vlbpL?fdrkJidfk+pbZ$ZKy|!&unQm;) z5zIR1z1J?A*Qy)x-`^>U-g~Veu)D5zK_b(C0p5FUO#g|x&NF<8gZEzh_WWvH%Zu-b zgZExHp>?*d=slfV(0i{tbxNqKzl+W-FTs1S^LnJIdpg>XSqHuMy0R4!x(E8C5C`x5 zMemzFx?7Ct+=AZwi!HN4!8Riif$=N9zdU%ni_ zMduxV9reL`e>L)wx=vl$CF0<{zdF=*zs{2dB8E4B_x{S&*jp#hgU&7Jy}!z@vDZoY zHjw(@z1Mf%S)>ztP>ncv@Aa#b({)1P>D+?ed%bK%XPv7gaoEF}xAH_l9eS&gz{0d6zhN?+uj? z_Uars>P;NH_tyg#T+!Kb?+tPA-e2zwwAEQTg3c}Iy}z~_P^dFkK8oq1_x?Kbe5%f* zYCYoMy}$nZp;BkW@_WR=dv9EP>WfYv-)jti1KxY1>7#m`4u9Vh2k*Txa^+O*A19X+ z2k*VHF=Dv(yEHntp!eQ1Zo~rZ7o&}-58ivzar@ocPs~yo{uaFVrprI|wDanE5eM(R zsaV@uJ9YJO;^4i%=}|7!jty8t9K8288#g9vhqiNOcr$qKZ_MvRXkR@`=N9zd-y}`= zr0txMNqz9%-+aCFQrqgkKl_H>``g*={%VUYPc!{@;Jv@qH5sa{U%!Dkc<*n6>gH*m zS?^99y!W@YyLM_H2zp5zy!Yl|DJ!+NcA|3&dhgBqrytQ?sX3VGw}AKF>~xi{JvUpO zIC$^PdEK40Cr`Rf9K82;9VFq}BW$h^2k-seibgN(K8=kGe-GaKJMp19?T(x2+=AZw zyVwUsT0j2#yXnw-f7h~buvSYq9a;yx_m;^)owZ)*Brv=cy!V!~1IB7SdDxjac<(Jf z7R$9BPNj1Tdhade9}j7zIxM9=c<=A~oi^2qeMjdO^xofZD?FnWx}DA~AHaKmZ@ogU zbu}WHSqHuM_vsN=v|M`iAP(O9``;sOYgri_Ar9Vq>jL{iEpg#WhJOU_z18SPik895 zi^RcuZw=S!sCCAf&MoM@x7L?`);iEi=N9nZRX(WRx~8?&p!(l+{*MFCdhh=_@GdtM ze@pi>v(BOSz9{#lqGcIh-x9oci#K`?I2k+hL_N*oHo*h*fegV99t9RO+ zWWV)?5l8QRqW?14+p>AY(R)8x*+o{hES@-c?-#E0TOrE}EFuox`-S4I-DHV9-ZI=8 zz4z`Gt7T#Qj>OS>UzgNF=2=bOV)Wjv<)7EeY}PNLK6vlecc=H32_xudB^&VGty|UC z%QOc(Vfr@cy-)G&D?1>gZ!yPvKl5axY<+!K>Vx-gbG3JW+5GMFvyv@%?>1!{x5&od zS;q8j(R=S@Hb^#LB;E1aqW8Wrew(bl72P?Y_ik(UVTkl&GySZD-n(t$lpWF+`+G9$ z+ky9P`{~RuX;Jbd;^@6k^V%&w4?u?NdOsAif(0jMHcy~y8+9#*7iG%lU|MkQX=`wW@arEA2UY;nOTHs0?ymtq!!ei3m^JyQU_wEqTWwNxJ&n%|z z2;RFx)vA+{pB*<7NAG=rc&emH|15Fv-W|8coR(CS$%%va?s%c;vLt7@2g5Id_wJZB z_N?S?P&9Gy-W`7&ohb?FN&AT7z0bL%@$a9&-a&ov-Y@Fr&yiSF(-|1O_lrRtv?ToX z=1kuSy!VUGm(P=^MbH`83BC8hd>zTYfw!p--n-MT2!F|1`6J@sy*t@9=u76*(>`(r z@7*b5)OE?&?Q{lq2JhYJ_d!ER-#ZtWbiiZ?bG29Kj z_e)MS=fz#-e<2Ru`=#8$@uF|Oy@`YO?$&OXrRa4>`dJdacekZBiJ~X^H>nTayBjb4 zf+(x(0mI$Fdv}ZYku18iyp}k4?{2T>*op#!eh>%m-F>u9s>rQZf8yZ1yC1%8FS6iY zX1E7<@9wTu=^~Tr7~bZ7eB;JtgvMtnm zWK0~qckeTvHG&KSdxoR;?(JO^F1YoC_7QsT-ep~12m)42p+0!;S9`4)B5(;_Lma&K zs~aV+1m?YtG292d_p4^H(E_7?|7IPAgZF+l;cbK9oTs+C_gDw{ZX@4iWm6Zv@;wh{;L-S_M0kNkud`dJ&j zcfXm3C-cJ&D46~=@ZSBjTt4$XQ#^@-_wMJPH2ZDKH}iL`v+#L@IF4GeT3e-|MNBrcrO-M zQy;wdfWeE}@rr!?8IIn2z%HZ3y!1|q#KC(Hunjf**HJ7b4&Hmf{knf|s-CnE2k$-L z_pr`9rxhl|!F#_xf6sEBBAE6Odhgc_?7Q*|dId3k^xm(BWUS&H7o-vg@BMo1?;gBu z&nt+7_Z~QG-WuMr4Idef-h1F){a(DOQFOO}-g}@!&^q4mLC(|%?>#W(elzYC^nP6HT@-h0T>t$R%Oew#rYy!Q~E!_UX}{^52l>Ro zdk<;&Y;QVtMm@vPdk-Bw{h(>z%Wa5*_a1s!W1MN*HbaSn_a5r%d)TB^a}IIv-a{Wg zaW<(b+(sO{_ppw=kD3%N)L=Mz?_n!8O)^RGeM20)_b{Q^36sdq?TLf;9u^(%X5wQw zk~nzpVND-SnK+a$WH@^7H^xo5Y$98^i#T}iH;$b-W1<_PLma&K8}43LOpf-pBo5yD zjYlQwCR>DEh=cbY-le;@$>N%^3`g%heDyj_lPMeNjt;%|aEZ*vWJuJ0>Vx+l9(zaI zq|0Cf;^4i9H@D0;{&voW;pn~JoH$X>xZ!mGaq!-6o;(p?{AA}u;^4jC^z^7O&bqsb zIC$?jiykjAzN1R_ROr1&bnjwh9B6II^wE2dShp&~_|khv;^4hU$VJu076*fggZCa0 zcWb4wNh<9l^xh-hzu_C7nY51SqxT*;Wo)?dZl@E(!F!K9bL55bs&7K#;JrtBUy3xI zb<%}6c<+&=`4VH*>~Myo_a4=&L$q=4nVH1Fdym?=)K4ERcSR6mzyzt^xk8(EKV}gUg<>~y!RLj6C0yLA+f~4 zdyh%Fkz%x|cLBrEdyo0@;=U_BwBkd#f-f#Ik zWEgdf@})j_@3$&5e;9rl9M5p{-edd!$ufL(u825z@3Gq#xEPkaeoGv@_t*;t*@hXr zIuZx(JvKG?ui>q`whTw_J@$J|o?(D0?IZNwZ_gRxVd!F=M1AnyZ|m;LH#B?yggAKb zw*zh485$jY&v5kKZ&#;3GCY~ql{k3scLx3RGTb?t?y1mwzq4yjk>Lub5bA^Xe#cg~ zv*Gk_X~e;MzjOb3iQ&kTl?+Gk{m!pveuh1=KNAPF(oH%&zclXZhV-U9RJ8|&d z?>cC1H1NFEk2rYmceAdA8rXE1%5e1F@BXc*F%TN9Cl20w{Gz@C4K&JWAEEaiZ@jtA z;K0h4)Ccc9{>J$TgY_Z58IIn2{L93b2J`z2A`aer!iZ19491IQ5eM%*;lR`egZ{Of ziG%l^;G{Ofp#7$^3`g%hA?IqN{>LcVN9esLwk;c_|6)j6>Vx;5xU}aR{UWnr#KC({ z!`*TEe({>b!F#`_`u?!K(`a+z;Jx2FJb8k? z!iM${dhhpKPkqqWZ&hXb=)K>2cx96Q@&En|1$ysE9g0rqZ%f-leem9sR&-0%Up85f zIC$?#!Zly@r#f3P9KH9XXz4WlVc%&Vq4%ED6njR$+bP;d=)EV8ZO+vDk+YoE0q;He z*!Z7%O|uRV2k$-E{rGIX3Qr@3qxYWt$X!z}N5zgfc<(8lAI;Uft96|?c<(8zJGapb zeoXraz4sK!%K3U8i)bIA_ns0f)YG%Nc9dBMz4w&n=tX+`EY%4)bdflC z@2MxpEYaIr5lS4q_f*frMtW;kr86A8_tYZSWqNZ$X&<5Yp4RPQ7rn84PEsGd_q28G zSL*c@iHU>vo+e+)*Hfu=WjK28X>q*Ox~-dT5(n=+?R`W~-J0kJ#KC({pIpCIw{S=e zaq!;L&y1AlrkK$_Lhn7@dw;ZUM5CPPqxYU(>eNryXBXX5q4$2jXZ9vthxlmfgZF-a zV;hC8Y;+!R@ZRs6E!v{1Yx9cX=)K=hFdn3Pr1cMR@ZRr#4BM`|-H- zPV=Tdc<&kN!*=RUnN0Uo=)Gt7?j5c>#Q71^NAEqO+N&kROmS+OfNAErJOOS)k9TmE#Lhn6m z#`8lufm+Sf2k$*gYmk%9rJ_y@NAEq$f9Fvhi$%1L(0k9Sw06-k@n1}R@ZKNvPtDdj z(%Qw zl8<(NOmE`gz31%wT%w&cbPB`Kd(W|*?x!7YzJ@q>?>YC?%e1{3X&<5Yp7YDsU)yds zpZegv=guqF*A~bBWH@^7xdy!hwYA6eCl20wZpbDh?L)THiG%l^TWi)wd*g?V#KC*d z8=7FEz3?#IQ=#{sx93Bs_QZ4v(?{<;&tXc9_MpjsiG%l^mvwfPc1M>X#KC*d`|Gt; z>+_G<#KC)ixUeKb>(%M43`g(%p>fY%S|z#a#KC)icw^l#t&G_U;^4hMd?}Y|-MT{i z2)+0G5qDy=0@~i9K6vl>``mm_tk-z4t<4$BUX_LrsW-_g)yiJX6!t+@3gi?}bhL zpPJT9LBzp(e>^ton5J-dD#Ouxe|+?{tENUm1#$4+AG@pOY91JKj5v7jj~^Xsqq*Kz zKpedHqR!6qHRpYBW;lB9MXPczYmPr0MjX8NB1zi<&Hm{b#KC(nie1t{v;7p>N9es5 zHJhqye02Freem9kC)_a7sQW?t2)*~>6EBN3icY&Ree~XoJx6rWNXv~R4&Hll(Y}=$ z(X+FOgZEz2&GDLs-<3LsqxW92HZxqqsqHV~;Juf~{`S;RXrChv-g`;hLPHJxB2VJr zy_dW{fmTN5QT2CCj_fqd6n>41Hv>^`Odui$J z8ydr&(mq1(y{xCr9gS{l<}iKq-pe-JAEf>xERHyM?`7wHiPW3=J|+&{ds)I93-t=| z8{*)-mwnW&SI>UYp5f@dKbaahT>b7Ax~D?#{fXMM-Ri+Hw2#nxf8skZR^4OhJz596 z_b27so7An$ONoQ`Uf$=zXmwsw3&YWSFW;PUKwWK57vkW(m!JRYpuRV83~}(@%adld zsIMKnlsI_rQtx~CBXRKFEByO@Qdjx! zf9D0g_ln9bQ`B1jnMi%`-km(C z-uu(kX*1L!a=#G=@BQg_H8nM#Iei$8-h1V2pDZ;8uYZVx_g<+}_FGNXZXI#(-Yc2E zfJRs6IC1daE2}qXs~st(dn)was|G3j)V3_9eT3e7)y{YowZ#DgnRU>6ud-=f`2TTr z*HKxZ+aAa5?(Xiw?hX`{?(XhxknR=%K|mUjk`RzaKtiMxk!}?1?mN$(weJ1R{r9`} zd7OLC412%t`RFKbo6bQU+~Oh?;Q!#4V7BvFQX3by`$$`qEdzBe%8^wcP_J@u9VfsOOr(o6@ zrPwQ~)WN-XdcHPOx}HJbQ=xnBtk~45x|rnFpHG!M%6&pIM-^XFYvSh3>t3rP*qwH69Uc9^HGl zNV=WU?2qfIgM07xdDW}#9KS1R6BET9hVy=UlzkYZ@f8P?Ig_pUK^SM=CN-&3J` z?-frORkR2-qIq!dz5WAV6*b0tQwR6nTf16JQCu^gb#(826GeR#FEy1=2lw80(x+VU z@Ub@P;NJU8t2Qfcj+Uhk?!7O4thD0d>E^7Xd+&R7BtUVJp+9wS@BQobs}(-C-K7rh zya0bTM^s?}Nczw-rtahEfOjKG?YUxx%goS*)XbADYg8L}AtXI_luwht9j6 zSD5KBOdZ_&kabC%{I5^rsDpbS%KqLiKPt7Hb#(7T@Al4@Z+m!vI=J`Yt(r#iHQRHk zgL@xVh)$Bv3uvMa?tM74tw%ob=P2vw-iMzqJSQKbG>JO6_m{J-^2oc^uAvU@{pCfc zRC%-gN2r5)e|fEFuDoiPAa!u>FLS@FkQW`_!aBP5mmfK7fqjAg|}XnUl4PWb#(8qp3f1LpD;s|I=J_dxm>w&AC2^>gL@yj z?66F(x9u}^aPK1yc^l;#&(CHZ-TO%XM;E!0yIZJ(dms6-y+AH~!CC6y-bXnUKgmT} zNl^#)KB{(0Sk9-fn>x7n(c8^(a<*4}SV#9h+BUOPPA79db#U*m7hL!zC$nNFb#U*m zxotS)xE(pEgL{ANeBW2@_^2G~=-yu!y{(knA!I=v-23bAn}5qK&kvvu?)}YPxqWie zHzZRB_x?sRSVQ)Q=N{_d-rvOh^9S6#{B(tNbnkCEr;U?sl~SS(?)~lJvxj9LJ+z?? z?)@#FWr%Ffj$rEG-ru@q)XTt=D`~3=-%V`JZ+1N6RCrHzt%Y@YI|pbGwXKf-WOS| zv%MZb|F_$rd*_*FZ)>}bc90#qcc%nyTlMr+^bWXpyMmL+wnC@qMzRO@ZuhC#%=Tnm zE1S1R_r62pob6U=y5a57y(^80ww*tEo#xTKhu+Y!`D1B79o)Np(~dfum(zGzcR=?( ztu(}@DTIEuJD__%E2d;qxP|^1cR=@U{^p@gLiP~71Mb}+-O%TgVyQyHkf; zqji4USoV%Hy7vWN!mQ)f+26xsozcBt_55w^_bs0~xOZpAb&ssATtcXWdv|`2yTw{{ z5q-zy0`A@UBhN=`{#YS4?}F}qTYsVT@%{AA@_*dBg5@gfO=a}65#77X%{ecv=kn98 zaRvA8(h$#O^|PCHw=25$sV5d#4eHV@^dI+ry0Ocu{*NR3>p}PKYNme5>VcO6b#U*l zseh+g#jT+4Ox(b|yAEA{YUP`>lFhrJdtbf%fR*KuAJoCUyNQ&Hu~L5YoI1F7H*Zln zD?U-W0o=j8yOqD*Zgq5!ZYpnU?0Gl5E}++`Gs7DjiEDOS%Ejy?btzJ#5K4jXsN^d-s(4 z9AbGS_!GSY?%gxkL&B1rVj4iXJ0^r z#kv=Jse^m>(cBtlF;kuPlrOkoOvFE*LrQG4({E5(%z}& zGgeHd4({FmL}jztmplEe2Y`F`Hp4Q7xzN4)-~IU4tmaV!&4YXQ?{}*<%NDhw z4(>f*#hUGAk%JP{!Mz6vW_>pEFh0aOy7vH&E5&9e6Bbej_a0E%yVgw3{{wY!?*Ttd zN6fCQeL@}F`}MuEu9_W4qZQ=@F!Q|R7rNWbznmAycpHPF4^2)rh4%GFGJ3f=pS$BQ_a0i~8)1C$@JE^l_Z~KW(_iB~mG!Kndk;IBUv0cXB!xP-_b>y2K;ub+YpH{K z4@(^WZ1moU_7uAJupXOYqxSLbG!O3m*3yM*j4J$TPoaCi#dl}KD1B`Zn@9J4%k^}& zQD~YGb#U*uidq&KIiC`s4(|QdSFIi+y*duo(Y=Rrj6Y)}DLsQaxc6|CkQqi7MqW_| z_Z}X;tHo%yMJ07`@8M4$Mj0)i8cQAAd&Er1aYhq^?O8|n9&!G?g5kT(a@4`SM_9S+ zG<@;z?>$8K9+A1?n_>BdWi$`&Jz^w7%rLcy_7uAJ$n{+74R0zuW%KCXBPBZD7&^YE zJ%#Q)(%+cJQ1_ZI&4YW7te(EqP+~UiDRl3*$42%Wo)5pm=Fz?1K62>1A;-=w)WN;q z)_XkLaB2P|>fqjQCn)L}PPp7h9o+lvuJ1<;-n7!5LiZlE*k__at6DglNB17ZyHUlU z>?`dlbnj8l4|W@*IE&Lfxc8_+em{edg$JpFdyo1&BxT?bvw(GU@6kK0^9^+NzNZfE zJz9Cbi-A}P?J0Ec(P4=K2Iu(h(mc5L=;l-R40d+Wo2P@ z2kPM795mo&1Rb#(9XpX8tE-LUYc4(>f+ z`?o`Sc2k~F2lt+!=siwP^ItQJ?mZ!NeXX9zCO+se$(52 z{wj5F?}=vy#q<_76;TKGo@j2hUT=&7-G}Jj6VvCu(S7yag3Y6QPaKZV(S3SNlsdTg zJ8MoZ)h(WVfI7JMJEBefx_83oQ3v;a$4BG5Zs3l0tfPCsQ#od~uI+=z)WN;q`4jj; zSL1RLb#U)V2X`FR6>4>*4(>fkyY!~+DK%y4;NFvB#guioem%!Jy7#2^H@kHgIIp1& z?)~lpho3rs7yO|P?)~o7r4>3aquZ#1d%x?Dwn?XXZ!YWT-tRu(bk->>xj`M=``r)i z{5pwzhSb5mCvP)Yp%c)>PaWKQvceQA9UGlp)WN+chlF3$QTt7M3f+5h!~O&vL64Vg z9^HG&lxhQ=lgnsNp?gm`Eq6j^OJWSogL_Xg{W3*o{$V@T(Y>dnda7yvu9T$??mcB_ zU6uB*$Z_i6-cwiQ1ZX!7(4Io~o+`p4qg`P5mFB^{r+W2o)lL{kdkWoqYPp4*w!hy! zHjnN-_4k}t+SY4mPoaBH+aH&ut(vM$^Wfgow2s?q3!J>nI=c6?=*G+1#~;(4Lie85 zrk13=S!yE9gL_Y(_s3X!?kn0;=-$&WUw@|c%eCNb*Z-tTQ04bqCss-X_<{hpltS1rHu^I1ptelK`Qsg`AvGj(w9 z_nxG9X{pH9Q3v;)G5Nfx7T^0b)WN-HoNVLPI%exe9o&0{iT)C;O|yQnj_y4pWm2Ek zoLlFqgL}^y2usoYx#K={aPOHb_s!BA$fG@l?mbiJQLASCWdk;k?mg2}_K0TQbJ|nr z-ZRTSPtc52+e!1_-ZOu?KhgC0I*mHG_pE(uIW#SthN**l&(g^Lp{cx}oON{XSy9}Q zn!M3b)WN-Hwf1e)Ji7N9b#U+5bIsmqZYYta4(>ghYt{qJ*?h-XNB5q6Eq1xa_pT+> z!M$ha9JA8s*Zx8s+fqjU`WJ82m^ChpI=J`T<;ia9-~7y3NB5p9cuq*YZw>7!bnm(D zt*g}^r|zeDaPPULy0+>$CuvWid(Ztbu}eLw_ASkWd(YbwdPm($vX*sp?|JHbjMYtF z-JuTdJukBIw7P=13w3bsdC#S%t8-7GJ%#T5!R(Kk>W6~PvUzmx4=%dZsIS{ZdkWqA z1KZU%)MsS=rg?Dh53;l5)xMsiJ%#T5!J8{3YCVlPG!O1Qe`AlQTCIE_>*(I|rA=R} zWxu06h3-8+aOQor+qQf(5AHp`Hrigza~AC>bngWdj&Q4)+?vkj(Y+TOdy=dszn%6J zy7vM@m04<6@+xQ^+DpRH;KxuU6qdoSqqi&k6n@9$kg_g=VcvyR$yRT(yq?!Az| z;EC$zFSMu7y%)L(aj14XrP4gO_rl_rKU8bx)1E^2Uii(nTs13NgUzFRFWSA(M>TR! zI(2aGMXE{fRNYH9QU~{56mcd`)tFa{I=J_uXU~_b%64{92lrk)OM6iDvi4@y(Y+UQ zj!#qF|LZ1oaPP&|H|MIZcJHMQ?!7pRqg{2{GGXfA-it>oj;nl1xWziU_mT}#lU2G7 z&!G^sBDw)Exr_jBZ)U5JX3Gb&ph3>s{T&A>&n<4Efbnm4{ zFKp zf4IvxQaNMF4(j0EAF6E9P`(v3l{&chhqv+%D7*gq?|ne`{_v^57-fUZhio3*d-;rE zIc4c{w5QO$m!G#OQNGw{OY`8~%dHl8D({u2J%#SQJmZdt@``syX&&5r`N-+D%9H>7 zvo+AYSFC$Fs`O#zXEu-Sy+T5(SE)U$kvh2d3jcA*N|oDbPoaCSs0ubyO3(9R^XT3y z$L>0-6vjn+3f+6<;fFJooS$E!d2sKQdXnRn^i^q3p?j~4e^;j@`DHxKgL|*+at=}A zbnIpw-TR}(E0mP>%%?qt?)?!@hJ@1cs8E^*_x{M4YlG6HJ+!CLy+11Ge5?4bScJ`^ zdw=xVC{MACm-ZC8_o|)KoD|DD<7pn;dzDh;fMS}~8|vWRtHKU(D&G7_dkWoqRny}+ zicao{Y#!Zv^>l?cMZIM!sDpd2KKm_BQ6fQ!I=J_03!h1f7Y@;$Lib*MZ$pD3N5yJ3 zkM6zt<%7M7%Y^-@gL|)8%iEwhpiW+pi^tZ z=Fz>^c8;-85SNss4(|Q&!XP7sbFU6l2lxJ%d*>;IU1oG2qI-YrST;>z>Ew@W9^L!n ze6ePQ@i!W%gL{Ac@lAyMn~f>d!M)dQcM6n$o4^zHJ= zjhw8bd#`Kc{30Lx@9&92_x@yRd$GLzTiR3T-k+Q?%$C=-p*@A}{fYThJ9)90`Rsjk z?@!VquF9VcqdkT0{mJlw#qvA1o6Nz}m3*$I4(`3-K<;?CyN(Ie!M!(V^M02Ln(xFq zy7z{d{$M$~DB4r#-W%F2z2&s_oThnj?~M!QIm(F?ucQv{z41!?2Dvjlw5QO$H#(eL zCbzxgIh#lK-k8^zE4N6C_7uAJ#t-VLa$|l5&^);JrmcT1WJla-PoaBnk`KHf`)nx> zn@9Kl4`DebTb!_+I=J_y`r3u9_uibkOiEVRZ~=92@6Cg0n`KXrm7)&r{pqR;A7r=r9-$8I{i$$Uf$YN7 zi&;na{?tp~MdojcJ9TjHPs^uR%e*>HdkWqA(_i72WSVQ}K1BEaZ2$iGGDQ-!r_jAW z)2!;0NqjYiy^rqwS+s1T%yqMi)WN+!d+{Yg#&$B@hv?p0=6RfyQNJ;c=E1$UTwb?d zMrflk>*(HF?C$@SIhj#F9o&0ME_b!e)^oe5gL`jz*Iz6%zrl<;xcBE<%w?tj$c<13 z_x@aNj-))p60>5x1LnB zlMdK^l67?Nt;T;$rLFU5PoaBnO}>6cTI~|;DRl3x1KVau3$(P*JK)}5tSA{LeL^LZ zI=J^2Lc&qfn?L)pj_&=1$7rzhJV#yX;ND-9*(*x@o`01(xc3)7m+X`pirPjU+N^Od#J%#SQ zW78)+sksO1**v=U4jK1jl0VC7PoaD7xUpuU`F=HWZF~c z-n*6`9g^fb9>V6)y>|)J-;+FAV?rI=dzZUvg5*XC+EeJ>yGnjtlAQf=56y#n@A@8a zQsRdh?J0Ec-Fr57NDNFGW%KCXyVVMtB%TD)o)Dn+Yragu3z3-0OA#tzI;xrHL zy{~umIB_!v+EeJ>`>YIP{jNP;;)i$FQ3v1Mt970_xc7nZItQ`ZAD+~~ zy$?K7HW$0^_Lp^Z?}Iaco)?Q+(oP-R```t?*|h`5DRl2clD50W*6P!Ji0*wTV9_SA8Dn12 zJK)}js_#^Ye(`z4I=c7aacA;Hdsf9#2lqaFq{UgZCfR{Hxc6awZGO@0<8su&y$>f& zxF{M~LwgF{`*3&gkf?{mayF0d{pAvl4pHNO|GgyW-e2;TCy2_KKBIYX?=M{>4MeX@ z%AgMJ{bk_?HPHisw5QO$zx?7dMRd&u9X5~d{nf6Oe?+Hc(4Io~{z^HcO62p|tuznr z{nah5>mpqZw5QO$ziRH15vi8#XY=UZM`jpr70G;4LLJ=u$oUzoMIx+ePoaAsvAq3C z#C?Vp&4YU%$vBiHVifw8b#(6|uO43$k==HPI=J`Ib&8ioxN>Pvp?e<{|8ZJm|HTh9 z|9|d%?5N*ZBaziJ3;*f=&%pn$d;iD4y9wF;EujCduc3QCIN#oOAe4T#Uqkn<8Gg~W z#{3fdwXUIizrA(3ZMtX!b#U+3o@E@h4LVTHx*fXr87KbOS})+Cj_&=;qf%Ri5A<2v z4&A${po{IL`dXR?_ilH$BinXYN)dH%?{>Xv7Pbq#xLCJG_rCb!Nt+*E^1Sj_%!I${w4F&GgScx_A2=|1UOoC(^EW0QYYHX5|JOzkd4f&H>%~`uLYNrls_= z+5z3W_}*k2=}5ZG(7ii&7wFrZvq@loJ?P#Y9$t*J*(y!D)e+pg!}msIo7ufqiTpZw~!j>}xbx)Zwhi7v6$ zp8oTxqkBI#x5?T-?-q4%?@oGQ!Pa6t^v^Q7cc-|`Qr0JTE@Sh~;NG3u(mkvTM<6TIk-Lt@zJcJ&mKCh3?%sy?wIP1IH=s9T#x#&O<6S zR<{-WsiS*e@!rSE<+KxZaPKYxmW5W@tLe5u_wM34$=*uf_joq%3hv#d!0)2f;TK=1 zgL`-RxZK!kRqi$F=-#))9krSeWJDd@yQ|FZKbEhIe^Uqd?i%o*)UsaiJ?n1Z-d(FM zytmBWYe5~{yX&9&EX%Mt^s^M*yW4(A3ro82z7ApZV`IgmfT6S z``p33yEXsZW4X^wjm`hZy-#zJvs|hwMIGF``{_9wEdE}gTLs;_yYbDJ76a>B*}Mn1 zclV@?i!EyY{mvZSyL)$PyG2^JD9wX=_gHi^(&9#8H+69D9#_hhEUdzsse^m>u;UG| zP_UF{9o@S}_6sqKOXB?0!M%HoDzC8Eb?60kaPOXL-*#IpSXf6L+`Ff!#ccEMAL+A? z7r1v%uL(`&U5!`RJi2$!QlDV+^0ZUb!M%HaTUueBrwVTx_7U-^JmS^O)jGj?%jL*lS$@V2JTY__wIc}yvBUi z!_(Bky?g8Q{V@9!wV!o#@7^&wg=R0V6;KEF?%n#`-mF+AojSO8pV^KV&Ek&JZG-OJ z=fW&wGtcF_*gU#-AB&KqX8PaPQ3v<#leYeknP^KAb#U)K11USrPGr$eNB8c#{P26T z_1Ec^K=7JWnuvy6Yx&aPPkF6}3$t%%JaB z(7pR@dL3pOIkK3|qkH$0GLtiPu9{9A+`FIu_zk97@z<$?d-tpKdTA=)NKf?d%vEwHrKfS z?kDQt-mky9YhzsPK1?0ld*GTwXN}X;^;t*v9w=O*Z+zpTGIen8fgV>67+Yc>WNB17G^qQby(a=Ha;NC-cr!O{)Eia}H z?mfgYu-(ujI+HrM_mI3bk%s#AC#Zva4|#j%iJ_?69@f#l-`sd0!0^P$JnG=yZ%P!4 z8m?cFLLJ=uP2bDThEskXq7Lr;X8F_0{}|)#tfPCs`BQGTLG%69)WN-n?tXQ`ATKb1 zI=J^x72|ORk%nQ^!M%ru{;e=@7T81`+J4~x&5WH3H)0d;WiVI8Lq=)ZKC!aBP5Tl1>E>pxNU zqYm!<)+M1r{jBrLsDpdIW!*WdAG&T9b#U*uGSt)c?Z%8_9o_q_;ZMf;DxDtG!M%sC zvWd~Znr}-T+BV-Hb>!rT`O7q~}BLXkA=mkEZ?;Oy*M^rb3>RF~3v3Ydw zkz-`!^yEF&sDpctJn(XZ-bIb~)WN++Y8nda?YuNV9o&0lu%wGRxcA$pTLN_NM54cb(&vr&ve#9&_{J z7M*pmRn)<~$2@5m)tTZz_a3_U*oji<+Hd63Xdc{q?9st4?dH=*se^lu)zgpB&ReyM zb#(8svA@)`BYxed4(>g+%{fTh=|vKCaPM(*=f2n0%!!~5?mdn(Y>hU5&=%_8-s7w` z@n|11PG=q6dt7>Yg7!+mSnA;3*(I&UD~|0GHWBLgL{uJP!ZI+c_)B6xcB%E?-y%bb9JE(?mc0PC8w5(>RjsJ-V$LW+_ofc+J)vsZZ>=R`?Wu!%PxupCs`JFM-%Gc-tXM`v0igW;~47T-tTlfS!>Qq{X`wyd(xuWcQw9w z(_M=0J?Y9#J&jH+eKwEoJ;`q40gZ>3m8pY!Ps&bJ(zvtb4RvtuNux)%X!uU*r4H`> z?%Ic=8pZ>ftfPCsE6TfEL$XYkI=J_{o~>OPXKoKu2lsxrR4GPdvu!(daPN1&zG>8$ zDWgIi+fA=3KSHE=BjA_8>V+ z?ckq7G!O1Q?fqeMwH593zty69Pv2CUpf)Zqlg*=hPnY5ur#c**KpotBy5IBNs&%G& zsDpb?uT=P~nkl@II=J`rU$1jjZ|+;lI=c6JdrgN`ug#634(|P)+PJ%_%I~&Q2lsyO zmY1IDmAci`!M)#WS{$Le=PrE@iS9jPN|d7NV)rmMkM2F=h6W9>hG#Nwwf)WN-H72lntT-IdEI=c6)F9)lY6VnZ;gL}{3UgE9n@=-#tyRFgGnHaw z2dIO4&pEfPPs#mXLyPV`$1MAVlHLjhng{oulX6;1N%)5d>*(Hd`f7G79edtQ9o&2F zQsK`^YqOiEgL}{A>0Yli`FbUFaPPT}8pDdO4f&~qd(X}Nd{?oFpYBq0@40Vn+Z1y- z>exKG_q>hMA`~NLmQn}zo+lBgsOUI)g*v$RJm1y+iW=3;tfPC+E59S6$d~YlI=J_| zp9hvJ9(2y94(|QI?xM?zE0pNoL-+nbiW^AwcL(%C$^ z_xz3pXB4i8#8LLj>l*YuB)a!PH}xa(7MDWU zJi7P7!jG!*a+~OTNObRopKMCxIVUcrd2sJV+orse-`+=eDZ2L}*?>&>xh2=xJi7Oy zz*XjQUnA%)MfYA*oe(eAVKbNJ!Mzuc*{3B}_V4daL-$^Mpm4Wb;!$s!2lrmAc}Z5z zXNf)Q=-!JXo7T%2eVtAn+0vpE=D*azy_ZZMULP1PaPOr#CuYg&#tc&j_g?zC@`S9ggFba|?`7)+Zpa>!SEdf` zy-cjVTz2g#an{kjmwBsvlAXM=mpZujva%06GNV7AQ3v;4_T9=_rtyU=b#U(wIVPWx z$;lC99o_pwCI5*s;Wyf;gL{8?bH#ocM`OB6(Y-%>67MaeAt*uf;NHt8?kSMr+e3FL zy7%&<4@P7T&Te7z=-$iqIMZa7zpkMU?!7#=!ANFo4g25L$D(^LZ<9JAJ#^<1b#U($ za|VA)KXyG%9o%~br~VG<43%=$(Y;q#{*;goxxhmm+62C- zdyqP~_sSKa^Q13#6|s))y;5M~GwIz08PvhOSGuI#lwK5ef;zbO%7SCEQok&AQwR56 z`JrOHRIk_u>fqiVZQ&D;dUP;_b#(8Kq+i%cr7S!|9o+k)0OdBRfREd$gL{AU=-pH) z^M=*b!M#8FV^Jq1o0>o!+p z?^VsQT#{e6L{SI#UOkP&OtNFr4c5`USD(sjlq~Icrw;DD+W5j)$%L}`)WN-1-+59l z>3w@Lb#U+1-4bq+Mz(&`!M)ck?9Y*ukfFO2-FwXyogvB7$7iv5bni9Sew>lqv}_!8 zaPKwQj(U>Qzj;sx_g*tPD_r9JGh5ctz1OY{`6khlHHA93_gc}70ulxOzo>(Iuk}n3 zk%-o#dk@`vZRwFT2{&GIng{n@`?ai7LU-4A*3rE`-pPAdLU{TY>fqiVE3~Rg9DVtU zI=J`8!Ad0(YaSU<2lxK?@#}JlNpW!J?s{mJ%6wuM^$-lL4^{;$wwc**v=U`el6)Vncf$QwR56 z&!=4{_IR!cb#U+XPG5hC-Fr)SDZ2Oi2M!`)!H=8SJi7P#_cNWuY?A0MMfcvYDJWA+ z$?XBngL`j~T0cwdvf2ge;NBbjl8=k+=B%R*?!BS%kfzw84W+E3dvEwvS}yu)TsC!Z z?~Qw}eiH5JIYk}Zd!uU0deO?FN7TW+H{MdP5>399PaWKQW8>>*q5+n4m!f-bnqu-o z)LcA<&7*s7Iyr8?sO;gL)WN+s8G0&;URZRWI=J_y#Ki@o+dk1xd{&aPZgox3UBT5LB@WMdTF zd+6Suewm>qGTm+t&4YV?wmnc+_`U2+>fqj=$*-vrZaLva9o+k~8+X?W7c9499o_r0 z+JgeZQ9q_r2lw7Gw)mQ`>vOtG(Y?1Ey7EF;C)<_g!M(R=J)0^lblr+Nxc8PQxx>Oo z4Rl#Y_ukSn;xD|0?>lvH@6Tr%mk3Ydu%{00{rTCyZ-qu@notM#{@l!iOQ>Ol?mcwx z&r=qf3Ei*y$mY?#Kkti(7rK>TOO>(cFGg&dsqsDpcN<;j*5Qdd%<4(`3x@sx}Z z@7Xu3qkC`7tZ59mv=kG)|5AMBfcc7i1&3?M~(7m^* ztWFbDn)i&&qkC@)P3#opdQaa&qI+*^IB-~y^NaPRHMxo!yj z^f*Qx+Pr z9LZej;NCk0GZ*oHIZEF{qI>UjJ9&Y>eaS&KkM6y*@R1>Z$(L=^!M%5W5`4@b-@J-C zxc9ED9l!X!?!~i??!8M^Z9BiAAKiQC-n*`UbmAA+rMnc}dsnqh7yl{lCG-xs_wF&1 zd-*qRkD?Clz577GasKJkwzH1zy<2moCf~c^)zrbgcSpwGH@#V)% zq7Lr8XL=zIUzEKsb#U)JXD;&dxymnP9o>76Nn;Y9&Z(Ky!M*p~m6^yVw32=g2)g&4 zo}sCHM}E4~Jh=DX#Rdv|t6wZ&9o>8H)n9&m6LTh02lw7UKzsZ#M)}C=}9^HGNSb77m!>ljV!M*o+pV-c;Hu{n} zxc9!YN(o+`8bj*f-uu4uyYcQ%{KY!D_x@e&eZ0$D-cbkl-mj!Omv@YEKXq{L{UPs9 z^9-KXrVj4C|B2O2p4xR^se^kTm^i75=ia|2AKm-FQQvPoL7km!9^LzZ-U-&Q+YToLuPdms7`%E5hpGZ}ky@55U*xp8lsn8oJNy$?&L zdU4P0KS}-n-22#J|FmCMzf5G`5Bxs^|G)129|LbcX!|!kjs0h@p?lvK_Sv>)jw*F@ z?<)5v*p}+9q>k=Aq+z~obRAg&bnn+{N7mUo?WCU-cIe*6Ox$Cu;z2ve4&D2{)u(MQ zj;v$v+o5|`Il^nZm5V-u{KvhA2+G(_OYot2aPM}t+S)dwliOIg2lsCG*Tu@_u?l_0 zv`6>8_qK;kO8G^aNB6G$;HHi5h5+i|-tB{*owCt)xJ(_~yM4_&9vgw)F|0d)d$<2P zP1S1PWsmTslblIY$YgI+wfK6RLOmJ_&l z$Ldf0)@yF`u=kz7y*vJy_0f9lmz~tnz3u2ruvV2-<`y5b(w=cQ=ofyRxE#SwcT5o z-U0XSe4~4;)r{A4GrNF$cdq&|$MVfpQ8th6-TC)|)s|0^=x08{-J;b1Q~)caI;D+szu@(hY;|-E(K&QL_x5GxR>VcTc%yF0<>&UaX^g z_Y8O=W@a*jZd7#do|RKn%|tc6(>%C$&mZed%nn!4UO@Nmwez@>*|IHkW1xHYk`uXZ z`qSkedk5XSSAbr$Y5O4U1$6IT74G*;3op~v-KX5A)5POPAa!u>KHm~wn`n#E zUO@NmyS?m}2~SQin@9KVE7LjMWcR<87~Q+C-?wEZb4@1DJh*q?@_Ad0KQz-`K=>YIP0o$$^8aZ5~y@2jLKw8<}NGVQ;=E1!O_}cmz zaZZe+4(>gmEF{usvl8tEbngLQGE$AEltr_7bnn-{{1cp z-TQUlvF(O;yJ#<4+l?FDr2 zL0g0m7_3VFO!MI0gCup&8;qHgL>=6FkhhzlevdBg1$6I0CD97{rFAK69^HG;rviQb zn4L=0!Mz7>Zn>uKn;5A zhs~pVzgfKPx$al#OzPm?Z+<*Kr2D*p?rL=Jp_^pB=;kfYrg?Dhq2lHfbwe#^FQ9u5 z^$b{`Yt>43HM;lEqU80uGKY29JLuj+KUD73JrzWI0o{Ap#-1~}Yd+InK=&Rd_LEO{ zoG{(h=-$IT7s~4NWpuLl(Y=Qiap>rj&!@W@-Fw&vE^D1QeY&gBz2DlX=%wRQA4Knf zd%q=SvqwjrgNHh}_gfypr*$rS(_M}3{Z?Ump3e5y^jQ_%`>pr2GCDJ_PGaw%dk^0* zsIC1biSBB2@8M!&thDQ=O=a`w-orhXd1$Ar6;TKG9$vWrrndhhx~tK>hrj1Z)Hd2w zO7q~}BQ~hzXbU^hUO@LAA!=W#eXyVIYIN@r?qScgmz=X;@1T2+D99er`W{7l0o{AV zyZTRBZDWRL9^8B6`jPQk`EqnuqkE4Ooj6bHR^dxFkM2Fvef3%`+f@gsgL{uGII>$y z&Wi48bnlVx1W#$5X^WzHaPPO*Yx8KWKT3N6-TQ437ip~tA#_)xd%x{=TT64`n%EHr>_e-lNvN(>dye zF?3g>dyf_J%TkXVufpCz_a5t#^ibVSf$nN_@3DF1P3j6I>NF4TJ@$21ulo5lv=`95 z$F2GPUVWp@Q#Oz8Jx*xB81+e=bXTK$k8{~ITW$FGbD9VD9+!7%m0DG(H+69DajzA2 zs@?fPcQv~A_%&9?)jY+0**v=Uc)=T2)UgEwBJl^?o-+e=-v}n|8Y_MxS#H7bngj*OKzw(2aIFypnFen-W#i$^*)C>xc7wI ztC^}ne6$zPy(f&Sl&PAfK4A0c-V;|}YgCn(rB5B)d!oS29@S&ov=`95Cpu-mQ(aL@ zcQv~A#GJanDu1@qUO@MrIP!9qN|#$Vdmr8Voz>&-sT2>>UO@MLM_}a+m8gq-G!O3m zj?Hx-QJzS5HM;jZBbwqWoJwJA9^HG=DyKgxo6Be~pnFf^kDRG8 zbsgQ+=-!hY^HwOovZK2i-Fwpg=IzQg|NdSHbni*8-W*jr!T)Qtl-;A-?yN9Pv=QdQZP7kn%)8T zo^JQZP(d)_3+w3K(=&VR754uwg+@F9|=ULERK=*zxQ&~|yv{jA0kM8~6kWIV1^ zaPJx18NTwTzR+Gk_nvX>afJL@VY;i)y=P<$rO1!VT+iM?_nt8{wm`0b9_ZnO)jpU?rL=Jnb-KP$+>d)v-i=xXJ)8>ms9tqyBghl=AdJ^+~wDS zG!O1QYiW40+>WbPsDpdYx|)+OH#3R$0=oAs+r}E%x6|mZM)#g|Z}f$1z4`?94!ZZO z!AUP=(;x9s2lt-6bj>%}fX#GQqkGT3dTg?+ky8PiNB5p>E4)ZnxZjXExcBUPx*KH= zo!d$s+B?-{3+Uc+FP98UCoZJB z8r^%Yb=y~Icca%J#L+ys_Xm9o&qysB{6HPt zd;TI0KFMDf5?M$0p3lW4E7=kAnL4=ld@H5#l11a``xbQX`KdPZBqJ5%X&&5resAzv zNxKr-3+Ubp7T((}sknyjYIN@fmugQ*p0{1b-a+?XU^&Pmxv`V(YIN@fsbi!iC!bhB z^WfeKdY5TQ42RNQK=)p_aKEKQ)eko|kM6zj5|6t?lK4~V;NA-@)j}jZbLjgPbnk^J z_6ZU?i=Wdxxc9=Iu=^6cCUjS$doNm$T_Lgi>3KGf?!D+@{Zol~`)DtqdoQwh)i3_h zKb_{my%(iS_$c1|p6+UN??pZT{5gl&{9J4v-FxwZBXh-r(&(;6_g;KaaE-Y6tZbSG z_g-wF%^@zKtwkN&dvUVMN%3Q~v=`957kA(07GJromCd7jFPZ;9O6-sOM(W_+OE{lu ziFLiyrw;DD#Qd$LSji>2tI@reBu`r{78OT(0o{8^_xcbq$BEb3JLuj^=buOrQ&yt8 z8r^#-r|5k#&N8~I(Y=?N8%T<6S;tN9fO{{!>!~3&)h>i}bnm5Iapt10x_POCdoP<; z^ij0-6zv6c?`0QWj1x@`r@I>6dzsniPGq$%-PP#cE9PGOD>9}oisr$+SDaUvCEUC74Rvtu6{c1zh08qY zu15D>ap%Sk;g}KH3+Ub}I?|2_J71=~fbP9=PW5GB)r3m+KDzhHbN%ANTvOlrlS z@<&a0n=0Ma=-w;uEHM?HUO{&?y7$VCz0N|fH`KCs(7ivJb2U(?&cU8Kxc5irRAPiu zducDAdw*ndEknrfj1!wj_x>m`R7}Vq@)&h+?~mFu8-xV^(p`=2y=rz{x6lEZlQa+R zz3S}nTcJhypQwX-uQD0;NATP7JFKI7uS#4QE!b*7djZ{hRr{fPf_bgUG!O2*dN%)d z!LY;Mse^m3KC5|D&^joUb#(95#!g*=GGAyfpnI=QhZc26R`Wd#^E`QY28`u$IlEd#_1Y8!Zse(Lx>EdrjN% z4gpth+6(C3Yi9|U2&liNyBghl?HRqF0#~m5VDF%NuQl?RCa@!klRCKf+W6R|0<)&k zU5)O&_C>*O{y#wz3@#&A8{P#A|UO@N$*l4COe}MB?HjnQ8 zar|Z_exv>z>fqiVzc^#aFLIvlYIN^)GbQc$4@KqCJh=C|)5d=MOUKe)K=)o}=o88J zLvAyhNB3SAmzc`eRz!C-y7#))vLe2MRrK={-TRXn9d&%+R+kf(%4WYe&?)^zC=QO_c-)JwOd#|4%x0G+9XefIJ-Fy8h zi!Ho^*>qQ8?ii-thc#B+vWga+(MC z-Z)+P1y9qze@-mA_r{Ypg*=&>d)Yj?_eTAYZ#;ojv=`95H^$s+=P})SfabxyH@4I* z;t_MTV;$Z5KmQ;2!E4{0zSBHPpv3JnDKh;~)dbL_& z8Fg^)Pos|xUroxXrw;D@=`+FFt6qy~FQ9vWHdS}hRUMNPY#!bFv*T_Huktq2UO@N$ zOgDPN)jj)9(>%EMXHf-=+0&M@S3dsxXXm1OZ<+F*|H@OoZ)_gjd&}|Z za#ylbX)mCAZ_(YTdnI_5GR=c~Z;3i(bH!Yn?rL=JEl(wKu1M6Xv3Ydw&!-rMT{*rz zlRCKf=f}K~uB>#Yy@2lhxlVlE-m6)WN;C9-IB;vZFHH)#%<^b+%2stX$Sc^WffFZ=YXq`Qkd-3+Uckn`L<} zZ?U_?=Fz>sm~6K9^3?9@)WN;KI2xeEHFAo+Z$bC|Li;WsS8X`m)#%<|L{_?UCI9AT z@1T2s(bTQO~^zvvgpnGpSy1JZDPur=3dvDjeH}BF- z0on`b-rFN;*Iu%`M|U;4_x8qt374ejTCjJ}y?0C+bL!Gbz1`Hoy>}d0#&c33;fqkHG!K8|tm~t@8r^$WxZpU>v@_Lg9^HFagZ5lbzsPIU!M%4+ zaJ|B5@b@5faPQrRZgX%7Nz-0H_uj4X;3VgPf+K7m-Fx?~r`(*2S2$4z_ugIq&gH^4 zOS-Gkz4uI*s(GQcmG%O<_nt!=VlO;6OnU*{dymEmw+mrGGVFbH?>)D~$}U)cp}m0a zy{BG3{(`KqJk5i9?;Y>ibK!JmGIen8y$55gO(>*SPwW z&7*to3zHT>j7}wCf%g-#Z&26P!b`9OTpU55C8y`2(JK)~0l|=5eHJU;HEZc#5zxMv+a$Ek5 zv~%syy|3Fl#dg=Jwd@@`bngO|9=6lP=x2@{x_A4BW;Vn6RWuLo-7aIMvQ4=MeU?G@ zZr3L=Y!elu$L8(Pz0Z$$YGYeSx12q?_jALgHc~D0_r`zRyV0IhnppTYKn6 z(>%C$hrnSCYc)5z4bZ(iRP1?eeIbf&ct>#W4qq%@Sg*_9$KG*7_r9sD((3P1x?#}0 zJBrT8vTA=ryAs{Iql?HPtGp@nGaTK!e6Spjq@Cyt?%k1QmucNfWUBa0NLGc*tG-Nj?* ztA$56-5%)PUGjJLTd3t&vw3vyE~DlR78e>CsDpcVT~Q`vv2KKJBy{ht+%wKtjG16Y z^WffHt%W(vJ66(-jqcqwIedkA-XXf>(7n5M3`Lj+{rm5NNB8bFYqzJlkvi=%bnk8_ z&CSjE9hS3S7u~y?PU$c6UAO2nGP-xSi0LoQr)Sg7MfdLZMCh5>@RKC=4!U=@zv1F$ z<->oegL`-97`kW{J&tYzbnotRyZ4)2TOP;e(Y?F-ny)pJ-v5I-xOeyB(ipR&Jbl!` zy}Q4k?q{}GjXrasd-qr?yukF69sLYP_wK(>%C$ul?qeOpJnaSV#Bn zrBw3Kg#X?Q>fqkJ0;lzu?5dHY4({EnTu9Mm`T%{0gYMny^DTbk;XiZ}qI>t=IC#>y zV(~ln4!U=55svM~(R_wJLxp=OlQx1Kt>YIPz9C_eMq?Bt zse^m>tr}c!*kMVZG10yI{@|Enm=`#X&7*tw+h+RPFepWYI=FW~iQ*AMqslwf!M*!= zOw%^x@BU34+`HccK}o}1KlrJGd-od&yJR?Hek|+g-u;&k)EKH|pTt{X3>DG?4c1U>)6iz)Zn$21oDirVj2s z;6&&{J)WN+6Xb%|Zf9lev4(>f5e3zVl!?zOZ|HIW?M`g8cao-nEvAeqy6i^JL z1f;vWyF)-irMnxXyE`QeBvnu;Q9i#MDi_R^gl-Fw3R+3ikNVstk|_nu&&TOcOh@;g_~6`#fqkfzAFXW`i$992lsw%ciKr?z4x^D zqI*B5_2{eZu{pH&qI*9V#_`y8)m97U9CYvJ&b!{U`OHsyFS_@0FFQJHZmHAWi|##r zEql35mBUv$2i$wQjM6@vh)@}(qkB*HN?T`Rl}mdsy7%<5N0~O_t+e-|dr!Z|5n;1+ zg!W!^?-}!4J#41ErM(y3dj@}px%DJF?Y-#UGwj%3ShsGb`vSW6j10w5>l8lPd(pjT zTu;-oc2zyY{Ql9sXZ(I7Zmn#0lRCKf%zYdOtoH|RXF9s~OnukQ*7LGGsDpdYjOxg@ zdeK6AFS_^43+%C0-9wEu5AHqlo#Gj*yf?F{gL}`~n8sq|J5!J8=-#sw9=^5G-&jK( z+U{RiWVu>bYDRC zp7ZgcpoOd7ICBoV_uOrJ_gW~YcTxxUo~wGY(qez(3hLnAa|7DXS?AV`trHd z!M*1do>VhGX7PnOxc9u#c2VdbD{(cX*hJ>McF z+pOyP9p-#=@A=6OBF!S6(%y^iJ->I4rmZy%*hkL8yYZ>7>~Wng{n@(2^o)+Ip7uUUcsTFCOrkrlio`i|)N} z&7Li$uC;VuK=)oK?NVT>e6^lAAKm-^{sr(j)BR6oQ3v;4ST=LH>AXL*_o90*ysN-! z@^U5Zz3ASH<|e;0>EUK$&O!HHbZlzEBwt#cI=J^D+dT#*z9zKyqI)k&cabyE_x{7o zqkAv9)^@|>Skg1<;NFXV&uBMURn12o+(7l)D%$R4ywe&l4KDzhPn{q#l=5xwX2lrmalJv~* zr8w=q=-$h?r>qTo3~28~_g-ea`?X=dha__jy7#g;=R1bJakTfMdoR1(+GD6+PWJ_L z?`0pS*Bc(|x=rVRdoSNAmt(li}`$3;egzgLI-YdT*oz$PNQ%vWBd#~Dg|BK#B z*Epu5d#}>m^;oYbiuPV~?^PjAH}&$1kJ3E2_p0Xe9eTc(BB_IWuX@2&rl)_0_Fi=F z)vM+9=^gt-_XTwC)lx|{daLJ$Fz29qulBf~srzLo?Y-#Ut4nu<>kbKDp?Prc)pwjc zbgQ*#??v}sGv~ayZlnwCz3AR+j=_*uP5QZ^d#`MyeNnqk=YV^!zv1YpHRM2hFS_@JX)PbM zszYZp^XT3ixLK#PB6Df)MfcudBx|B&-Aa2ey7z|IL`5x$k?C{}xc7!jlLA`X-o9lz zy7z|vcJ9?;Wv9It-FxE}$5op5H`CsW?!8gD<(y_4-wQei+|o0E2UYOJ=Qy%*hkbGO59^)G?+o*dnK^OxqA>O+~d_o91m*~x0B zUfuMQIUn77i-wH0dgM)c>fqj6f)gavtzXjKi|)OpX@XZ>l8yFWbnh+CcQmPQUoXX+ zgYNzODu)7f*24#=gL^+O*&L^KU*Q9FaPQ|mSeL7{S>0hey7%)XGOTK;{`;tddp|!O z|4z*dVY+xEsMtNO_sQ3vZi2#qI+*Y9WSI3HBFc~2i<#n#XSxco7M5u!M(RnZeOh;$wPZDy7!I+4m>K`WslK3 zxc3gh=8Y<>W>HK>_uk>aGEe#b*;CZPy?10ueOGQvIYb@Yd&gkBw{mLjM(W_+JO19Y zQg-X7y%*j41+MKH$|{d(??v~1!O&hznd{FQ<{WhI7h;-vlozbDrw;D@!lh~TO0T$R z??v~1;e%9;Qm-`az3ARAZjOsmDlnn@0=oB$N_V}L{7x$~=c9YS=(jyV$smdLUUcsl zYwdlN_^W8|MfZO3VUxYm>OMI-2i*ImrPFj3zdWL!8@l&PqEb?dLqBQnMfZNmCGMJH z&2sv=p?klSceh0`Y9H;r=-w|4Z!1)^kql&hU3Bl4r`zvVlr*Bf7v1~igH0j zW7{JIw;!g|!M%5?+uc-9UD`|?+fd%iE=r&73+Udve%Ni2vvH%n7u|dJp2qodk}>_vuZ!-zTl;T}-1ZXM z-_X5xhfAK3W9@XJd2sLDt+6(;_wRKv9o>8Pt2=LH+rH4=i|)N=-PXIZX^U8C9^8A6 ztZlEX`>u=B!M*o*H#Eqqij+_X_ufQz4uH=ZkJu)N_#K5_ul!j#WJs= zXzxY$-Yak?QKq-(8=V90z1M!Lzf8fU2h_p6_h#BU%J|)(y%*hk??8i|jKL@RxuJXS z{qtu^Mqobez3ATi_Dh<`tl3F>FS_?WgIGoBufnwVqI>U)z9S$#tVMe-y7#_|TlYxU zxX|8fWOzs44mCAA=5ggUtQYX`<7C117E z-iz-2n(5X8$=*@gd(pjLOR$ZTEO;-#oP+ND+LZ<$N&h)JsDpdI_UVtEq~R9Yd(pjL z-!7poDZo!ZH+1jU)nX(h*Qn9ni|+k;;P^p_uMV{LqI!N$V{?uln zL`^R3z3AQtR@D8Hh-#(17v1}S_^%fdHp8^{qI(~3mtd8Ud`tHQbngR2(eEU7%$mmh zy6D~q#_q67ux(bQ4(@$$)|SuWQ+!3#!MzV2v3V%ouJV)V=-vk{>j%Zt?C9r)?tL)j zcbm9-5beF_-Us`{OT|^QBj_A(?}Oi?lf}7Pq^W~@zp?vvfcSzT+I!Kx-_YLdbmG

k|StwD+QWzv(ruabis%?Y-#UZ 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 z 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 z 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