From 82a8432ea6c5b0886808bc5be7c6ba9013b29064 Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Wed, 14 Feb 2024 15:11:46 +0100 Subject: [PATCH] Move subscription Code to BSK (#2473) Task/Issue URL: https://app.asana.com/0/414235014887631/1206582087819536/f Description: Moves Subscriptions to BSK --- DuckDuckGo.xcodeproj/project.pbxproj | 122 ++------ .../xcshareddata/swiftpm/Package.resolved | 4 +- DuckDuckGo/AppDelegate.swift | 4 + DuckDuckGo/DesktopDownloadView.swift | 1 + DuckDuckGo/MainViewController+Segues.swift | 13 +- DuckDuckGo/MainViewController.swift | 4 + DuckDuckGo/SettingsHostingController.swift | 3 + DuckDuckGo/SettingsSubscriptionView.swift | 1 + DuckDuckGo/SettingsViewModel.swift | 33 ++- .../Subscription/AccountManager.swift | 211 ------------- .../AccountKeychainStorage.swift | 196 ------------ .../AccountStorage/AccountStorage.swift | 32 -- .../AppStoreAccountManagementFlow.swift | 57 ---- .../Flows/AppStore/AppStorePurchaseFlow.swift | 139 --------- .../Flows/AppStore/AppStoreRestoreFlow.swift | 93 ------ .../Subscription/Flows/PurchaseFlow.swift | 73 ----- .../Subscription/Subscription/Logging.swift | 57 ---- .../Subscription/PurchaseManager.swift | 278 ------------------ .../Subscription/Services/APIService.swift | 111 ------- .../Subscription/Services/AuthService.swift | 117 -------- .../Services/SubscriptionService.swift | 91 ------ .../SubscriptionPurchaseEnvironment.swift | 66 ----- .../Subscription/URL+Subscription.swift | 59 ---- ...IdentityTheftRestorationPagesFeature.swift | 1 + ...scriptionPagesUseSubscriptionFeature.swift | 1 + .../SubscriptionEmailViewModel.swift | 1 + .../ViewModel/SubscriptionFlowViewModel.swift | 4 +- .../ViewModel/SubscriptionITPViewModel.swift | 5 +- .../SubscriptionRestoreViewModel.swift | 1 + .../SubscriptionSettingsViewModel.swift | 3 +- .../SubscriptionDebugViewController.swift | 2 + LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/Subscription/.gitignore | 8 - LocalPackages/Subscription/Package.resolved | 104 ------- LocalPackages/Subscription/Package.swift | 27 -- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 37 files changed, 85 insertions(+), 1843 deletions(-) delete mode 100644 DuckDuckGo/Subscription/Subscription/AccountManager.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/AccountStorage/AccountKeychainStorage.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/AccountStorage/AccountStorage.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Flows/PurchaseFlow.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Logging.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/PurchaseManager.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Services/APIService.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Services/AuthService.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift delete mode 100644 DuckDuckGo/Subscription/Subscription/URL+Subscription.swift delete mode 100644 LocalPackages/Subscription/.gitignore delete mode 100644 LocalPackages/Subscription/Package.resolved delete mode 100644 LocalPackages/Subscription/Package.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5a4dbfe3b6..4902911bc7 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -394,7 +394,6 @@ 853A717820F645FB00FE60BC /* PixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853A717720F645FB00FE60BC /* PixelTests.swift */; }; 853C5F5B21BFF0AE001F7A05 /* HomeCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853C5F5A21BFF0AE001F7A05 /* HomeCollectionView.swift */; }; 853C5F6121C277C7001F7A05 /* global.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853C5F6021C277C7001F7A05 /* global.swift */; }; - 854007E72B57FC000001BD98 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 854007E62B57FC000001BD98 /* ZIPFoundation */; }; 8540BBA22440857A00017FE4 /* PreserveLoginsWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BBA12440857A00017FE4 /* PreserveLoginsWorker.swift */; }; 8540BD5223D8C2220057FDD2 /* PreserveLoginsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5123D8C2220057FDD2 /* PreserveLoginsTests.swift */; }; 8540BD5423D8D5080057FDD2 /* PreserveLoginsAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5323D8D5080057FDD2 /* PreserveLoginsAlert.swift */; }; @@ -765,6 +764,8 @@ CBDD5DE129A6741300832877 /* MockBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBDD5DE029A6741300832877 /* MockBundle.swift */; }; CBEFB9142AE0844700DEDE7B /* CriticalAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */; }; CBFCB30E2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */; }; + D61CDA162B7CF77300A0FBB9 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = D61CDA152B7CF77300A0FBB9 /* Subscription */; }; + D61CDA182B7CF78300A0FBB9 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = D61CDA172B7CF78300A0FBB9 /* ZIPFoundation */; }; D63657192A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */; }; D64648AD2B59936B0033090B /* SubscriptionEmailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64648AC2B59936B0033090B /* SubscriptionEmailView.swift */; }; D64648AF2B5993890033090B /* SubscriptionEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64648AE2B5993890033090B /* SubscriptionEmailViewModel.swift */; }; @@ -790,20 +791,6 @@ D69FBF762B28BE3600B505F1 /* SettingsSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */; }; D6BFCB5F2B7524AA0051FF81 /* SubscriptionPIRView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BFCB5E2B7524AA0051FF81 /* SubscriptionPIRView.swift */; }; D6BFCB612B7525160051FF81 /* SubscriptionPIRViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BFCB602B7525160051FF81 /* SubscriptionPIRViewModel.swift */; }; - D6D12C9F2B291CA90054390C /* URL+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8B2B291CA90054390C /* URL+Subscription.swift */; }; - D6D12CA02B291CA90054390C /* SubscriptionPurchaseEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8C2B291CA90054390C /* SubscriptionPurchaseEnvironment.swift */; }; - D6D12CA12B291CA90054390C /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8D2B291CA90054390C /* Logging.swift */; }; - D6D12CA22B291CA90054390C /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8E2B291CA90054390C /* AccountManager.swift */; }; - D6D12CA32B291CAA0054390C /* AccountKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C902B291CA90054390C /* AccountKeychainStorage.swift */; }; - D6D12CA42B291CAA0054390C /* AccountStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C912B291CA90054390C /* AccountStorage.swift */; }; - D6D12CA52B291CAA0054390C /* AppStorePurchaseFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C942B291CA90054390C /* AppStorePurchaseFlow.swift */; }; - D6D12CA62B291CAA0054390C /* AppStoreRestoreFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C952B291CA90054390C /* AppStoreRestoreFlow.swift */; }; - D6D12CA72B291CAA0054390C /* AppStoreAccountManagementFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C962B291CA90054390C /* AppStoreAccountManagementFlow.swift */; }; - D6D12CA82B291CAA0054390C /* PurchaseFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C972B291CA90054390C /* PurchaseFlow.swift */; }; - D6D12CAA2B291CAA0054390C /* SubscriptionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9B2B291CA90054390C /* SubscriptionService.swift */; }; - D6D12CAB2B291CAA0054390C /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9C2B291CA90054390C /* APIService.swift */; }; - D6D12CAC2B291CAA0054390C /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9D2B291CA90054390C /* AuthService.swift */; }; - D6D12CAD2B291CAA0054390C /* PurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9E2B291CA90054390C /* PurchaseManager.swift */; }; D6D95CE12B6D52DA00960317 /* RootPresentationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D95CE02B6D52DA00960317 /* RootPresentationMode.swift */; }; D6D95CE32B6D9F8800960317 /* AsyncHeadlessWebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D95CE22B6D9F8800960317 /* AsyncHeadlessWebViewModel.swift */; }; D6E0C1832B7A2B1E00D5E1E9 /* DesktopDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E0C1822B7A2B1E00D5E1E9 /* DesktopDownloadView.swift */; }; @@ -2451,20 +2438,6 @@ D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionView.swift; sourceTree = ""; }; D6BFCB5E2B7524AA0051FF81 /* SubscriptionPIRView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPIRView.swift; sourceTree = ""; }; D6BFCB602B7525160051FF81 /* SubscriptionPIRViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPIRViewModel.swift; sourceTree = ""; }; - D6D12C8B2B291CA90054390C /* URL+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+Subscription.swift"; sourceTree = ""; }; - D6D12C8C2B291CA90054390C /* SubscriptionPurchaseEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionPurchaseEnvironment.swift; sourceTree = ""; }; - D6D12C8D2B291CA90054390C /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; - D6D12C8E2B291CA90054390C /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = ""; }; - D6D12C902B291CA90054390C /* AccountKeychainStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountKeychainStorage.swift; sourceTree = ""; }; - D6D12C912B291CA90054390C /* AccountStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountStorage.swift; sourceTree = ""; }; - D6D12C942B291CA90054390C /* AppStorePurchaseFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStorePurchaseFlow.swift; sourceTree = ""; }; - D6D12C952B291CA90054390C /* AppStoreRestoreFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStoreRestoreFlow.swift; sourceTree = ""; }; - D6D12C962B291CA90054390C /* AppStoreAccountManagementFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStoreAccountManagementFlow.swift; sourceTree = ""; }; - D6D12C972B291CA90054390C /* PurchaseFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchaseFlow.swift; sourceTree = ""; }; - D6D12C9B2B291CA90054390C /* SubscriptionService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionService.swift; sourceTree = ""; }; - D6D12C9C2B291CA90054390C /* APIService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; - D6D12C9D2B291CA90054390C /* AuthService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; }; - D6D12C9E2B291CA90054390C /* PurchaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchaseManager.swift; sourceTree = ""; }; D6D95CE02B6D52DA00960317 /* RootPresentationMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootPresentationMode.swift; sourceTree = ""; }; D6D95CE22B6D9F8800960317 /* AsyncHeadlessWebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncHeadlessWebViewModel.swift; sourceTree = ""; }; D6E0C1822B7A2B1E00D5E1E9 /* DesktopDownloadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesktopDownloadView.swift; sourceTree = ""; }; @@ -2716,7 +2689,6 @@ F4D7F634298C00C3006C3AE9 /* FindInPageIOSJSSupport in Frameworks */, 85D598872927F84C00FA3B1B /* Crashes in Frameworks */, D664C7DD2B28A02800CBFA76 /* StoreKit.framework in Frameworks */, - 854007E72B57FC000001BD98 /* ZIPFoundation in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2787,12 +2759,14 @@ F486D33425069BBB002D07D7 /* Kingfisher in Frameworks */, EE8E568A2A56BCE400F11DCA /* NetworkProtection in Frameworks */, CBC83E3429B631780008E19C /* Configuration in Frameworks */, + D61CDA182B7CF78300A0FBB9 /* ZIPFoundation in Frameworks */, 98A16C2D28A11D6200A6C003 /* BrowserServicesKit in Frameworks */, 8599690F29D2F1C100DBF9FA /* DDGSync in Frameworks */, 1E60989F290011E600A508F9 /* PrivacyDashboard in Frameworks */, 851481882A600EFC00ABC65F /* RemoteMessaging in Frameworks */, 37DF000C29F9CA80002B7D3E /* SyncDataProviders in Frameworks */, 1E6098A1290011E600A508F9 /* UserScript in Frameworks */, + D61CDA162B7CF77300A0FBB9 /* Subscription in Frameworks */, C14882ED27F211A000D59F0C /* SwiftSoup in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4519,7 +4493,6 @@ D664C7AC2B289AA000CBFA76 /* Views */, D664C7B02B289AA000CBFA76 /* UserScripts */, D664C7962B289AA000CBFA76 /* Extensions */, - D6D12C8A2B291CA90054390C /* Subscription */, D65CEA6F2B6AC6C9008A759B /* Subscription.xcassets */, ); path = Subscription; @@ -4574,59 +4547,6 @@ path = UserScripts; sourceTree = ""; }; - D6D12C8A2B291CA90054390C /* Subscription */ = { - isa = PBXGroup; - children = ( - D6D12C8B2B291CA90054390C /* URL+Subscription.swift */, - D6D12C8C2B291CA90054390C /* SubscriptionPurchaseEnvironment.swift */, - D6D12C8D2B291CA90054390C /* Logging.swift */, - D6D12C8E2B291CA90054390C /* AccountManager.swift */, - D6D12C8F2B291CA90054390C /* AccountStorage */, - D6D12C922B291CA90054390C /* Flows */, - D6D12C9A2B291CA90054390C /* Services */, - D6D12C9E2B291CA90054390C /* PurchaseManager.swift */, - ); - path = Subscription; - sourceTree = ""; - }; - D6D12C8F2B291CA90054390C /* AccountStorage */ = { - isa = PBXGroup; - children = ( - D6D12C902B291CA90054390C /* AccountKeychainStorage.swift */, - D6D12C912B291CA90054390C /* AccountStorage.swift */, - ); - path = AccountStorage; - sourceTree = ""; - }; - D6D12C922B291CA90054390C /* Flows */ = { - isa = PBXGroup; - children = ( - D6D12C932B291CA90054390C /* AppStore */, - D6D12C972B291CA90054390C /* PurchaseFlow.swift */, - ); - path = Flows; - sourceTree = ""; - }; - D6D12C932B291CA90054390C /* AppStore */ = { - isa = PBXGroup; - children = ( - D6D12C942B291CA90054390C /* AppStorePurchaseFlow.swift */, - D6D12C952B291CA90054390C /* AppStoreRestoreFlow.swift */, - D6D12C962B291CA90054390C /* AppStoreAccountManagementFlow.swift */, - ); - path = AppStore; - sourceTree = ""; - }; - D6D12C9A2B291CA90054390C /* Services */ = { - isa = PBXGroup; - children = ( - D6D12C9B2B291CA90054390C /* SubscriptionService.swift */, - D6D12C9C2B291CA90054390C /* APIService.swift */, - D6D12C9D2B291CA90054390C /* AuthService.swift */, - ); - path = Services; - sourceTree = ""; - }; D6D95CE42B6DA3F200960317 /* AsyncHeadlessWebview */ = { isa = PBXGroup; children = ( @@ -5728,7 +5648,6 @@ F42D541C29DCA40B004C4FF1 /* DesignResourcesKit */, 0238E44E29C0FAA100615E30 /* FindInPageIOSJSSupport */, 4B2754EB29E8C7DF00394032 /* Lottie */, - 854007E62B57FC000001BD98 /* ZIPFoundation */, ); productName = DuckDuckGo; productReference = 84E341921E2F7EFB00BDBA6F /* DuckDuckGo.app */; @@ -5910,6 +5829,8 @@ 37DF000B29F9CA80002B7D3E /* SyncDataProviders */, 851481872A600EFC00ABC65F /* RemoteMessaging */, EE8E56892A56BCE400F11DCA /* NetworkProtection */, + D61CDA152B7CF77300A0FBB9 /* Subscription */, + D61CDA172B7CF78300A0FBB9 /* ZIPFoundation */, ); productName = Core; productReference = F143C2E41E4A4CD400CFDE3A /* Core.framework */; @@ -6508,7 +6429,6 @@ 1E24295E293F57FA00584836 /* LottieView.swift in Sources */, 8577A1C5255D2C0D00D43FCD /* HitTestingToolbar.swift in Sources */, 4BB697A42B1D99C4003699B5 /* VPNWaitlistActivationDateStore.swift in Sources */, - D6D12CAD2B291CAA0054390C /* PurchaseManager.swift in Sources */, 853C5F5B21BFF0AE001F7A05 /* HomeCollectionView.swift in Sources */, 3132FA2627A0784600DD7A12 /* FilePreviewHelper.swift in Sources */, 9820FF502244FECC008D4782 /* UIScrollViewExtension.swift in Sources */, @@ -6532,7 +6452,6 @@ D6E83C122B1E6AB3006C8AFB /* SettingsView.swift in Sources */, F1668BCE1E798081008CBA04 /* BookmarksViewController.swift in Sources */, 8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */, - D6D12CA62B291CAA0054390C /* AppStoreRestoreFlow.swift in Sources */, C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */, D668D9272B6937D2008E2FF2 /* SubscriptionITPViewModel.swift in Sources */, F1CA3C371F045878005FADB3 /* PrivacyStore.swift in Sources */, @@ -6555,9 +6474,7 @@ D664C7CE2B289AA200CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift in Sources */, EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */, AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */, - D6D12CA42B291CAA0054390C /* AccountStorage.swift in Sources */, D6E83C2E2B1EA06E006C8AFB /* SettingsViewModel.swift in Sources */, - D6D12CA32B291CAA0054390C /* AccountKeychainStorage.swift in Sources */, 8590CB612684D0600089F6BF /* CookieDebugViewController.swift in Sources */, 319A37152829A55F0079FBCE /* AutofillListItemTableViewCell.swift in Sources */, 1EA513782866039400493C6A /* TrackerAnimationLogic.swift in Sources */, @@ -6597,7 +6514,6 @@ 020108A929A7C1CD00644F9D /* AppTrackerImageCache.swift in Sources */, 4B78074E2B183A1F009DB2CF /* SurveyURLBuilder.swift in Sources */, 3132FA2A27A0788F00DD7A12 /* QuickLookPreviewHelper.swift in Sources */, - D6D12CA82B291CAA0054390C /* PurchaseFlow.swift in Sources */, C1D21E2D293A5965006E5A05 /* AutofillLoginSession.swift in Sources */, 4B53648A26718D0E001AA041 /* EmailWaitlist.swift in Sources */, 027F48762A4B5FBE001A1C6C /* AppTPLinkButton.swift in Sources */, @@ -6627,7 +6543,6 @@ 8505836F219F424500ED4EDB /* UIViewExtension.swift in Sources */, D6F93E3C2B4FFA97004C268D /* SubscriptionDebugViewController.swift in Sources */, 8505836E219F424500ED4EDB /* RoundedRectangleView.swift in Sources */, - D6D12CA12B291CA90054390C /* Logging.swift in Sources */, EE8594992A44791C008A6D06 /* NetworkProtectionTunnelController.swift in Sources */, 1EEF123F2850A68A003DDE57 /* PrivacyInfoContainerView.swift in Sources */, F4B0B796252CB35700830156 /* OnboardingWidgetsDetailsViewController.swift in Sources */, @@ -6671,7 +6586,6 @@ D652498E2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift in Sources */, 8548D95E25262B1B005AAE49 /* ViewHighlighter.swift in Sources */, F4D7221026F29A70007D6193 /* BookmarkDetailsCell.swift in Sources */, - D6D12CA22B291CA90054390C /* AccountManager.swift in Sources */, F1617C131E572E0300DEDCAF /* TabSwitcherViewController.swift in Sources */, 83BE9BC3215D69C1009844D9 /* AppConfigurationFetch.swift in Sources */, 1EEC460627A9499600E75FCB /* DownloadsList.swift in Sources */, @@ -6679,7 +6593,6 @@ 85B9CB8921AEBDD5009001F1 /* FavoriteHomeCell.swift in Sources */, 98999D5922FDA41500CBBE1B /* BasicAuthenticationAlert.swift in Sources */, C13B32D22A0E750700A59236 /* AutofillSettingStatus.swift in Sources */, - D6D12CA52B291CAA0054390C /* AppStorePurchaseFlow.swift in Sources */, D68DF81E2B5830380023DBEA /* SubscriptionRestoreViewModel.swift in Sources */, F4F6DFB426E6B63700ED7E12 /* BookmarkFolderCell.swift in Sources */, D6F93E3E2B50A8A0004C268D /* SubscriptionSettingsView.swift in Sources */, @@ -6689,7 +6602,6 @@ 3157B43327F497E90042D3D7 /* SaveLoginView.swift in Sources */, F17922E01E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift in Sources */, 0290472529E8496A0008FE3C /* AppTPActivityIconView.swift in Sources */, - D6D12CA72B291CAA0054390C /* AppStoreAccountManagementFlow.swift in Sources */, D664C7C82B289AA200CBFA76 /* SubscriptionFlowView.swift in Sources */, EE458D142ABB652900FC651A /* NetworkProtectionDebugUtilities.swift in Sources */, 8528AE7C212EF4A200D0BD74 /* AppRatingPrompt.swift in Sources */, @@ -6733,7 +6645,6 @@ 4BBBBA8D2B031B4200D965DA /* VPNWaitlistDebugViewController.swift in Sources */, C160544129D6044D00B715A1 /* AutofillInterfaceUsernameTruncator.swift in Sources */, 02A54A9A2A094A17000C8FED /* AppTPHomeView.swift in Sources */, - D6D12C9F2B291CA90054390C /* URL+Subscription.swift in Sources */, 31C70B5528045E3500FB6AD1 /* SecureVaultErrorReporter.swift in Sources */, F4CE6D1B257EA33C00D0A6AA /* FireButtonAnimator.swift in Sources */, 85582E0029D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift in Sources */, @@ -6754,7 +6665,6 @@ 85047C772A0D5D3D00D2FF3F /* SyncSettingsViewController+SyncDelegate.swift in Sources */, 85DDE0402AC6FF65006ABCA2 /* MainView.swift in Sources */, 980891A72237D5D800313A70 /* FeedbackPresenter.swift in Sources */, - D6D12CAB2B291CAA0054390C /* APIService.swift in Sources */, 989B337522D7EF2100437824 /* EmptyCollectionReusableView.swift in Sources */, 8524CC94246C5C8900E59D45 /* DaxDialogViewController.swift in Sources */, F42EF9312614BABE00101FB9 /* ActionSheetDaxDialogViewController.swift in Sources */, @@ -6785,7 +6695,6 @@ 3158461A281B08F5004ADB8B /* AutofillLoginListViewModel.swift in Sources */, 31C138A827A3E9C900FFD4B2 /* URLDownloadSession.swift in Sources */, 981FED76220464EF008488D7 /* AutoClearSettingsModel.swift in Sources */, - D6D12CA02B291CA90054390C /* SubscriptionPurchaseEnvironment.swift in Sources */, 83004E882193E8C700DA013C /* TabViewControllerLongPressMenuExtension.swift in Sources */, 98F78B8E22419093007CACF4 /* ThemableNavigationController.swift in Sources */, CBD4F140279EBFB300B20FD7 /* SwiftUICollectionViewCell.swift in Sources */, @@ -6859,7 +6768,6 @@ 9872D205247DCAC100CEF398 /* TabPreviewsSource.swift in Sources */, F130D73A1E5776C500C45811 /* OmniBarDelegate.swift in Sources */, 85DFEDEF24C7EA3B00973FE7 /* SmallOmniBarState.swift in Sources */, - D6D12CAC2B291CAA0054390C /* AuthService.swift in Sources */, 1E908BF129827C480008C8F3 /* AutoconsentUserScript.swift in Sources */, 4B0295192537BC6700E00CEF /* ConfigurationDebugViewController.swift in Sources */, 1E7A71192934EC6100B7EA19 /* OmniBarNotificationContainerView.swift in Sources */, @@ -6949,7 +6857,6 @@ F4F6DFB626E6B71300ED7E12 /* BookmarkFoldersTableViewController.swift in Sources */, 8586A11024CCCD040049720E /* TabsBarViewController.swift in Sources */, F1D796F41E7C2A410019D451 /* BookmarksDelegate.swift in Sources */, - D6D12CAA2B291CAA0054390C /* SubscriptionService.swift in Sources */, D664C7B92B289AA200CBFA76 /* WKUserContentController+Handler.swift in Sources */, C1B7B52428941F2A0098FD6A /* RemoteMessageRequest.swift in Sources */, EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */, @@ -9985,7 +9892,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 106.0.1; + version = 107.0.0; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { @@ -10112,11 +10019,6 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = RemoteMessaging; }; - 854007E62B57FC000001BD98 /* ZIPFoundation */ = { - isa = XCSwiftPackageProductDependency; - package = 854007E52B57FB020001BD98 /* XCRemoteSwiftPackageReference "ZIPFoundation" */; - productName = ZIPFoundation; - }; 85875B6029912A9900115F05 /* SyncUI */ = { isa = XCSwiftPackageProductDependency; productName = SyncUI; @@ -10206,6 +10108,16 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + D61CDA152B7CF77300A0FBB9 /* Subscription */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Subscription; + }; + D61CDA172B7CF78300A0FBB9 /* ZIPFoundation */ = { + isa = XCSwiftPackageProductDependency; + package = 854007E52B57FB020001BD98 /* XCRemoteSwiftPackageReference "ZIPFoundation" */; + productName = ZIPFoundation; + }; EE8E56892A56BCE400F11DCA /* NetworkProtection */ = { isa = XCSwiftPackageProductDependency; package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7e74a98bef..ae8e29e12e 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "3f5e33ec3d75dd2c130cfc6915c0a6e8efeb96f1", - "version" : "106.0.1" + "revision" : "328ce451fd1593809d1470ab5a0b5242a595f88c", + "version" : "107.0.0" } }, { diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index f84dedc923..c9bfe95a32 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -34,6 +34,10 @@ import Networking import DDGSync import SyncDataProviders +#if SUBSCRIPTION +import Subscription +#endif + #if NETWORK_PROTECTION import NetworkProtection import WebKit diff --git a/DuckDuckGo/DesktopDownloadView.swift b/DuckDuckGo/DesktopDownloadView.swift index f089c1b21b..42d43e3fc2 100644 --- a/DuckDuckGo/DesktopDownloadView.swift +++ b/DuckDuckGo/DesktopDownloadView.swift @@ -63,6 +63,7 @@ struct DesktopDownloadView: View { } } ) + // XAI: Move all strings to a Constants enum at the top .buttonStyle(DesktopDownloadViewButtonStyle(enabled: true)) .padding(.horizontal, padding) .padding(.top, 24) diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index 94b4be4591..c1e8a9560f 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -23,7 +23,10 @@ import Core import Bookmarks import BrowserServicesKit import SwiftUI -import PrivacyDashboard + +#if SUBSCRIPTION +import Subscription +#endif extension MainViewController { @@ -235,15 +238,19 @@ extension MainViewController { syncDataProviders: syncDataProviders, appSettings: appSettings, bookmarksDatabase: bookmarksDatabase) - +#if SUBSCRIPTION let settingsViewModel = SettingsViewModel(legacyViewProvider: legacyViewProvider, accountManager: AccountManager()) +#else + let settingsViewModel = SettingsViewModel(legacyViewProvider: legacyViewProvider) +#endif + let settingsController = SettingsHostingController(viewModel: settingsViewModel, viewProvider: legacyViewProvider) settingsController.applyTheme(ThemeManager.shared.currentTheme) // We are still presenting legacy views, so use a Navcontroller let navController = UINavigationController(rootViewController: settingsController) navController.applyTheme(ThemeManager.shared.currentTheme) - settingsController.modalPresentationStyle = .automatic + settingsController.modalPresentationStyle = UIModalPresentationStyle.automatic present(navController, animated: true) { completion?(settingsViewModel) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 697640fce6..8799d48c8b 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -31,6 +31,10 @@ import Persistence import PrivacyDashboard import Networking +#if SUBSCRIPTION +import Subscription +#endif + #if NETWORK_PROTECTION import NetworkProtection #endif diff --git a/DuckDuckGo/SettingsHostingController.swift b/DuckDuckGo/SettingsHostingController.swift index 8bbce4186e..7bf9e3c242 100644 --- a/DuckDuckGo/SettingsHostingController.swift +++ b/DuckDuckGo/SettingsHostingController.swift @@ -19,6 +19,9 @@ import UIKit import SwiftUI +#if SUBSCRIPTION +import Subscription +#endif class SettingsHostingController: UIHostingController { var viewModel: SettingsViewModel diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index d03b65ce41..02b5acda25 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -21,6 +21,7 @@ import SwiftUI import UIKit #if SUBSCRIPTION +import Subscription @available(iOS 15.0, *) struct SettingsSubscriptionView: View { diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index f4f426b1fe..0e79e5475e 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -25,6 +25,10 @@ import Common import Combine import SyncUI +#if SUBSCRIPTION +import Subscription +#endif + #if APP_TRACKING_PROTECTION import NetworkExtension #endif @@ -42,9 +46,12 @@ final class SettingsViewModel: ObservableObject { private lazy var animator: FireButtonAnimator = FireButtonAnimator(appSettings: AppUserDefaults()) private var legacyViewProvider: SettingsLegacyViewProvider private lazy var versionProvider: AppVersion = AppVersion.shared - private var accountManager: AccountManager private let voiceSearchHelper: VoiceSearchHelperProtocol - +#if SUBSCRIPTION + private var accountManager: AccountManager +#endif + + #if NETWORK_PROTECTION private let connectionObserver = ConnectionStatusObserverThroughSession() #endif @@ -56,7 +63,7 @@ final class SettingsViewModel: ObservableObject { // Defaults @UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false) static private var cachedHasActiveSubscription: Bool - + // Closures to interact with legacy view controllers through the container var onRequestPushLegacyView: ((UIViewController) -> Void)? var onRequestPresentLegacyView: ((UIViewController, _ modal: Bool) -> Void)? @@ -64,7 +71,7 @@ final class SettingsViewModel: ObservableObject { var onRequestDismissSettings: (() -> Void)? // SwiftUI Programatic Navigation Variables - // Add more views as needed here... + // Add more views as needed here... @Published var shouldNavigateToDBP = false @Published var shouldNavigateToITP = false @@ -86,7 +93,7 @@ final class SettingsViewModel: ObservableObject { case networkProtection #endif } - + var shouldShowNoMicrophonePermissionAlert: Bool = false // Used to automatically navigate on Appear to a specific section @@ -194,7 +201,7 @@ final class SettingsViewModel: ObservableObject { } ) } - +#if SUBSCRIPTION // MARK: Default Init init(state: SettingsState? = nil, legacyViewProvider: SettingsLegacyViewProvider, @@ -207,8 +214,20 @@ final class SettingsViewModel: ObservableObject { self.voiceSearchHelper = voiceSearchHelper self.onAppearNavigationTarget = navigateOnAppearDestination } +#else + // MARK: Default Init + init(state: SettingsState? = nil, + legacyViewProvider: SettingsLegacyViewProvider, + voiceSearchHelper: VoiceSearchHelperProtocol = AppDependencyProvider.shared.voiceSearchHelper, + navigateOnAppearDestination: SettingsSection = .none) { + self.state = SettingsState.defaults + self.legacyViewProvider = legacyViewProvider + self.voiceSearchHelper = voiceSearchHelper + self.onAppearNavigationTarget = navigateOnAppearDestination + } +#endif + } - // MARK: Private methods extension SettingsViewModel { diff --git a/DuckDuckGo/Subscription/Subscription/AccountManager.swift b/DuckDuckGo/Subscription/Subscription/AccountManager.swift deleted file mode 100644 index 60b87a9d65..0000000000 --- a/DuckDuckGo/Subscription/Subscription/AccountManager.swift +++ /dev/null @@ -1,211 +0,0 @@ -// -// AccountManager.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Common - -public extension Notification.Name { - static let accountDidSignIn = Notification.Name("com.duckduckgo.browserServicesKit.AccountDidSignIn") - static let accountDidSignOut = Notification.Name("com.duckduckgo.browserServicesKit.AccountDidSignOut") -} - -public protocol AccountManagerKeychainAccessDelegate: AnyObject { - func accountManagerKeychainAccessFailed(accessType: AccountKeychainAccessType, error: AccountKeychainAccessError) -} - -public class AccountManager { - - private let storage: AccountStorage - public weak var delegate: AccountManagerKeychainAccessDelegate? - - public var isUserAuthenticated: Bool { - return accessToken != nil - } - - public init(storage: AccountStorage = AccountKeychainStorage()) { - self.storage = storage - } - - public var authToken: String? { - do { - return try storage.getAuthToken() - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .getAuthToken, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - - return nil - } - } - - public var accessToken: String? { - do { - return try storage.getAccessToken() - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .getAccessToken, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - - return nil - } - } - - public var email: String? { - do { - return try storage.getEmail() - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .getEmail, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - - return nil - } - } - - public var externalID: String? { - do { - return try storage.getExternalID() - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .getExternalID, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - - return nil - } - } - - public func storeAuthToken(token: String) { - do { - try storage.store(authToken: token) - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .storeAuthToken, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - } - } - - public func storeAccount(token: String, email: String?, externalID: String?) { - do { - try storage.store(accessToken: token) - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .storeAccessToken, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - } - - do { - try storage.store(email: email) - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .storeEmail, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - } - - do { - try storage.store(externalID: externalID) - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .storeExternalID, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - } - NotificationCenter.default.post(name: .accountDidSignIn, object: self, userInfo: nil) - } - - public func signOut() { - do { - try storage.clearAuthenticationState() - } catch { - if let error = error as? AccountKeychainAccessError { - delegate?.accountManagerKeychainAccessFailed(accessType: .clearAuthenticationData, error: error) - } else { - assertionFailure("Expected AccountKeychainAccessError") - } - } - - NotificationCenter.default.post(name: .accountDidSignOut, object: self, userInfo: nil) - } - - // MARK: - - - public func hasEntitlement(for name: String) async -> Bool { - await fetchEntitlements().contains(name) - } - - public func fetchEntitlements() async -> [String] { - guard let accessToken else { return [] } - - switch await AuthService.validateToken(accessToken: accessToken) { - case .success(let response): - let entitlements = response.account.entitlements - return entitlements.map { $0.name } - - case .failure(let error): - os_log("AccountManager error: %{public}@", log: .error, error.localizedDescription) - return [] - } - } - - public func exchangeAuthTokenToAccessToken(_ authToken: String) async -> Result { - switch await AuthService.getAccessToken(token: authToken) { - case .success(let response): - return .success(response.accessToken) - case .failure(let error): - os_log("AccountManager error: %{public}@", log: .error, error.localizedDescription) - return .failure(error) - } - } - - public typealias AccountDetails = (email: String?, externalID: String) - - public func fetchAccountDetails(with accessToken: String) async -> Result { - switch await AuthService.validateToken(accessToken: accessToken) { - case .success(let response): - return .success(AccountDetails(email: response.account.email, externalID: response.account.externalID)) - case .failure(let error): - os_log("AccountManager error: %{public}@", log: .error, error.localizedDescription) - return .failure(error) - } - } - - public func checkSubscriptionState() async { - guard let token = accessToken else { return } - - if case .success(let response) = await SubscriptionService.getSubscriptionDetails(token: token) { - if !response.isSubscriptionActive { - signOut() - } - } - } -} diff --git a/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountKeychainStorage.swift b/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountKeychainStorage.swift deleted file mode 100644 index 5fc79bc636..0000000000 --- a/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountKeychainStorage.swift +++ /dev/null @@ -1,196 +0,0 @@ -// -// AccountKeychainStorage.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public enum AccountKeychainAccessType: String { - case getAuthToken - case storeAuthToken - case getAccessToken - case storeAccessToken - case getEmail - case storeEmail - case getExternalID - case storeExternalID - case clearAuthenticationData -} - -public enum AccountKeychainAccessError: Error, Equatable { - case failedToDecodeKeychainValueAsData - case failedToDecodeKeychainDataAsString - case keychainSaveFailure(OSStatus) - case keychainDeleteFailure(OSStatus) - case keychainLookupFailure(OSStatus) - - public var errorDescription: String { - switch self { - case .failedToDecodeKeychainValueAsData: return "failedToDecodeKeychainValueAsData" - case .failedToDecodeKeychainDataAsString: return "failedToDecodeKeychainDataAsString" - case .keychainSaveFailure: return "keychainSaveFailure" - case .keychainDeleteFailure: return "keychainDeleteFailure" - case .keychainLookupFailure: return "keychainLookupFailure" - } - } -} - -public class AccountKeychainStorage: AccountStorage { - - public init() {} - - public func getAuthToken() throws -> String? { - try Self.getString(forField: .authToken) - } - - public func store(authToken: String) throws { - try Self.set(string: authToken, forField: .authToken) - } - - public func getAccessToken() throws -> String? { - try Self.getString(forField: .accessToken) - } - - public func store(accessToken: String) throws { - try Self.set(string: accessToken, forField: .accessToken) - } - - public func getEmail() throws -> String? { - try Self.getString(forField: .email) - } - - public func getExternalID() throws -> String? { - try Self.getString(forField: .externalID) - } - - public func store(externalID: String?) throws { - if let externalID = externalID, !externalID.isEmpty { - try Self.set(string: externalID, forField: .externalID) - } else { - try Self.deleteItem(forField: .externalID) - } - } - - public func store(email: String?) throws { - if let email = email, !email.isEmpty { - try Self.set(string: email, forField: .email) - } else { - try Self.deleteItem(forField: .email) - } - } - - public func clearAuthenticationState() throws { - try Self.deleteItem(forField: .authToken) - try Self.deleteItem(forField: .accessToken) - try Self.deleteItem(forField: .email) - try Self.deleteItem(forField: .externalID) - } - -} - -private extension AccountKeychainStorage { - - /* - Uses just kSecAttrService as the primary key, since we don't want to store - multiple accounts/tokens at the same time - */ - enum AccountKeychainField: String, CaseIterable { - case authToken = "account.authToken" - case accessToken = "account.accessToken" - case email = "account.email" - case externalID = "account.external_id" - - var keyValue: String { - (Bundle.main.bundleIdentifier ?? "com.duckduckgo") + "." + rawValue - } - } - - static func getString(forField field: AccountKeychainField) throws -> String? { - guard let data = try retrieveData(forField: field) else { - return nil - } - - if let decodedString = String(data: data, encoding: String.Encoding.utf8) { - return decodedString - } else { - throw AccountKeychainAccessError.failedToDecodeKeychainDataAsString - } - } - - static func retrieveData(forField field: AccountKeychainField, useDataProtectionKeychain: Bool = true) throws -> Data? { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecMatchLimit as String: kSecMatchLimitOne, - kSecAttrService as String: field.keyValue, - kSecReturnData as String: true, - kSecUseDataProtectionKeychain as String: useDataProtectionKeychain - ] - - var item: CFTypeRef? - let status = SecItemCopyMatching(query as CFDictionary, &item) - - if status == errSecSuccess { - if let existingItem = item as? Data { - return existingItem - } else { - throw AccountKeychainAccessError.failedToDecodeKeychainValueAsData - } - } else if status == errSecItemNotFound { - return nil - } else { - throw AccountKeychainAccessError.keychainLookupFailure(status) - } - } - - static func set(string: String, forField field: AccountKeychainField) throws { - guard let stringData = string.data(using: .utf8) else { - return - } - - try deleteItem(forField: field) - try store(data: stringData, forField: field) - } - - static func store(data: Data, forField field: AccountKeychainField, useDataProtectionKeychain: Bool = true) throws { - let query = [ - kSecClass: kSecClassGenericPassword, - kSecAttrSynchronizable: false, - kSecAttrService: field.keyValue, - kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock, - kSecValueData: data, - kSecUseDataProtectionKeychain: useDataProtectionKeychain] as [String: Any] - - let status = SecItemAdd(query as CFDictionary, nil) - - if status != errSecSuccess { - throw AccountKeychainAccessError.keychainSaveFailure(status) - } - } - - static func deleteItem(forField field: AccountKeychainField, useDataProtectionKeychain: Bool = true) throws { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: field.keyValue, - kSecUseDataProtectionKeychain as String: useDataProtectionKeychain] - - let status = SecItemDelete(query as CFDictionary) - - if status != errSecSuccess && status != errSecItemNotFound { - throw AccountKeychainAccessError.keychainDeleteFailure(status) - } - } -} diff --git a/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountStorage.swift b/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountStorage.swift deleted file mode 100644 index 9a1c98d6b7..0000000000 --- a/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountStorage.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// AccountStorage.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public protocol AccountStorage: AnyObject { - func getAuthToken() throws -> String? - func store(authToken: String) throws - func getAccessToken() throws -> String? - func store(accessToken: String) throws - func getEmail() throws -> String? - func store(email: String?) throws - func getExternalID() throws -> String? - func store(externalID: String?) throws - func clearAuthenticationState() throws -} diff --git a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift deleted file mode 100644 index 4c199acf20..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// AppStoreAccountManagementFlow.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import StoreKit - -public final class AppStoreAccountManagementFlow { - - public enum Error: Swift.Error { - case noPastTransaction - case authenticatingWithTransactionFailed - } - - @discardableResult - public static func refreshAuthTokenIfNeeded() async -> Result { - var authToken = AccountManager().authToken ?? "" - - // Check if auth token if still valid - if case let .failure(error) = await AuthService.validateToken(accessToken: authToken) { - print(error) - - if #available(macOS 12.0, iOS 15.0, *) { - // In case of invalid token attempt store based authentication to obtain a new one - guard let lastTransactionJWSRepresentation = await PurchaseManager.mostRecentTransaction() else { - return .failure(.noPastTransaction) } - - switch await AuthService.storeLogin(signature: lastTransactionJWSRepresentation) { - case .success(let response): - if response.externalID == AccountManager().externalID { - authToken = response.authToken - AccountManager().storeAuthToken(token: authToken) - } - case .failure: - return .failure(.authenticatingWithTransactionFailed) - } - } - } - - return .success(authToken) - } -} diff --git a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift deleted file mode 100644 index 28a3c0cfdd..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// AppStorePurchaseFlow.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import StoreKit - -@available(macOS 12.0, iOS 15.0, *) -public final class AppStorePurchaseFlow { - - public enum Error: Swift.Error { - case noProductsFound - - case activeSubscriptionAlreadyPresent - case authenticatingWithTransactionFailed - case accountCreationFailed - case purchaseFailed - - case missingEntitlements - - case somethingWentWrong - } - - public static func subscriptionOptions() async -> Result { - - let products = PurchaseManager.shared.availableProducts - - let monthly = products.first(where: { $0.id.contains("1month") }) - let yearly = products.first(where: { $0.id.contains("1year") }) - - guard let monthly, let yearly else { return .failure(.noProductsFound) } - - let options = [SubscriptionOption(id: monthly.id, cost: .init(displayPrice: monthly.displayPrice, recurrence: "monthly")), - SubscriptionOption(id: yearly.id, cost: .init(displayPrice: yearly.displayPrice, recurrence: "yearly"))] - - let features = SubscriptionFeatureName.allCases.map { SubscriptionFeature(name: $0.rawValue) } - - return .success(SubscriptionOptions(platform: SubscriptionPlatformName.macos.rawValue, - options: options, - features: features)) - } - - public static func purchaseSubscription(with subscriptionIdentifier: String, emailAccessToken: String?) async -> Result { - let accountManager = AccountManager() - let externalID: String - - // Check for past transactions most recent - switch await AppStoreRestoreFlow.restoreAccountFromPastPurchase() { - case .success: - return .failure(.activeSubscriptionAlreadyPresent) - case .failure(let error): - switch error { - case .subscriptionExpired(let expiredAccountDetails): - externalID = expiredAccountDetails.externalID - accountManager.storeAuthToken(token: expiredAccountDetails.authToken) - accountManager.storeAccount(token: expiredAccountDetails.accessToken, - email: expiredAccountDetails.email, - externalID: expiredAccountDetails.externalID) - case .missingAccountOrTransactions, .pastTransactionAuthenticationError: - // No history, create new account - switch await AuthService.createAccount(emailAccessToken: emailAccessToken) { - case .success(let response): - externalID = response.externalID - - if case let .success(accessToken) = await accountManager.exchangeAuthTokenToAccessToken(response.authToken), - case let .success(accountDetails) = await accountManager.fetchAccountDetails(with: accessToken) { - accountManager.storeAuthToken(token: response.authToken) - accountManager.storeAccount(token: accessToken, - email: accountDetails.email, - externalID: accountDetails.externalID) - } - case .failure: - return .failure(.accountCreationFailed) - } - default: - return .failure(.authenticatingWithTransactionFailed) - } - } - - // Make the purchase - switch await PurchaseManager.shared.purchaseSubscription(with: subscriptionIdentifier, externalID: externalID) { - case .success: - return .success(()) - case .failure(let error): - print("Something went wrong, reason: \(error)") - AccountManager().signOut() - return .failure(.purchaseFailed) - } - } - - @discardableResult - public static func completeSubscriptionPurchase() async -> Result { - - let result = await checkForEntitlements(wait: 2.0, retry: 10) - - return result ? .success(PurchaseUpdate(type: "completed")) : .failure(.missingEntitlements) - } - - @discardableResult - public static func checkForEntitlements(wait waitTime: Double, retry retryCount: Int) async -> Bool { - var count = 0 - var hasEntitlements = false - - repeat { - hasEntitlements = await !AccountManager().fetchEntitlements().isEmpty - - if hasEntitlements { - break - } else { - count += 1 - try? await Task.sleep(seconds: waitTime) - } - } while !hasEntitlements && count < retryCount - - return hasEntitlements - } -} - -extension Task where Success == Never, Failure == Never { - static func sleep(seconds: Double) async throws { - let duration = UInt64(seconds * 1_000_000_000) - try await Task.sleep(nanoseconds: duration) - } -} diff --git a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift deleted file mode 100644 index de10a8f3ab..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// AppStoreRestoreFlow.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import StoreKit - -@available(macOS 12.0, iOS 15.0, *) -public final class AppStoreRestoreFlow { - - // swiftlint:disable:next large_tuple - public typealias RestoredAccountDetails = (authToken: String, accessToken: String, externalID: String, email: String?) - - public enum Error: Swift.Error { - case missingAccountOrTransactions - case pastTransactionAuthenticationError - case failedToObtainAccessToken - case failedToFetchAccountDetails - case failedToFetchSubscriptionDetails - case subscriptionExpired(accountDetails: RestoredAccountDetails) - case somethingWentWrong - } - - public static func restoreAccountFromPastPurchase() async -> Result { - guard let lastTransactionJWSRepresentation = await PurchaseManager.mostRecentTransaction() else { - return .failure(.missingAccountOrTransactions) - } - - let accountManager = AccountManager() - - // Do the store login to get short-lived token - let authToken: String - - switch await AuthService.storeLogin(signature: lastTransactionJWSRepresentation) { - case .success(let response): - authToken = response.authToken - case .failure: - return .failure(.pastTransactionAuthenticationError) - } - - let accessToken: String - let email: String? - let externalID: String - - switch await accountManager.exchangeAuthTokenToAccessToken(authToken) { - case .success(let exchangedAccessToken): - accessToken = exchangedAccessToken - case .failure: - return .failure(.failedToObtainAccessToken) - } - - switch await accountManager.fetchAccountDetails(with: accessToken) { - case .success(let accountDetails): - email = accountDetails.email - externalID = accountDetails.externalID - case .failure: - return .failure(.failedToFetchAccountDetails) - } - - var isSubscriptionActive = false - - switch await SubscriptionService.getSubscriptionDetails(token: accessToken) { - case .success(let response): - isSubscriptionActive = response.isSubscriptionActive - case .failure: - return .failure(.somethingWentWrong) - } - - if isSubscriptionActive { - accountManager.storeAuthToken(token: authToken) - accountManager.storeAccount(token: accessToken, email: email, externalID: externalID) - return .success(()) - } else { - let details = RestoredAccountDetails(authToken: authToken, accessToken: accessToken, externalID: externalID, email: email) - return .failure(.subscriptionExpired(accountDetails: details)) - } - } -} diff --git a/DuckDuckGo/Subscription/Subscription/Flows/PurchaseFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/PurchaseFlow.swift deleted file mode 100644 index e5f7bec453..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Flows/PurchaseFlow.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// PurchaseFlow.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -protocol PurchaseFlow { - -} - -public struct SubscriptionOptions: Encodable { - let platform: String - let options: [SubscriptionOption] - let features: [SubscriptionFeature] -} - -public struct SubscriptionOption: Encodable { - let id: String - let cost: SubscriptionOptionCost -} - -struct SubscriptionOptionCost: Encodable { - let displayPrice: String - let recurrence: String -} - -public struct SubscriptionFeature: Encodable { - let name: String -} - -// MARK: - - -public enum SubscriptionFeatureName: String, CaseIterable { - case privateBrowsing = "private-browsing" - case privateSearch = "private-search" - case emailProtection = "email-protection" - case appTrackingProtection = "app-tracking-protection" - case vpn = "vpn" - case personalInformationRemoval = "personal-information-removal" - case identityTheftRestoration = "identity-theft-restoration" -} - -public enum SubscriptionPlatformName: String { - case macos - case stripe -} - -// MARK: - - -public struct PurchaseUpdate: Codable { - let type: String - let token: String? - - public init(type: String, token: String? = nil) { - self.type = type - self.token = token - } -} diff --git a/DuckDuckGo/Subscription/Subscription/Logging.swift b/DuckDuckGo/Subscription/Subscription/Logging.swift deleted file mode 100644 index 24dc783c6e..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Logging.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// Logging.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Common - -struct Logging { - - static let subsystem = "com.duckduckgo.macos.browser.account" - - fileprivate static let accountLoggingEnabled = true - fileprivate static let account: OSLog = OSLog(subsystem: subsystem, category: "Account") - - fileprivate static let authServiceLoggingEnabled = true - fileprivate static let authService: OSLog = OSLog(subsystem: subsystem, category: "Account : AuthService") - - fileprivate static let subscriptionServiceLoggingEnabled = true - fileprivate static let subscriptionService: OSLog = OSLog(subsystem: subsystem, category: "Account : SubscriptionService") - - fileprivate static let errorsLoggingEnabled = true - fileprivate static let error: OSLog = OSLog(subsystem: subsystem, category: "Account : Errors") -} - -extension OSLog { - - public static var account: OSLog { - Logging.accountLoggingEnabled ? Logging.account : .disabled - } - - public static var authService: OSLog { - Logging.authServiceLoggingEnabled ? Logging.authService : .disabled - } - - public static var subscriptionService: OSLog { - Logging.subscriptionServiceLoggingEnabled ? Logging.subscriptionService : .disabled - } - - public static var error: OSLog { - Logging.errorsLoggingEnabled ? Logging.error : .disabled - } -} diff --git a/DuckDuckGo/Subscription/Subscription/PurchaseManager.swift b/DuckDuckGo/Subscription/Subscription/PurchaseManager.swift deleted file mode 100644 index fcdb4d230b..0000000000 --- a/DuckDuckGo/Subscription/Subscription/PurchaseManager.swift +++ /dev/null @@ -1,278 +0,0 @@ -// -// PurchaseManager.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import StoreKit - -@available(macOS 12.0, iOS 15.0, *) typealias Transaction = StoreKit.Transaction -@available(macOS 12.0, iOS 15.0, *) typealias RenewalInfo = StoreKit.Product.SubscriptionInfo.RenewalInfo -@available(macOS 12.0, iOS 15.0, *) typealias RenewalState = StoreKit.Product.SubscriptionInfo.RenewalState - -public enum StoreError: Error { - case failedVerification -} - -enum PurchaseManagerError: Error { - case productNotFound - case externalIDisNotAValidUUID - case purchaseFailed - case transactionCannotBeVerified - case transactionPendingAuthentication - case purchaseCancelledByUser - case unknownError -} - -@available(macOS 12.0, iOS 15.0, *) -public final class PurchaseManager: ObservableObject { - - static let productIdentifiers = ["ios.subscription.1month", "ios.subscription.1year", - "subscription.1week", "subscription.1month", "subscription.1year", - "review.subscription.1week", "review.subscription.1month", "review.subscription.1year"] - - public static let shared = PurchaseManager() - - @Published public private(set) var availableProducts: [Product] = [] - @Published public private(set) var purchasedProductIDs: [String] = [] - @Published public private(set) var purchaseQueue: [String] = [] - - @Published private(set) var subscriptionGroupStatus: RenewalState? - - private var transactionUpdates: Task? - private var storefrontChanges: Task? - - public init() { - transactionUpdates = observeTransactionUpdates() - storefrontChanges = observeStorefrontChanges() - } - - deinit { - transactionUpdates?.cancel() - storefrontChanges?.cancel() - } - - @MainActor - public func hasProductsAvailable() async -> Bool { - do { - let availableProducts = try await Product.products(for: Self.productIdentifiers) - print(" -- [PurchaseManager] updateAvailableProducts(): fetched \(availableProducts.count)") - return !availableProducts.isEmpty - } catch { - print("Error fetching available products: \(error)") - return false - } - } - - @MainActor - @discardableResult - public func syncAppleIDAccount() async -> Result { - do { - purchaseQueue.removeAll() - - print("Before AppStore.sync()") - - try await AppStore.sync() - - print("After AppStore.sync()") - - await updatePurchasedProducts() - await updateAvailableProducts() - - return .success(()) - } catch { - print("AppStore.sync error: \(error)") - return .failure(error) - } - } - - @MainActor - public func updateAvailableProducts() async { - print(" -- [PurchaseManager] updateAvailableProducts()") - - do { - let availableProducts = try await Product.products(for: Self.productIdentifiers) - print(" -- [PurchaseManager] updateAvailableProducts(): fetched \(availableProducts.count) products") - - if self.availableProducts != availableProducts { - print("availableProducts changed!") - self.availableProducts = availableProducts - } - } catch { - print("Error updating available products: \(error)") - } - } - - @MainActor - public func updatePurchasedProducts() async { - print(" -- [PurchaseManager] updatePurchasedProducts()") - - var purchasedSubscriptions: [String] = [] - - do { - for await result in Transaction.currentEntitlements { - let transaction = try checkVerified(result) - - guard transaction.productType == .autoRenewable else { continue } - guard transaction.revocationDate == nil else { continue } - - if let expirationDate = transaction.expirationDate, expirationDate > .now { - purchasedSubscriptions.append(transaction.productID) - - if let token = transaction.appAccountToken { - print(" -- [PurchaseManager] updatePurchasedProducts(): \(transaction.productID) -- custom UUID: \(token)" ) - } - } - } - } catch { - print("Error updating purchased products: \(error)") - } - - print(" -- [PurchaseManager] updatePurchasedProducts(): have \(purchasedSubscriptions.count) active subscriptions") - - if self.purchasedProductIDs != purchasedSubscriptions { - print("purchasedSubscriptions changed!") - self.purchasedProductIDs = purchasedSubscriptions - } - - subscriptionGroupStatus = try? await availableProducts.first?.subscription?.status.first?.state - } - - @MainActor - public static func mostRecentTransaction() async -> String? { - print(" -- [PurchaseManager] mostRecentTransaction()") - - var transactions: [VerificationResult] = [] - - for await result in Transaction.all { - transactions.append(result) - } - - print(" -- [PurchaseManager] mostRecentTransaction(): fetched \(transactions.count) transactions") - - return transactions.first?.jwsRepresentation - } - - @MainActor - public static func hasActiveSubscription() async -> Bool { - print(" -- [PurchaseManager] hasActiveSubscription()") - - var transactions: [VerificationResult] = [] - - for await result in Transaction.currentEntitlements { - transactions.append(result) - } - - print(" -- [PurchaseManager] hasActiveSubscription(): fetched \(transactions.count) transactions") - - return !transactions.isEmpty - } - - @MainActor - public func purchaseSubscription(with identifier: String, externalID: String) async -> Result { - - guard let product = availableProducts.first(where: { $0.id == identifier }) else { return .failure(PurchaseManagerError.productNotFound) } - - print(" -- [PurchaseManager] buy: \(product.displayName) (customUUID: \(externalID))") - - print("purchaseQueue append!") - purchaseQueue.append(product.id) - - print(" -- [PurchaseManager] starting purchase") - - var options: Set = Set() - - if let token = UUID(uuidString: externalID) { - options.insert(.appAccountToken(token)) - } else { - print("Wrong UUID") - return .failure(PurchaseManagerError.externalIDisNotAValidUUID) - } - - let result: Product.PurchaseResult - do { - result = try await product.purchase(options: options) - } catch { - print("error \(error)") - return .failure(PurchaseManagerError.purchaseFailed) - } - - print(" -- [PurchaseManager] purchase complete") - - purchaseQueue.removeAll() - print("purchaseQueue removeAll!") - - switch result { - case let .success(.verified(transaction)): - // Successful purchase - await transaction.finish() - await self.updatePurchasedProducts() - return .success(()) - case let .success(.unverified(_, error)): - // Successful purchase but transaction/receipt can't be verified - // Could be a jailbroken phone - print("Error: \(error.localizedDescription)") - return .failure(PurchaseManagerError.transactionCannotBeVerified) - case .pending: - // Transaction waiting on SCA (Strong Customer Authentication) or - // approval from Ask to Buy - return .failure(PurchaseManagerError.transactionPendingAuthentication) - case .userCancelled: - return .failure(PurchaseManagerError.purchaseCancelledByUser) - @unknown default: - return .failure(PurchaseManagerError.unknownError) - } - } - - private func checkVerified(_ result: VerificationResult) throws -> T { - // Check whether the JWS passes StoreKit verification. - switch result { - case .unverified: - // StoreKit parses the JWS, but it fails verification. - throw StoreError.failedVerification - case .verified(let safe): - // The result is verified. Return the unwrapped value. - return safe - } - } - - private func observeTransactionUpdates() -> Task { - - Task.detached { [unowned self] in - for await result in Transaction.updates { - print(" -- [PurchaseManager] observeTransactionUpdates()") - - if case .verified(let transaction) = result { - await transaction.finish() - } - - await self.updatePurchasedProducts() - } - } - } - - private func observeStorefrontChanges() -> Task { - - Task.detached { [unowned self] in - for await result in Storefront.updates { - print(" -- [PurchaseManager] observeStorefrontChanges(): \(result.countryCode)") - await updatePurchasedProducts() - await updateAvailableProducts() - } - } - } -} diff --git a/DuckDuckGo/Subscription/Subscription/Services/APIService.swift b/DuckDuckGo/Subscription/Subscription/Services/APIService.swift deleted file mode 100644 index b0d036a41f..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Services/APIService.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// APIService.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Common - -public enum APIServiceError: Swift.Error { - case decodingError - case encodingError - case serverError(description: String) - case unknownServerError - case connectionError -} - -struct ErrorResponse: Decodable { - let error: String -} - -public protocol APIService { - static var baseURL: URL { get } - static var session: URLSession { get } - static func executeAPICall(method: String, endpoint: String, headers: [String: String]?, body: Data?) async -> Result where T: Decodable -} - -public extension APIService { - - static func executeAPICall(method: String, endpoint: String, headers: [String: String]? = nil, body: Data? = nil) async -> Result where T: Decodable { - let request = makeAPIRequest(method: method, endpoint: endpoint, headers: headers, body: body) - - do { - let (data, urlResponse) = try await session.data(for: request) - - printDebugInfo(method: method, endpoint: endpoint, data: data, response: urlResponse) - - if let httpResponse = urlResponse as? HTTPURLResponse, (200..<300).contains(httpResponse.statusCode) { - if let decodedResponse = decode(T.self, from: data) { - return .success(decodedResponse) - } else { - return .failure(.decodingError) - } - } else { - if let decodedResponse = decode(ErrorResponse.self, from: data) { - let errorDescription = "[\(endpoint)] \(urlResponse.httpStatusCodeAsString ?? ""): \(decodedResponse.error)" - return .failure(.serverError(description: errorDescription)) - } else { - return .failure(.unknownServerError) - } - } - } catch { - os_log(.error, log: .subscription, "Service error: %{public}@", error.localizedDescription) - return .failure(.connectionError) - } - } - - private static func makeAPIRequest(method: String, endpoint: String, headers: [String: String]?, body: Data?) -> URLRequest { - let url = baseURL.appendingPathComponent(endpoint) - var request = URLRequest(url: url) - request.httpMethod = method - if let headers = headers { - request.allHTTPHeaderFields = headers - } - if let body = body { - request.httpBody = body - } - - return request - } - - private static func decode(_: T.Type, from data: Data) -> T? where T: Decodable { - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - decoder.dateDecodingStrategy = .millisecondsSince1970 - - return try? decoder.decode(T.self, from: data) - } - - private static func printDebugInfo(method: String, endpoint: String, data: Data, response: URLResponse) { - let statusCode = (response as? HTTPURLResponse)!.statusCode - let stringData = String(data: data, encoding: .utf8) ?? "" - - os_log(.info, log: .subscription, "[API] %d %{public}s /%{public}s :: %{private}s", statusCode, method, endpoint, stringData) - } - - static func makeAuthorizationHeader(for token: String) -> [String: String] { - ["Authorization": "Bearer " + token] - } -} - -extension URLResponse { - - var httpStatusCodeAsString: String? { - guard let httpStatusCode = (self as? HTTPURLResponse)?.statusCode else { return nil } - return String(httpStatusCode) - } -} diff --git a/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift b/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift deleted file mode 100644 index 1e4b7730ac..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// AuthService.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Common - -public struct AuthService: APIService { - - public static let session = { - let configuration = URLSessionConfiguration.ephemeral - return URLSession(configuration: configuration) - }() - public static let baseURL = URL(string: "https://quackdev.duckduckgo.com/api/auth")! - - // MARK: - - - public static func getAccessToken(token: String) async -> Result { - await executeAPICall(method: "GET", endpoint: "access-token", headers: makeAuthorizationHeader(for: token)) - } - - public struct AccessTokenResponse: Decodable { - public let accessToken: String - } - - // MARK: - - - public static func validateToken(accessToken: String) async -> Result { - await executeAPICall(method: "GET", endpoint: "validate-token", headers: makeAuthorizationHeader(for: accessToken)) - } - - // swiftlint:disable nesting - public struct ValidateTokenResponse: Decodable { - public let account: Account - - public struct Account: Decodable { - public let email: String? - let entitlements: [Entitlement] - public let externalID: String - - enum CodingKeys: String, CodingKey { - case email, entitlements, externalID = "externalId" // no underscores due to keyDecodingStrategy = .convertFromSnakeCase - } - } - - struct Entitlement: Decodable { - let id: Int - let name: String - let product: String - } - } - // swiftlint:enable nesting - - // MARK: - - - public static func createAccount(emailAccessToken: String?) async -> Result { - var headers: [String: String]? - - if let emailAccessToken { - headers = makeAuthorizationHeader(for: emailAccessToken) - } - - return await executeAPICall(method: "POST", endpoint: "account/create", headers: headers) - } - - public struct CreateAccountResponse: Decodable { - public let authToken: String - public let externalID: String - public let status: String - - // swiftlint:disable nesting - enum CodingKeys: String, CodingKey { - case authToken = "authToken", externalID = "externalId", status // no underscores due to keyDecodingStrategy = .convertFromSnakeCase - } - // swiftlint:enable nesting - } - - // MARK: - - - public static func storeLogin(signature: String) async -> Result { - let bodyDict = ["signature": signature, - "store": "apple_app_store"] - - guard let bodyData = try? JSONEncoder().encode(bodyDict) else { return .failure(.encodingError) } - return await executeAPICall(method: "POST", endpoint: "store-login", body: bodyData) - } - - public struct StoreLoginResponse: Decodable { - public let authToken: String - public let email: String - public let externalID: String - public let id: Int - public let status: String - - // swiftlint:disable nesting - enum CodingKeys: String, CodingKey { - // no underscores due to keyDecodingStrategy = .convertFromSnakeCase - case authToken = "authToken", email, externalID = "externalId", id, status - } - // swiftlint:enable nesting - } -} diff --git a/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift b/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift deleted file mode 100644 index ff048cba70..0000000000 --- a/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// SubscriptionService.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Common - -public struct SubscriptionService: APIService { - - public static let session = { - let configuration = URLSessionConfiguration.ephemeral - return URLSession(configuration: configuration) - }() - public static let baseURL = URL(string: "https://subscriptions-dev.duckduckgo.com/api")! - - // MARK: - - - public static func getSubscriptionDetails(token: String) async -> Result { - let result: Result = await executeAPICall(method: "GET", - endpoint: "subscription", - headers: makeAuthorizationHeader(for: token)) - - switch result { - case .success(let response): - cachedSubscriptionDetailsResponse = response - case .failure: - cachedSubscriptionDetailsResponse = nil - } - - return result - } - - public struct GetSubscriptionDetailsResponse: Decodable { - public let productId: String - public let startedAt: Date - public let expiresOrRenewsAt: Date - public let platform: String - public let status: String - - public var isSubscriptionActive: Bool { - status.lowercased() != "expired" && status.lowercased() != "inactive" - } - } - - public static var cachedSubscriptionDetailsResponse: GetSubscriptionDetailsResponse? - - // MARK: - - - public static func getProducts() async -> Result { - await executeAPICall(method: "GET", endpoint: "products") - } - - public typealias GetProductsResponse = [GetProductsItem] - - public struct GetProductsItem: Decodable { - public let productId: String - public let productLabel: String - public let billingPeriod: String - public let price: String - public let currency: String - } - - // MARK: - - - public static func getCustomerPortalURL(accessToken: String, externalID: String) async -> Result { - var headers = makeAuthorizationHeader(for: accessToken) - headers["externalAccountId"] = externalID - return await executeAPICall(method: "GET", endpoint: "checkout/portal", headers: headers) - } - - public struct GetCustomerPortalURLResponse: Decodable { - public let customerPortalUrl: String - } - -} diff --git a/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift b/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift deleted file mode 100644 index f8847dafaf..0000000000 --- a/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// SubscriptionPurchaseEnvironment.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Common - -public final class SubscriptionPurchaseEnvironment { - - public enum Environment: String { - case appStore, stripe - } - - public static var current: Environment = .appStore { - didSet { - os_log(.info, log: .subscription, "[SubscriptionPurchaseEnvironment] Setting to %@", current.rawValue) - - canPurchase = false - - switch current { - case .appStore: - setupForAppStore() - case .stripe: - setupForStripe() - } - } - } - - public static var canPurchase: Bool = false { - didSet { - os_log(.info, log: .subscription, "[SubscriptionPurchaseEnvironment] canPurchase %@", (canPurchase ? "true" : "false")) - } - } - - private static func setupForAppStore() { - if #available(macOS 12.0, iOS 15.0, *) { - Task { - await PurchaseManager.shared.updateAvailableProducts() - canPurchase = !PurchaseManager.shared.availableProducts.isEmpty - } - } - } - - private static func setupForStripe() { - Task { - if case let .success(products) = await SubscriptionService.getProducts() { - canPurchase = !products.isEmpty - } - } - } -} diff --git a/DuckDuckGo/Subscription/Subscription/URL+Subscription.swift b/DuckDuckGo/Subscription/Subscription/URL+Subscription.swift deleted file mode 100644 index d8f35b7312..0000000000 --- a/DuckDuckGo/Subscription/Subscription/URL+Subscription.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// URL+Subscription.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public extension URL { - - static var purchaseSubscription: URL { - URL(string: "https://abrown.duckduckgo.com/subscriptions/welcome")! - } - - static var subscriptionFAQ: URL { - URL(string: "https://duckduckgo.com/about")! - } - - // MARK: - Subscription Email - static var activateSubscriptionViaEmail: URL { - URL(string: "https://abrown.duckduckgo.com/subscriptions/activate")! - } - - static var addEmailToSubscription: URL { - URL(string: "https://abrown.duckduckgo.com/subscriptions/add-email")! - } - - static var manageSubscriptionEmail: URL { - URL(string: "https://abrown.duckduckgo.com/subscriptions/manage")! - } - - // MARK: - App Store app manage subscription URL - - static var manageSubscriptionsInAppStoreAppURL: URL { - URL(string: "macappstores://apps.apple.com/account/subscriptions")! - } - - static var manageSubscriptionsIniOSAppStoreAppURL: URL { - URL(string: "https://apps.apple.com/account/subscriptions")! - } - - // MARK: - Identity Theft Protection - static var manageITP: URL { - URL(string: "https://abrown.duckduckgo.com/identity-theft-restoration")! - } -} diff --git a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift index 28aa9881f0..b094c71124 100644 --- a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift @@ -24,6 +24,7 @@ import Foundation import WebKit import UserScript import Combine +import Subscription @available(iOS 15.0, *) final class IdentityTheftRestorationPagesFeature: Subfeature, ObservableObject { diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index 780b317fbe..90b2eacc49 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -24,6 +24,7 @@ import Foundation import WebKit import UserScript import Combine +import Subscription @available(iOS 15.0, *) final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObject { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift index abe47f3fc5..0a492e5e25 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift @@ -23,6 +23,7 @@ import Combine import Core #if SUBSCRIPTION +import Subscription @available(iOS 15.0, *) final class SubscriptionEmailViewModel: ObservableObject { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 084717c1b3..d42dbb0e61 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -22,7 +22,9 @@ import UserScript import Combine import Core + #if SUBSCRIPTION +import Subscription @available(iOS 15.0, *) final class SubscriptionFlowViewModel: ObservableObject { @@ -39,7 +41,7 @@ final class SubscriptionFlowViewModel: ObservableObject { private var canGoBackCancellable: AnyCancellable? // State variables - var purchaseURL = URL.purchaseSubscription + var purchaseURL = URL.subscriptionPurchase enum FeatureName { static let netP = "vpn" diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift index a076f08341..d189f1a6f1 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift @@ -23,12 +23,13 @@ import Combine import Core #if SUBSCRIPTION +import Subscription @available(iOS 15.0, *) final class SubscriptionITPViewModel: ObservableObject { let userScript: IdentityTheftRestorationPagesUserScript let subFeature: IdentityTheftRestorationPagesFeature - var manageITPURL = URL.manageITP + var manageITPURL = URL.identityTheftRestoration var viewTitle = UserText.settingsPProITRTitle enum Constants { @@ -37,7 +38,7 @@ final class SubscriptionITPViewModel: ObservableObject { } // State variables - var itpURL = URL.manageITP + var itpURL = URL.identityTheftRestoration @Published var webViewModel: AsyncHeadlessWebViewViewModel @Published var shouldShowNavigationBar: Bool = false @Published var canNavigateBack: Bool = false diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift index 0733cd2e83..666c6fe77e 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift @@ -23,6 +23,7 @@ import Combine import Core #if SUBSCRIPTION +import Subscription @available(iOS 15.0, *) final class SubscriptionRestoreViewModel: ObservableObject { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index 4cf960c44f..3619b69b95 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -22,6 +22,7 @@ import SwiftUI import StoreKit #if SUBSCRIPTION +import Subscription @available(iOS 15.0, *) final class SubscriptionSettingsViewModel: ObservableObject { @@ -81,7 +82,7 @@ final class SubscriptionSettingsViewModel: ObservableObject { } private func openSubscriptionManagementURL() { - let url = URL.manageSubscriptionsIniOSAppStoreAppURL + let url = URL.manageSubscriptionsInAppStoreAppURL if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } diff --git a/DuckDuckGo/SubscriptionDebugViewController.swift b/DuckDuckGo/SubscriptionDebugViewController.swift index d688ebf0e1..8e571649f7 100644 --- a/DuckDuckGo/SubscriptionDebugViewController.swift +++ b/DuckDuckGo/SubscriptionDebugViewController.swift @@ -19,6 +19,7 @@ import UIKit + #if !SUBSCRIPTION final class SubscriptionDebugViewController: UITableViewController { @@ -26,6 +27,7 @@ final class SubscriptionDebugViewController: UITableViewController { } #else +import Subscription @available(iOS 15.0, *) final class SubscriptionDebugViewController: UITableViewController { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 9765ab738d..17a9dfc50e 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "106.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "107.0.0"), ], targets: [ .target( diff --git a/LocalPackages/Subscription/.gitignore b/LocalPackages/Subscription/.gitignore deleted file mode 100644 index 0023a53406..0000000000 --- a/LocalPackages/Subscription/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -/.build -/Packages -xcuserdata/ -DerivedData/ -.swiftpm/configuration/registries.json -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata -.netrc diff --git a/LocalPackages/Subscription/Package.resolved b/LocalPackages/Subscription/Package.resolved deleted file mode 100644 index c4a8bbc9a0..0000000000 --- a/LocalPackages/Subscription/Package.resolved +++ /dev/null @@ -1,104 +0,0 @@ -{ - "pins" : [ - { - "identity" : "bloom_cpp", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/bloom_cpp.git", - "state" : { - "revision" : "8076199456290b61b4544bf2f4caf296759906a0", - "version" : "3.0.0" - } - }, - { - "identity" : "browserserviceskit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/BrowserServicesKit", - "state" : { - "revision" : "4cf8e857cd78e15c64ba37839634970fc675947c", - "version" : "81.4.0" - } - }, - { - "identity" : "content-scope-scripts", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/content-scope-scripts", - "state" : { - "revision" : "aa279a3b006a0b1e009707311283c7fcaed24fb7", - "version" : "4.39.0" - } - }, - { - "identity" : "duckduckgo-autofill", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/duckduckgo-autofill.git", - "state" : { - "revision" : "6dd7d696d4e666cedb2f1890a46fe53615226646", - "version" : "8.4.2" - } - }, - { - "identity" : "grdb.swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/GRDB.swift.git", - "state" : { - "revision" : "77d9a83191a74e319a5cfa27b0e3145d15607573", - "version" : "2.2.0" - } - }, - { - "identity" : "privacy-dashboard", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/privacy-dashboard", - "state" : { - "revision" : "51e2b46f413bf3ef18afefad631ca70f2c25ef70", - "version" : "1.4.0" - } - }, - { - "identity" : "punycodeswift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/gumob/PunycodeSwift.git", - "state" : { - "revision" : "4356ec54e073741449640d3d50a1fd24fd1e1b8b", - "version" : "2.1.0" - } - }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser", - "state" : { - "revision" : "6b2aa2748a7881eebb9f84fb10c01293e15b52ca", - "version" : "0.5.0" - } - }, - { - "identity" : "sync_crypto", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/sync_crypto", - "state" : { - "revision" : "2ab6ab6f0f96b259c14c2de3fc948935fc16ac78", - "version" : "0.2.0" - } - }, - { - "identity" : "trackerradarkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", - "state" : { - "revision" : "4684440d03304e7638a2c8086895367e90987463", - "version" : "1.2.1" - } - }, - { - "identity" : "wireguard-apple", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/wireguard-apple", - "state" : { - "revision" : "2d8172c11478ab11b0f5ad49bdb4f93f4b3d5e0d", - "version" : "1.1.1" - } - } - ], - "version" : 2 -} diff --git a/LocalPackages/Subscription/Package.swift b/LocalPackages/Subscription/Package.swift deleted file mode 100644 index c65b6109c7..0000000000 --- a/LocalPackages/Subscription/Package.swift +++ /dev/null @@ -1,27 +0,0 @@ -// swift-tools-version: 5.9 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "Subscription", - platforms: [ .macOS(.v11), .iOS(.v14) ], - products: [ - .library( - name: "Subscription", - targets: ["Subscription"]), - ], - dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "94.0.0"), - ], - targets: [ - .target( - name: "Subscription", - dependencies: [ - .product(name: "BrowserServicesKit", package: "BrowserServicesKit"), - ]), - .testTarget( - name: "SubscriptionTests", - dependencies: ["Subscription"]), - ] -) diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index aa8c6af19b..32b5b7b4e1 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "106.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "107.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 2d0a802dba..9496370013 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "106.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "107.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [