diff --git a/Core/AccountManagerExtension.swift b/Core/AccountManager+AppGroup.swift similarity index 93% rename from Core/AccountManagerExtension.swift rename to Core/AccountManager+AppGroup.swift index 5dd738fa3d..5e6ae4f057 100644 --- a/Core/AccountManagerExtension.swift +++ b/Core/AccountManager+AppGroup.swift @@ -1,5 +1,5 @@ // -// AccountManagerExtension.swift +// AccountManager+AppGroup.swift // DuckDuckGo // // Copyright © 2024 DuckDuckGo. All rights reserved. @@ -17,8 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION - import Foundation import Subscription @@ -27,5 +25,3 @@ public extension AccountManager { self.init(subscriptionAppGroup: Bundle.main.appGroup(bundle: .subs)) } } - -#endif diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 565956bbab..87f62fdb53 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -729,9 +729,6 @@ BD862E072B30F5E30073E2EE /* VPNFeedbackSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */; }; BD862E092B30F63E0073E2EE /* VPNMetadataCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */; }; BD862E0B2B30F9300073E2EE /* VPNFeedbackFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */; }; - BDA583872B98B6C700732FDC /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA583862B98B6C700732FDC /* AccountManagerExtension.swift */; }; - BDA583882B98B92F00732FDC /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA583862B98B6C700732FDC /* AccountManagerExtension.swift */; }; - BDA583892B98BA7600732FDC /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA583862B98B6C700732FDC /* AccountManagerExtension.swift */; }; BDC234F72B27F51100D3C798 /* UniquePixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC234F62B27F51100D3C798 /* UniquePixel.swift */; }; BDD3B3552B8EF8DB005857A8 /* NetworkProtectionUNNotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE3766DD2AC5945500AAB575 /* NetworkProtectionUNNotificationPresenter.swift */; }; BDFF031D2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFF031C2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift */; }; @@ -880,6 +877,7 @@ D6FEB8B12B7498A300C3615F /* HeadlessWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6FEB8B02B7498A300C3615F /* HeadlessWebView.swift */; }; D6FEB8B32B74990D00C3615F /* HeadlessWebViewNavCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6FEB8B22B74990D00C3615F /* HeadlessWebViewNavCoordinator.swift */; }; D6FEB8B52B74994000C3615F /* HeadlessWebViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6FEB8B42B74994000C3615F /* HeadlessWebViewCoordinator.swift */; }; + D6FF22482BC95F0B008E7BCC /* AccountManager+AppGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6FF22472BC95F0B008E7BCC /* AccountManager+AppGroup.swift */; }; EA39B7E2268A1A35000C62CD /* privacy-reference-tests in Resources */ = {isa = PBXBuildFile; fileRef = EA39B7E1268A1A35000C62CD /* privacy-reference-tests */; }; EAB19EDA268963510015D3EA /* DomainMatchingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB19ED9268963510015D3EA /* DomainMatchingTests.swift */; }; EE0153E12A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0153E02A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift */; }; @@ -2418,7 +2416,6 @@ BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackSender.swift; sourceTree = ""; }; BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNMetadataCollector.swift; sourceTree = ""; }; BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackFormView.swift; sourceTree = ""; }; - BDA583862B98B6C700732FDC /* AccountManagerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagerExtension.swift; sourceTree = ""; }; BDC234F62B27F51100D3C798 /* UniquePixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniquePixel.swift; sourceTree = ""; }; BDFF03192BA39C5A00F324C9 /* NetworkProtectionFeatureVisibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionFeatureVisibility.swift; sourceTree = ""; }; BDFF031C2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultNetworkProtectionVisibility.swift; sourceTree = ""; }; @@ -2579,6 +2576,7 @@ D6FEB8B02B7498A300C3615F /* HeadlessWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlessWebView.swift; sourceTree = ""; }; D6FEB8B22B74990D00C3615F /* HeadlessWebViewNavCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlessWebViewNavCoordinator.swift; sourceTree = ""; }; D6FEB8B42B74994000C3615F /* HeadlessWebViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlessWebViewCoordinator.swift; sourceTree = ""; }; + D6FF22472BC95F0B008E7BCC /* AccountManager+AppGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountManager+AppGroup.swift"; sourceTree = ""; }; EA39B7E1268A1A35000C62CD /* privacy-reference-tests */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "privacy-reference-tests"; path = "submodules/privacy-reference-tests"; sourceTree = SOURCE_ROOT; }; EAB19ED9268963510015D3EA /* DomainMatchingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainMatchingTests.swift; sourceTree = ""; }; EE0153E02A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionConvenienceInitialisers.swift; sourceTree = ""; }; @@ -4564,14 +4562,6 @@ path = Feedback; sourceTree = ""; }; - BDA583852B98B69C00732FDC /* Subscription */ = { - isa = PBXGroup; - children = ( - BDA583862B98B6C700732FDC /* AccountManagerExtension.swift */, - ); - name = Subscription; - sourceTree = ""; - }; BDFF031F2BA3D3AD00F324C9 /* Feature Visibility */ = { isa = PBXGroup; children = ( @@ -4871,6 +4861,14 @@ name = Model; sourceTree = ""; }; + D6FF22462BC95EF9008E7BCC /* Subscription */ = { + isa = PBXGroup; + children = ( + D6FF22472BC95F0B008E7BCC /* AccountManager+AppGroup.swift */, + ); + name = Subscription; + sourceTree = ""; + }; EA7EFE662677F5BD0075464E /* PrivacyReferenceTests */ = { isa = PBXGroup; children = ( @@ -5260,7 +5258,7 @@ F143C2E51E4A4CD400CFDE3A /* Core */ = { isa = PBXGroup; children = ( - BDA583852B98B69C00732FDC /* Subscription */, + D6FF22462BC95EF9008E7BCC /* Subscription */, 4B470ED4299C484B0086EBDC /* AppTrackingProtection */, F1CE42A71ECA0A520074A8DF /* Bookmarks */, 837774491F8E1ECE00E17A29 /* ContentBlocker */, @@ -6624,7 +6622,6 @@ 02025AD42988229800E694E7 /* ProxySocket.swift in Sources */, 02025AD62988229800E694E7 /* SocketProtocol.swift in Sources */, 02025AD82988229800E694E7 /* Tunnel.swift in Sources */, - BDA583892B98BA7600732FDC /* AccountManagerExtension.swift in Sources */, 02025ADA2988229800E694E7 /* Port.swift in Sources */, 02025ADB2988229800E694E7 /* HTTPStreamScanner.swift in Sources */, 02025ADC2988229800E694E7 /* UInt128.swift in Sources */, @@ -7078,7 +7075,6 @@ 85F98F92296F32BD00742F4A /* SyncSettingsViewController.swift in Sources */, 84E341961E2F7EFB00BDBA6F /* AppDelegate.swift in Sources */, 310D091D2799F57200DC0060 /* Download.swift in Sources */, - BDA583882B98B92F00732FDC /* AccountManagerExtension.swift in Sources */, C13F3F6C2B7F88470083BE40 /* AuthConfirmationPromptViewModel.swift in Sources */, 1EEF124E2850EADE003DDE57 /* PrivacyIconView.swift in Sources */, 37FCAAAB29911BF1000E420A /* WaitlistExtensions.swift in Sources */, @@ -7435,6 +7431,7 @@ 854858E32937BC550063610B /* CollectionExtension.swift in Sources */, 1E6A4D692984208800A371D3 /* LocaleExtension.swift in Sources */, 98F6EA472863124100720957 /* ContentBlockerRulesLists.swift in Sources */, + D6FF22482BC95F0B008E7BCC /* AccountManager+AppGroup.swift in Sources */, F1134EB01F40AC6300B73467 /* AtbParser.swift in Sources */, EE50052E29C369D300AE0773 /* FeatureFlag.swift in Sources */, BD15DB852B959CFD00821457 /* BundleExtension.swift in Sources */, @@ -7479,7 +7476,6 @@ B652DF0D287C2A6300C12A9C /* PrivacyFeatures.swift in Sources */, F10E522D1E946F8800CE1253 /* NSAttributedStringExtension.swift in Sources */, 9887DC252354D2AA005C85F5 /* Database.swift in Sources */, - BDA583872B98B6C700732FDC /* AccountManagerExtension.swift in Sources */, F143C3171E4A99D200CFDE3A /* AppURLs.swift in Sources */, C1963863283794A000298D4D /* BookmarksCachingSearch.swift in Sources */, ); @@ -8705,7 +8701,7 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = "-ld_classic"; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "NETWORK_PROTECTION SUBSCRIPTION"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = NETWORK_PROTECTION; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; @@ -8735,7 +8731,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Development - App"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_TRACKING_PROTECTION NETWORK_PROTECTION SUBSCRIPTION"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_TRACKING_PROTECTION NETWORK_PROTECTION"; SWIFT_VERSION = 5.0; }; name = Debug; @@ -8759,6 +8755,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.duckduckgo.mobile.ios"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = NETWORK_PROTECTION; SWIFT_VERSION = 5.0; }; name = Release; @@ -9185,7 +9182,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_TRACKING_PROTECTION NETWORK_PROTECTION SUBSCRIPTION ALPHA"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_TRACKING_PROTECTION NETWORK_PROTECTION ALPHA"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; @@ -9217,6 +9214,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID)"; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Development App - Alpha"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_TRACKING_PROTECTION NETWORK_PROTECTION ALPHA"; SWIFT_VERSION = 5.0; }; name = "Alpha Debug"; @@ -9581,7 +9579,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ld_classic"; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "NETWORK_PROTECTION ALPHA SUBSCRIPTION"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "NETWORK_PROTECTION ALPHA"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; @@ -9609,6 +9607,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.mobile.ios.alpha; PRODUCT_NAME = "$(TARGET_NAME)-Alpha"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.duckduckgo.mobile.ios.alpha"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "NETWORK_PROTECTION ALPHA"; SWIFT_VERSION = 5.0; }; name = Alpha; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8523aeeb34..32226596b2 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "b0749d25996c0fa18be07b7851f02ebb3b9fab50", - "version" : "134.0.1" + "revision" : "bc70d1a27263cc97a4060ac9e73ec10929c28a29", + "version" : "134.0.0" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "1bb3bc5eb565735051f342a87b5405d4374876c7", - "version" : "5.12.0" + "revision" : "62d5dc3d02f6a8347dc5f0b52162a0107d38b74c", + "version" : "5.8.0" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/airbnb/lottie-spm.git", "state" : { - "revision" : "4d0c11c85f5a9ec3d1b0daf2dc6daebc0e2df897", - "version" : "4.4.2" + "revision" : "3bd43e12d6fb54654366a61f7cfaca787318b8ce", + "version" : "4.4.1" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/privacy-dashboard", "state" : { - "revision" : "14b13d0c3db38f471ce4ba1ecb502ee1986c84d7", - "version" : "3.5.0" + "revision" : "620921fea14569eb00745cb5a44890d5890d99ec", + "version" : "3.4.0" } }, { @@ -167,8 +167,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/scinfu/SwiftSoup", "state" : { - "revision" : "028487d4a8a291b2fe1b4392b5425b6172056148", - "version" : "2.7.2" + "revision" : "1d39e56d364cba79ce43b341f9661b534cccb18d", + "version" : "2.7.1" } }, { @@ -203,8 +203,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/weichsel/ZIPFoundation.git", "state" : { - "revision" : "02b6abe5f6eef7e3cbd5f247c5cc24e246efcfe0", - "version" : "0.9.19" + "revision" : "b979e8b52c7ae7f3f39fa0182e738e9e7257eb78", + "version" : "0.9.18" } } ], diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index a485e5561e..be3bbc7501 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -33,10 +33,7 @@ import Configuration import Networking import DDGSync import SyncDataProviders - -#if SUBSCRIPTION import Subscription -#endif #if NETWORK_PROTECTION import NetworkProtection @@ -335,9 +332,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { NetworkProtectionAccessController().refreshNetworkProtectionAccess() #endif -#if SUBSCRIPTION setupSubscriptionsEnvironment() -#endif if vpnFeatureVisibility.shouldKeepVPNAccessViaWaitlist() { clearDebugWaitlistState() @@ -406,9 +401,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { #if NETWORK_PROTECTION private func presentExpiredEntitlementAlert() { let alertController = CriticalAlerts.makeExpiredEntitlementAlert { [weak self] in - #if SUBSCRIPTION self?.mainViewController?.segueToPrivacyPro() - #endif } window?.rootViewController?.present(alertController, animated: true) { [weak self] in DailyPixel.fireDailyAndCount(pixel: .privacyProVPNAccessRevokedDialogShown) @@ -452,7 +445,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } -#if SUBSCRIPTION private func setupSubscriptionsEnvironment() { Task { #if DEBUG || ALPHA @@ -470,7 +462,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { SubscriptionPurchaseEnvironment.current = .appStore } } -#endif func applicationDidBecomeActive(_ application: UIApplication) { guard !testing else { return } @@ -573,7 +564,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func updateSubscriptionStatus() { -#if SUBSCRIPTION Task { let accountManager = AccountManager() @@ -588,7 +578,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ = await accountManager.fetchEntitlements(cachePolicy: .reloadIgnoringLocalCacheData) } -#endif } func applicationWillResignActive(_ application: UIApplication) { @@ -627,7 +616,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } private func firePrivacyProFeatureEnabledPixel() { -#if SUBSCRIPTION let subscriptionFeatureAvailability = AppDependencyProvider.shared.subscriptionFeatureAvailability guard subscriptionFeatureAvailability.isFeatureAvailable, subscriptionFeatureAvailability.isSubscriptionPurchaseAllowed else { @@ -635,7 +623,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } DailyPixel.fire(pixel: .privacyProFeatureEnabled) -#endif } private func fireAppTPActiveUserPixel() { diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index 076d713f99..a52fa424c7 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -24,10 +24,7 @@ import Bookmarks import BrowserServicesKit import SwiftUI import PrivacyDashboard - -#if SUBSCRIPTION import Subscription -#endif extension MainViewController { @@ -204,7 +201,6 @@ extension MainViewController { launchSettings() } -#if SUBSCRIPTION func segueToPrivacyPro() { os_log(#function, log: .generalLog, type: .debug) hideAllHighlightsIfNeeded() @@ -212,7 +208,6 @@ extension MainViewController { $0.triggerDeepLinkNavigation(to: .subscriptionFlow) } } -#endif func segueToDebugSettings() { os_log(#function, log: .generalLog, type: .debug) @@ -251,13 +246,10 @@ extension MainViewController { appSettings: appSettings, bookmarksDatabase: bookmarksDatabase, tabManager: tabManager) -#if SUBSCRIPTION + let settingsViewModel = SettingsViewModel(legacyViewProvider: legacyViewProvider, accountManager: AccountManager(), deepLink: deepLinkTarget) -#else - let settingsViewModel = SettingsViewModel(legacyViewProvider: legacyViewProvider) -#endif Pixel.fire(pixel: .settingsPresented, withAdditionalParameters: PixelExperiment.parameters) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index ad7c7a025f..4acdc20f46 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -31,10 +31,7 @@ import Persistence import PrivacyDashboard import Networking import Suggestions - -#if SUBSCRIPTION import Subscription -#endif #if NETWORK_PROTECTION import NetworkProtection @@ -293,7 +290,7 @@ class MainViewController: UIViewController { subscribeToEmailProtectionStatusNotifications() subscribeToURLInterceptorNotifications() -#if NETWORK_PROTECTION && SUBSCRIPTION +#if NETWORK_PROTECTION subscribeToNetworkProtectionEvents() #endif @@ -1346,7 +1343,7 @@ class MainViewController: UIViewController { .store(in: &urlInterceptorCancellables) } -#if NETWORK_PROTECTION && SUBSCRIPTION +#if NETWORK_PROTECTION private func subscribeToNetworkProtectionEvents() { NotificationCenter.default.publisher(for: .accountDidSignIn) .receive(on: DispatchQueue.main) diff --git a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift index a062a26b3c..dcc76a9ac6 100644 --- a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift +++ b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift @@ -23,10 +23,8 @@ import NetworkProtection import UIKit import Common import NetworkExtension - -#if SUBSCRIPTION import Subscription -#endif + private class DefaultTunnelSessionProvider: TunnelSessionProvider { func activeSession() async -> NETunnelProviderSession? { @@ -63,13 +61,11 @@ extension NetworkProtectionKeychainTokenStore { let featureVisibility = DefaultNetworkProtectionVisibility.forTokenStore() let isSubscriptionEnabled = featureVisibility.isPrivacyProLaunched() let accessTokenProvider: () -> String? = { -#if SUBSCRIPTION - if featureVisibility.shouldMonitorEntitlement() { - return { AccountManager().accessToken } - } -#endif - return { nil } - }() + if featureVisibility.shouldMonitorEntitlement() { + return { AccountManager().accessToken } + } + return { nil } + }() self.init(keychainType: .dataProtection(.unspecified), serviceName: "\(Bundle.main.bundleIdentifier!).authToken", diff --git a/DuckDuckGo/NetworkProtectionDebugViewController.swift b/DuckDuckGo/NetworkProtectionDebugViewController.swift index a6b2a301c2..7331ba7e5a 100644 --- a/DuckDuckGo/NetworkProtectionDebugViewController.swift +++ b/DuckDuckGo/NetworkProtectionDebugViewController.swift @@ -33,10 +33,8 @@ import Common import Network import NetworkExtension import NetworkProtection - -#if SUBSCRIPTION import Subscription -#endif + // swiftlint:disable:next type_body_length final class NetworkProtectionDebugViewController: UITableViewController { @@ -688,9 +686,7 @@ shouldShowVPNShortcut: \(vpnVisibility.shouldShowVPNShortcut() ? "YES" : "NO") if let subscriptionOverrideEnabled = defaults.subscriptionOverrideEnabled { if subscriptionOverrideEnabled { defaults.subscriptionOverrideEnabled = false -#if SUBSCRIPTION AccountManager().signOut() -#endif } else { defaults.resetsubscriptionOverrideEnabled() } diff --git a/DuckDuckGo/NetworkProtectionFeatureVisibility.swift b/DuckDuckGo/NetworkProtectionFeatureVisibility.swift index 45f47e0153..b0d1be1cce 100644 --- a/DuckDuckGo/NetworkProtectionFeatureVisibility.swift +++ b/DuckDuckGo/NetworkProtectionFeatureVisibility.swift @@ -52,12 +52,8 @@ public extension NetworkProtectionFeatureVisibility { func shouldShowVPNShortcut() -> Bool { if isPrivacyProLaunched() { -#if SUBSCRIPTION let accountManager = AccountManager() return accountManager.isUserAuthenticated -#else - return false -#endif } else { return shouldKeepVPNAccessViaWaitlist() } diff --git a/DuckDuckGo/NetworkProtectionVisibilityForTunnelProvider.swift b/DuckDuckGo/NetworkProtectionVisibilityForTunnelProvider.swift index 2b50eee28d..dce270a1a5 100644 --- a/DuckDuckGo/NetworkProtectionVisibilityForTunnelProvider.swift +++ b/DuckDuckGo/NetworkProtectionVisibilityForTunnelProvider.swift @@ -20,10 +20,7 @@ #if NETWORK_PROTECTION import Foundation - -#if SUBSCRIPTION import Subscription -#endif struct NetworkProtectionVisibilityForTunnelProvider: NetworkProtectionFeatureVisibility { func isWaitlistBetaActive() -> Bool { @@ -35,11 +32,7 @@ struct NetworkProtectionVisibilityForTunnelProvider: NetworkProtectionFeatureVis } func isPrivacyProLaunched() -> Bool { -#if SUBSCRIPTION AccountManager().isUserAuthenticated -#else - false -#endif } func shouldMonitorEntitlement() -> Bool { diff --git a/DuckDuckGo/SettingsHostingController.swift b/DuckDuckGo/SettingsHostingController.swift index fb22ce2a7b..268ca5e5e1 100644 --- a/DuckDuckGo/SettingsHostingController.swift +++ b/DuckDuckGo/SettingsHostingController.swift @@ -20,9 +20,7 @@ import UIKit import SwiftUI import Core -#if SUBSCRIPTION import Subscription -#endif class SettingsHostingController: UIHostingController { var viewModel: SettingsViewModel diff --git a/DuckDuckGo/SettingsRootView.swift b/DuckDuckGo/SettingsRootView.swift index 998e8a2ddd..2c4cd10de4 100644 --- a/DuckDuckGo/SettingsRootView.swift +++ b/DuckDuckGo/SettingsRootView.swift @@ -29,32 +29,26 @@ struct SettingsRootView: View { @State private var subscriptionNavigationCoordinator = SubscriptionNavigationCoordinator() @State private var shouldDisplayDeepLinkSheet: Bool = false @State private var shouldDisplayDeepLinkPush: Bool = false -#if SUBSCRIPTION @State var deepLinkTarget: SettingsViewModel.SettingsDeepLinkSection? -#endif var body: some View { // Hidden navigationLink for programatic navigation if #available(iOS 15.0, *) { - - #if SUBSCRIPTION + if let target = deepLinkTarget { NavigationLink(destination: deepLinkDestinationView(for: target), isActive: $shouldDisplayDeepLinkPush) { EmptyView() } } - #endif } List { SettingsPrivacyProtectionsView() -#if SUBSCRIPTION if #available(iOS 15, *) { SettingsSubscriptionView().environmentObject(subscriptionNavigationCoordinator) } -#endif SettingsMainSettingsView() SettingsNextStepsView() SettingsOthersView() @@ -76,7 +70,6 @@ struct SettingsRootView: View { viewModel.onDissapear() } -#if SUBSCRIPTION // MARK: Deeplink Modifiers .sheet(isPresented: $shouldDisplayDeepLinkSheet, @@ -111,10 +104,8 @@ struct SettingsRootView: View { } } }) -#endif } -#if SUBSCRIPTION // MARK: DeepLink Views @available(iOS 15.0, *) @ViewBuilder @@ -141,7 +132,6 @@ struct SettingsRootView: View { return } } -#endif } struct InsetGroupedListStyleModifier: ViewModifier { diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 78441a3e00..3bb858a390 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -18,6 +18,7 @@ // import BrowserServicesKit +import Subscription struct SettingsState { @@ -45,6 +46,9 @@ struct SettingsState { var canPurchase: Bool var hasActiveSubscription: Bool var isSubscriptionPendingActivation: Bool + var isRestoring: Bool + var shouldDisplayRestoreSubscriptionError: Bool + var entitlements: [Entitlement.ProductName] } struct SyncSettings { @@ -117,7 +121,10 @@ struct SettingsState { subscription: Subscription(enabled: false, canPurchase: false, hasActiveSubscription: false, - isSubscriptionPendingActivation: false), + isSubscriptionPendingActivation: false, + isRestoring: false, + shouldDisplayRestoreSubscriptionError: false, + entitlements: []), sync: SyncSettings(enabled: false, title: "") ) } diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index df5b603b87..eff657c441 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -20,7 +20,6 @@ import SwiftUI import UIKit -#if SUBSCRIPTION import Subscription import Core @available(iOS 15.0, *) @@ -32,6 +31,7 @@ struct SettingsSubscriptionView: View { @State var isShowingITP = false @State var isShowingRestoreFlow = false @State var isShowingSubscribeFlow = false + @State var isShowingSubscriptionError = false enum Constants { static let purchaseDescriptionPadding = 5.0 @@ -53,7 +53,7 @@ struct SettingsSubscriptionView: View { @ViewBuilder private var restorePurchaseView: some View { - let text = !viewModel.isRestoringSubscription ? UserText.subscriptionActivateAppleIDButton : UserText.subscriptionRestoringTitle + let text = !viewModel.state.subscription.isRestoring ? UserText.subscriptionActivateAppleIDButton : UserText.subscriptionRestoringTitle SettingsCustomCell(content: { Text(text) .daxBodyRegular() @@ -61,8 +61,8 @@ struct SettingsSubscriptionView: View { action: { Task { await viewModel.restoreAccountPurchase() } }, - isButton: !viewModel.isRestoringSubscription ) - .alert(isPresented: $viewModel.shouldDisplayRestoreSubscriptionError) { + isButton: !viewModel.state.subscription.isRestoring ) + .alert(isPresented: $isShowingSubscriptionError) { Alert( title: Text(UserText.subscriptionAppStoreErrorTitle), message: Text(UserText.subscriptionAppStoreErrorMessage), @@ -124,7 +124,7 @@ struct SettingsSubscriptionView: View { @ViewBuilder private var subscriptionDetailsView: some View { - if viewModel.shouldShowNetP { + if viewModel.state.subscription.entitlements.contains(.networkProtection) { SettingsCellView(label: UserText.settingsPProVPNTitle, subtitle: viewModel.state.networkProtection.status != "" ? viewModel.state.networkProtection.status : nil, action: { viewModel.presentLegacyView(.netP) }, @@ -132,7 +132,7 @@ struct SettingsSubscriptionView: View { isButton: true) } - if viewModel.shouldShowDBP { + if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { NavigationLink(destination: SubscriptionPIRView(), isActive: $isShowingDBP, label: { @@ -142,7 +142,7 @@ struct SettingsSubscriptionView: View { } - if viewModel.shouldShowITP { + if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { NavigationLink(destination: SubscriptionITPView(), isActive: $isShowingITP, label: { @@ -164,7 +164,7 @@ struct SettingsSubscriptionView: View { if viewModel.state.subscription.hasActiveSubscription { // Allow managing the subscription if we have some entitlements - if viewModel.shouldShowDBP || viewModel.shouldShowITP || viewModel.shouldShowNetP { + if !viewModel.state.subscription.entitlements.isEmpty { subscriptionDetailsView // If no entitlements it should mean the backend is still out of sync @@ -179,6 +179,12 @@ struct SettingsSubscriptionView: View { } } + .onChange(of: viewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in + if value { + isShowingSubscriptionError = true + } + } + .onReceive(subscriptionNavigationCoordinator.$shouldPopToAppSettings) { shouldDismiss in if shouldDismiss { isShowingRestoreFlow = false @@ -189,4 +195,3 @@ struct SettingsSubscriptionView: View { } } } -#endif diff --git a/DuckDuckGo/SettingsView.swift b/DuckDuckGo/SettingsView.swift index 4169b490b1..ab326856a9 100644 --- a/DuckDuckGo/SettingsView.swift +++ b/DuckDuckGo/SettingsView.swift @@ -29,23 +29,19 @@ struct SettingsView: View { @State private var subscriptionNavigationCoordinator = SubscriptionNavigationCoordinator() @State private var shouldDisplayDeepLinkSheet: Bool = false @State private var shouldDisplayDeepLinkPush: Bool = false -#if SUBSCRIPTION @State var deepLinkTarget: SettingsViewModel.SettingsDeepLinkSection? -#endif var body: some View { // Hidden navigationLink for programatic navigation if #available(iOS 15.0, *) { - - #if SUBSCRIPTION + if let target = deepLinkTarget { NavigationLink(destination: deepLinkDestinationView(for: target), isActive: $shouldDisplayDeepLinkPush) { EmptyView() } } - #endif } // Settings Sections @@ -55,11 +51,9 @@ struct SettingsView: View { SettingsLoginsView() SettingsAppeareanceViewOld() SettingsPrivacyView() -#if SUBSCRIPTION if #available(iOS 15, *) { SettingsSubscriptionView().environmentObject(subscriptionNavigationCoordinator) } -#endif SettingsCustomizeView() SettingsMoreView() SettingsAboutViewOld() @@ -81,7 +75,6 @@ struct SettingsView: View { viewModel.onDissapear() } -#if SUBSCRIPTION // MARK: Deeplink Modifiers .sheet(isPresented: $shouldDisplayDeepLinkSheet, @@ -119,10 +112,8 @@ struct SettingsView: View { } } }) -#endif } -#if SUBSCRIPTION // MARK: DeepLink Views @available(iOS 15.0, *) @ViewBuilder @@ -149,5 +140,4 @@ struct SettingsView: View { return } } -#endif } diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 97c6063941..05d427f3df 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -25,10 +25,7 @@ import Common import Combine import SyncUI - -#if SUBSCRIPTION import Subscription -#endif #if APP_TRACKING_PROTECTION import NetworkExtension @@ -40,8 +37,8 @@ import NetworkProtection // swiftlint:disable type_body_length final class SettingsViewModel: ObservableObject { -// swiftlint:enable type_body_length + // Dependencies private(set) lazy var appSettings = AppDependencyProvider.shared.appSettings private(set) var privacyStore = PrivacyUserDefaults() @@ -52,28 +49,9 @@ final class SettingsViewModel: ObservableObject { private let voiceSearchHelper: VoiceSearchHelperProtocol var emailManager: EmailManager { EmailManager() } -#if SUBSCRIPTION + // Subscription Dependencies private var accountManager: AccountManager private var signOutObserver: Any? - private var isPrivacyProEnabled: Bool { - AppDependencyProvider.shared.subscriptionFeatureAvailability.isFeatureAvailable - } - // Cache subscription state in memory to prevent UI glitches - private var cacheSubscriptionState: SettingsState.Subscription = SettingsState.Subscription(enabled: false, - canPurchase: false, - hasActiveSubscription: false, - isSubscriptionPendingActivation: false) - - // Sheet Presentation & Navigation - @Published var isRestoringSubscription: Bool = false - @Published var shouldDisplayRestoreSubscriptionError: Bool = false - @Published var shouldShowNetP = false - @Published var shouldShowDBP = false - @Published var shouldShowITP = false -#endif - @UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false) - static private var cachedHasActiveSubscription: Bool - #if NETWORK_PROTECTION private let connectionObserver = ConnectionStatusObserverThroughSession() @@ -108,15 +86,12 @@ final class SettingsViewModel: ObservableObject { var shouldShowNoMicrophonePermissionAlert: Bool = false @Published var shouldShowEmailAlert: Bool = false var autocompleteSubtitle: String? - -#if SUBSCRIPTION - // MARK: - Deep linking + // MARK: - Deep linking // Used to automatically navigate to a specific section // immediately after loading the Settings View @Published private(set) var deepLinkTarget: SettingsDeepLinkSection? -#endif - + // MARK: Bindings var themeBinding: Binding { @@ -377,7 +352,7 @@ final class SettingsViewModel: ObservableObject { } ) } - + var universalLinksBinding: Binding { Binding( get: { self.state.allowUniversalLinks }, @@ -391,16 +366,15 @@ final class SettingsViewModel: ObservableObject { var cookiePopUpProtectionStatus: StatusIndicator { return appSettings.autoconsentEnabled ? .on : .off } - + var emailProtectionStatus: StatusIndicator { return emailManager.isSignedIn ? .on : .off } - + var syncStatus: StatusIndicator { legacyViewProvider.syncService.authState != .inactive ? .on : .off } - -#if SUBSCRIPTION + // MARK: Default Init init(state: SettingsState? = nil, legacyViewProvider: SettingsLegacyViewProvider, @@ -421,21 +395,9 @@ final class SettingsViewModel: ObservableObject { deinit { signOutObserver = nil } - -#else - // MARK: Default Init - init(state: SettingsState? = nil, - legacyViewProvider: SettingsLegacyViewProvider, - variantManager: VariantManager = AppDependencyProvider.shared.variantManager, - voiceSearchHelper: VoiceSearchHelperProtocol = AppDependencyProvider.shared.voiceSearchHelper) { - self.state = SettingsState.defaults - self.legacyViewProvider = legacyViewProvider - self.voiceSearchHelper = voiceSearchHelper - autocompleteSubtitle = variantManager.isSupported(feature: .history) ? UserText.settingsAutocompleteSubtitle : nil - } -#endif - } +// swiftlint:enable type_body_length + // MARK: Private methods extension SettingsViewModel { @@ -465,83 +427,40 @@ extension SettingsViewModel { speechRecognitionAvailable: AppDependencyProvider.shared.voiceSearchHelper.isSpeechRecognizerAvailable, loginsEnabled: featureFlagger.isFeatureOn(.autofillAccessCredentialManagement), networkProtection: getNetworkProtectionState(), - subscription: cacheSubscriptionState, + subscription: SettingsState.defaults.subscription, sync: getSyncState() ) setupSubscribers() - Task { await refreshSubscriptionState() } + Task { await setupSubscriptionEnvironment() } } private func getNetworkProtectionState() -> SettingsState.NetworkProtection { var enabled = false - #if NETWORK_PROTECTION - if #available(iOS 15, *) { - enabled = DefaultNetworkProtectionVisibility().shouldKeepVPNAccessViaWaitlist() - } - #endif - return SettingsState.NetworkProtection(enabled: enabled, status: "") - } - - private func refreshSubscriptionState() async { - let state = await self.getSubscriptionState() - DispatchQueue.main.async { - self.state.subscription = state - } - } - - private func getSubscriptionState() async -> SettingsState.Subscription { - var enabled = false - var canPurchase = false - var hasActiveSubscription = false - var isSubscriptionPendingActivation = false - - #if SUBSCRIPTION +#if NETWORK_PROTECTION if #available(iOS 15, *) { - enabled = isPrivacyProEnabled - canPurchase = SubscriptionPurchaseEnvironment.canPurchase - await setupSubscriptionEnvironment() - if let token = AccountManager().accessToken { - let subscriptionResult = await SubscriptionService.getSubscription(accessToken: token) - switch subscriptionResult { - case .success(let subscription): - hasActiveSubscription = subscription.isActive - - cacheSubscriptionState = SettingsState.Subscription(enabled: enabled, - canPurchase: canPurchase, - hasActiveSubscription: hasActiveSubscription, - isSubscriptionPendingActivation: isSubscriptionPendingActivation) - - case .failure: - if await PurchaseManager.hasActiveSubscription() { - isSubscriptionPendingActivation = true - } - } - } - } - #endif - return SettingsState.Subscription(enabled: enabled, - canPurchase: canPurchase, - hasActiveSubscription: hasActiveSubscription, - isSubscriptionPendingActivation: isSubscriptionPendingActivation) + enabled = DefaultNetworkProtectionVisibility().shouldKeepVPNAccessViaWaitlist() } +#endif + return SettingsState.NetworkProtection(enabled: enabled, status: "") + } private func getSyncState() -> SettingsState.SyncSettings { SettingsState.SyncSettings(enabled: legacyViewProvider.syncService.featureFlags.contains(.userInterface), - title: { - let syncService = legacyViewProvider.syncService - let isDataSyncingDisabled = !syncService.featureFlags.contains(.dataSyncing) - && syncService.authState == .active - if SyncBookmarksAdapter.isSyncBookmarksPaused - || SyncCredentialsAdapter.isSyncCredentialsPaused - || isDataSyncingDisabled { - return "⚠️ \(UserText.settingsSync)" - } - return SyncUI.UserText.syncTitle - }()) + title: { + let syncService = legacyViewProvider.syncService + let isDataSyncingDisabled = !syncService.featureFlags.contains(.dataSyncing) + && syncService.authState == .active + if SyncBookmarksAdapter.isSyncBookmarksPaused + || SyncCredentialsAdapter.isSyncCredentialsPaused + || isDataSyncingDisabled { + return "⚠️ \(UserText.settingsSync)" + } + return SyncUI.UserText.syncTitle + }()) } - + private func firePixel(_ event: Pixel.Event, withAdditionalParameters params: [String: String] = [:]) { Pixel.fire(pixel: event, withAdditionalParameters: params) @@ -556,104 +475,9 @@ extension SettingsViewModel { completion(true) } } - - #if SUBSCRIPTION - @available(iOS 15.0, *) - @MainActor - private func setupSubscriptionEnvironment() async { - - // Active subscription check - guard let token = accountManager.accessToken else { - setupSubscriptionPurchaseOptions() - return - } - - // Fetch available subscriptions from the backend (or sign out) - switch await SubscriptionService.getSubscription(accessToken: token) { - - case .success(let subscription): - if subscription.isActive { - state.subscription.hasActiveSubscription = true - state.subscription.isSubscriptionPendingActivation = false - - // Check entitlements and update UI accordingly - let entitlements: [Entitlement.ProductName] = [.networkProtection, .dataBrokerProtection, .identityTheftRestoration] - for entitlement in entitlements { - if case let .success(result) = await AccountManager().hasEntitlement(for: entitlement) { - switch entitlement { - case .identityTheftRestoration: - self.shouldShowITP = result - case .dataBrokerProtection: - self.shouldShowDBP = result - case .networkProtection: - self.shouldShowNetP = result - case .unknown: - return - } - } - } - } else { - // Sign out in case subscription is no longer active, reset the state - state.subscription.hasActiveSubscription = false - state.subscription.isSubscriptionPendingActivation = false - signOutUser() - } - - case .failure: - // Account is active but there's not a valid subscription / entitlements - if await PurchaseManager.hasActiveSubscription() { - state.subscription.isSubscriptionPendingActivation = true - } - } - - } - @available(iOS 15.0, *) - private func signOutUser() { - AccountManager().signOut() - setupSubscriptionPurchaseOptions() - } - @available(iOS 15.0, *) - private func setupSubscriptionPurchaseOptions() { - PurchaseManager.shared.$availableProducts - .receive(on: RunLoop.main) - .sink { [weak self] products in - self?.state.subscription.canPurchase = !products.isEmpty - }.store(in: &cancellables) - } - - private func setupNotificationObservers() { - signOutObserver = NotificationCenter.default.addObserver(forName: .accountDidSignOut, object: nil, queue: .main) { [weak self] _ in - if #available(iOS 15.0, *) { - guard let strongSelf = self else { return } - Task { await strongSelf.refreshSubscriptionState() } - } - } - } - - @available(iOS 15.0, *) - func restoreAccountPurchase() async { - DispatchQueue.main.async { self.isRestoringSubscription = true } - let result = await AppStoreRestoreFlow.restoreAccountFromPastPurchase(subscriptionAppGroup: Bundle.main.appGroup(bundle: .subs)) - switch result { - case .success: - DispatchQueue.main.async { - self.isRestoringSubscription = false - } - await self.setupSubscriptionEnvironment() - - case .failure: - DispatchQueue.main.async { - self.isRestoringSubscription = false - self.shouldDisplayRestoreSubscriptionError = true - } - } - } - -#endif // SUBSCRIPTION - - #if NETWORK_PROTECTION +#if NETWORK_PROTECTION private func updateNetPStatus(connectionStatus: ConnectionStatus) { if DefaultNetworkProtectionVisibility().isPrivacyProLaunched() { switch connectionStatus { @@ -676,7 +500,8 @@ extension SettingsViewModel { } } } - #endif +#endif + } // MARK: Subscribers @@ -703,16 +528,12 @@ extension SettingsViewModel { func onAppear() { Task { await initState() -#if SUBSCRIPTION triggerDeepLinkNavigation(to: self.deepLinkTarget) -#endif } } func onDissapear() { -#if SUBSCRIPTION self.deepLinkTarget = nil -#endif } func setAsDefaultBrowser() { @@ -848,7 +669,6 @@ extension SettingsViewModel: AutofillLoginSettingsListViewControllerDelegate { } // MARK: DeepLinks -#if SUBSCRIPTION extension SettingsViewModel { enum SettingsDeepLinkSection: Identifiable { @@ -905,5 +725,115 @@ extension SettingsViewModel { } } } -#endif + +// MARK: Subscriptions +extension SettingsViewModel { + + @MainActor + private func setupSubscriptionEnvironment() async { + + + state.subscription.enabled = AppDependencyProvider.shared.subscriptionFeatureAvailability.isFeatureAvailable + state.subscription.canPurchase = SubscriptionPurchaseEnvironment.canPurchase + state.subscription.hasActiveSubscription = false + state.subscription.isSubscriptionPendingActivation = false + self.state.subscription.entitlements = [] + + // Active subscription check + guard let token = accountManager.accessToken else { + if #available(iOS 15, *) { + setupSubscriptionPurchaseOptions() + } + return + } + + let subscriptionResult = await SubscriptionService.getSubscription(accessToken: token) + switch subscriptionResult { + + case .success(let subscription): + if subscription.isActive { + state.subscription.hasActiveSubscription = true + state.subscription.isSubscriptionPendingActivation = false + + // Check entitlements and update state + let entitlements: [Entitlement.ProductName] = [.networkProtection, .dataBrokerProtection, .identityTheftRestoration] + for entitlement in entitlements { + if case .success = await AccountManager().hasEntitlement(for: entitlement) { + switch entitlement { + case .identityTheftRestoration: + self.state.subscription.entitlements.append(.identityTheftRestoration) + case .dataBrokerProtection: + self.state.subscription.entitlements.append(.dataBrokerProtection) + case .networkProtection: + self.state.subscription.entitlements.append(.networkProtection) + case .unknown: + return + } + } + } + } else { + // Sign out in case subscription is no longer active, reset the state + state.subscription.hasActiveSubscription = false + state.subscription.isSubscriptionPendingActivation = false + if #available(iOS 15, *) { + signOutUser() + } + } + + case .failure: + // Account is active but there's not a valid subscription / entitlements + if #available(iOS 15, *) { + if await PurchaseManager.hasActiveSubscription() { + state.subscription.isSubscriptionPendingActivation = true + } + } + + } + } + + @available(iOS 15.0, *) + private func signOutUser() { + AccountManager().signOut() + setupSubscriptionPurchaseOptions() + } + + @available(iOS 15.0, *) + private func setupSubscriptionPurchaseOptions() { + PurchaseManager.shared.$availableProducts + .receive(on: RunLoop.main) + .sink { [weak self] products in + self?.state.subscription.canPurchase = !products.isEmpty + }.store(in: &cancellables) + } + + private func setupNotificationObservers() { + signOutObserver = NotificationCenter.default.addObserver(forName: .accountDidSignOut, object: nil, queue: .main) { [weak self] _ in + if #available(iOS 15.0, *) { + guard let strongSelf = self else { return } + Task { await strongSelf.setupSubscriptionEnvironment() } + } + } + } + + @available(iOS 15.0, *) + func restoreAccountPurchase() async { + DispatchQueue.main.async { self.state.subscription.isRestoring = true } + let result = await AppStoreRestoreFlow.restoreAccountFromPastPurchase(subscriptionAppGroup: Bundle.main.appGroup(bundle: .subs)) + switch result { + case .success: + DispatchQueue.main.async { + self.state.subscription.isRestoring = false + } + await self.setupSubscriptionEnvironment() + + case .failure: + DispatchQueue.main.async { + self.state.subscription.isRestoring = false + self.state.subscription.shouldDisplayRestoreSubscriptionError = true + self.state.subscription.shouldDisplayRestoreSubscriptionError = false + + } + } + } +} // swiftlint:enable file_length diff --git a/DuckDuckGo/Subscription/Subscription.swift b/DuckDuckGo/Subscription/Subscription.swift index 2ccb227e74..2adfc0dbea 100644 --- a/DuckDuckGo/Subscription/Subscription.swift +++ b/DuckDuckGo/Subscription/Subscription.swift @@ -18,6 +18,7 @@ // import Foundation +import Subscription enum SubscriptionPurchaseError: Error { case purchaseFailed, diff --git a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift index a577a61613..1f1e1b7a37 100644 --- a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift @@ -17,7 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION import BrowserServicesKit import Common import Foundation @@ -80,4 +79,3 @@ final class IdentityTheftRestorationPagesFeature: Subfeature, ObservableObject { } } -#endif diff --git a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift index cfbafec477..0eb08ca0a4 100644 --- a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift +++ b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift @@ -17,8 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION - import BrowserServicesKit import Common import Combine @@ -66,5 +64,3 @@ extension IdentityTheftRestorationPagesUserScript: WKScriptMessageHandler { // unsupported } } - -#endif diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index e1bd6f27f6..1ca47844f7 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -19,7 +19,6 @@ // swiftlint:disable file_length -#if SUBSCRIPTION import BrowserServicesKit import Common import Foundation @@ -410,7 +409,4 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } } - -#endif - // swiftlint:enable file_length diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift index c3f52e4c40..5a841f17fc 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift @@ -17,8 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION - import BrowserServicesKit import Common import Combine @@ -69,5 +67,3 @@ extension SubscriptionPagesUserScript: WKScriptMessageHandler { os_log("SubscriptionPagesUserScript sent an unsupported message: %s", log: .generalLog, type: .fault, message.messageName) } } - -#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionContainerViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionContainerViewModel.swift index 38534d8805..a30d527090 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionContainerViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionContainerViewModel.swift @@ -20,7 +20,6 @@ import Foundation import Combine -#if SUBSCRIPTION @available(iOS 15.0, *) final class SubscriptionContainerViewModel: ObservableObject { @@ -45,4 +44,3 @@ final class SubscriptionContainerViewModel: ObservableObject { subFeature.cleanup() } } -#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift index 9ffba5e46c..0a53d953e0 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift @@ -21,9 +21,8 @@ import Foundation import UserScript import Combine import Core - -#if SUBSCRIPTION import Subscription + @available(iOS 15.0, *) final class SubscriptionEmailViewModel: ObservableObject { @@ -257,4 +256,3 @@ final class SubscriptionEmailViewModel: ObservableObject { } } -#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionExternalLinkViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionExternalLinkViewModel.swift index 4890437265..3df21c014e 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionExternalLinkViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionExternalLinkViewModel.swift @@ -21,7 +21,6 @@ import Foundation import Core import Combine -#if SUBSCRIPTION @available(iOS 15.0, *) final class SubscriptionExternalLinkViewModel: ObservableObject { @@ -74,4 +73,3 @@ final class SubscriptionExternalLinkViewModel: ObservableObject { } } -#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 56177a19a5..906de051db 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -21,10 +21,8 @@ import Foundation import UserScript import Combine import Core - - -#if SUBSCRIPTION import Subscription + @available(iOS 15.0, *) // swiftlint:disable type_body_length final class SubscriptionFlowViewModel: ObservableObject { @@ -346,7 +344,6 @@ final class SubscriptionFlowViewModel: ObservableObject { } } -#endif // TODO: Move to BSK later private extension URL { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift index 4ff55c52a7..a9ac238d38 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift @@ -21,9 +21,8 @@ import Foundation import UserScript import Combine import Core - -#if SUBSCRIPTION import Subscription + @available(iOS 15.0, *) final class SubscriptionITPViewModel: ObservableObject { @@ -193,4 +192,3 @@ final class SubscriptionITPViewModel: ObservableObject { } } -#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift index 658dc2fff4..4f6cddffa4 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift @@ -20,7 +20,6 @@ import Foundation import Core -#if SUBSCRIPTION @available(iOS 15.0, *) final class SubscriptionPIRViewModel: ObservableObject { @@ -30,4 +29,3 @@ final class SubscriptionPIRViewModel: ObservableObject { Pixel.fire(pixel: .privacyProPersonalInformationRemovalSettings) } } -#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift index 2c3d3ee1d0..f025776f7e 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift @@ -21,9 +21,8 @@ import Foundation import UserScript import Combine import Core - -#if SUBSCRIPTION import Subscription + @available(iOS 15.0, *) final class SubscriptionRestoreViewModel: ObservableObject { @@ -208,4 +207,3 @@ final class SubscriptionRestoreViewModel: ObservableObject { } -#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index b5b5ab21ee..06e9af978c 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -20,10 +20,9 @@ import Foundation import SwiftUI import StoreKit - -#if SUBSCRIPTION import Subscription import Core + @available(iOS 15.0, *) final class SubscriptionSettingsViewModel: ObservableObject { @@ -209,4 +208,3 @@ final class SubscriptionSettingsViewModel: ObservableObject { signOutObserver = nil } } -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift b/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift index f006631188..57e5822f37 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift @@ -20,7 +20,6 @@ import Foundation import SwiftUI -#if SUBSCRIPTION @available(iOS 15.0, *) struct SubscriptionContainerView: View { @@ -61,4 +60,3 @@ struct SubscriptionContainerView: View { } } } -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift index ce23b945ea..84cda5b8d8 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift @@ -17,7 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION import SwiftUI import Foundation import Core @@ -190,5 +189,3 @@ struct SubscriptionEmailView: View { // SubscriptionEmailView() // } // } - -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionExternalLinkView.swift b/DuckDuckGo/Subscription/Views/SubscriptionExternalLinkView.swift index 4b5af9a3d5..9884d9bcc3 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionExternalLinkView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionExternalLinkView.swift @@ -17,7 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION import SwiftUI import Foundation import DesignResourcesKit @@ -95,4 +94,3 @@ struct SubscriptionExternalLinkView: View { navAppearance.tintColor = UIColor(designSystemColor: .textPrimary) } } -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 23f206d79f..5ad42c7639 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -17,7 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION import SwiftUI import Foundation import DesignResourcesKit @@ -250,5 +249,3 @@ struct SubscriptionFlowView: View { // SubscriptionFlowView() // } // } - -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionGoogleView.swift b/DuckDuckGo/Subscription/Views/SubscriptionGoogleView.swift index 394ea90b58..c790e29ae4 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionGoogleView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionGoogleView.swift @@ -19,9 +19,9 @@ import Foundation import SwiftUI -#if SUBSCRIPTION -@available(iOS 15.0, *) + +@available(iOS 15.0, *) struct SubscriptionGoogleView: View { enum Constants { @@ -50,10 +50,9 @@ struct SubscriptionGoogleView: View { } } -#endif -#if SUBSCRIPTION && DEBUG +#if DEBUG @available(iOS 15.0, *) struct SubscriptionGoogleView_Previews: PreviewProvider { diff --git a/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift b/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift index 490d68b1fb..649284eda8 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift @@ -17,7 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION import SwiftUI import Foundation import DesignResourcesKit @@ -149,5 +148,3 @@ struct SubscriptionITPView: View { // SubscriptionITPView() // } // } - -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift b/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift index b6f97b1afe..74a7fcf1a0 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift @@ -17,7 +17,6 @@ // limitations under the License. // -#if SUBSCRIPTION import SwiftUI import Foundation import DesignResourcesKit @@ -188,5 +187,3 @@ struct SubscriptionPIRView: View { // SubscriptionPIRView() // } // } - -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift index ab5b662103..648cc3b925 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift @@ -22,8 +22,6 @@ import SwiftUI import DesignResourcesKit import Core - -#if SUBSCRIPTION @available(iOS 15.0, *) // swiftlint:disable type_body_length struct SubscriptionRestoreView: View { @@ -339,4 +337,3 @@ struct SubscriptionRestoreView: View { } // swiftlint:enable type_body_length -#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index f3bf14d671..9d57d27765 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -22,7 +22,6 @@ import SwiftUI import DesignResourcesKit import Core -#if SUBSCRIPTION @available(iOS 15.0, *) struct SubscriptionSettingsView: View { @@ -216,7 +215,6 @@ struct SubscriptionSettingsView: View { } -#endif // Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f // @available(iOS 15.0, *) diff --git a/DuckDuckGo/SubscriptionDebugViewController.swift b/DuckDuckGo/SubscriptionDebugViewController.swift index 8368fb92e2..61dc59b577 100644 --- a/DuckDuckGo/SubscriptionDebugViewController.swift +++ b/DuckDuckGo/SubscriptionDebugViewController.swift @@ -19,14 +19,6 @@ import UIKit - -#if !SUBSCRIPTION - -final class SubscriptionDebugViewController: UITableViewController { - // Just an empty VC -} - -#else import Subscription @available(iOS 15.0, *) @@ -250,5 +242,3 @@ final class SubscriptionDebugViewController: UITableViewController { } } } - -#endif diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift index 9b86b6768f..4735f0a27e 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift @@ -26,10 +26,7 @@ import Core import Networking import NetworkExtension import NetworkProtection - -#if SUBSCRIPTION import Subscription -#endif // swiftlint:disable type_body_length @@ -226,12 +223,10 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { let featureVisibility = NetworkProtectionVisibilityForTunnelProvider() let isSubscriptionEnabled = featureVisibility.isPrivacyProLaunched() let accessTokenProvider: () -> String? = { -#if SUBSCRIPTION - if featureVisibility.shouldMonitorEntitlement() { - return { AccountManager().accessToken } - } -#endif - return { nil } + if featureVisibility.shouldMonitorEntitlement() { + return { AccountManager().accessToken } + } + return { nil } }() let tokenStore = NetworkProtectionKeychainTokenStore( keychainType: .dataProtection(.unspecified), @@ -307,7 +302,6 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { } private static func entitlementCheck() async -> Result { -#if SUBSCRIPTION guard NetworkProtectionVisibilityForTunnelProvider().shouldMonitorEntitlement() else { return .success(true) } @@ -324,9 +318,6 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { case .failure(let error): return .failure(error) } -#else - return .success(true) -#endif } }