From 20c6e114b381107c779aa37c83a17321154b9540 Mon Sep 17 00:00:00 2001 From: Jonathan Jackson Date: Wed, 20 Nov 2024 13:17:28 -0500 Subject: [PATCH] Re-prompt for crash reporting (#3595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/1208592102886666/1208630312625695/f Tech Design URL: https://app.asana.com/0/1208592102886666/1208660326715650/f **Description**: - Introduce a feature flag for use with this project overall, currently set to internal only - Introduce a new UserDefaults value (```crashCollectionShouldRevertOptedInStatusTrigger```) that can be used to force the client to revert the existing ```crashCollectionOptInStatus``` to .unknown, only if the value is currently .optedIn. This new trigger is implemented as an Int, so that we can ensure the user’s Opt-In state will be reverted only once after the feature is turned on. In the future if we need this functionality again, we’d simply bump the garget value and the opt-in state would be reset again. See asana task for specific requirements, and the parent project for more context if necessary. --- Core/FeatureFlag.swift | 5 +++++ DuckDuckGo/AppSettings.swift | 1 + DuckDuckGo/AppUserDefaults.swift | 14 ++++++++++++++ DuckDuckGo/CrashCollectionOnboarding.swift | 20 +++++++++++++++++++- DuckDuckGoTests/AppSettingsMock.swift | 2 ++ 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Core/FeatureFlag.swift b/Core/FeatureFlag.swift index 81e9b34dd4..da04bcfaf6 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -53,6 +53,9 @@ public enum FeatureFlag: String { /// https://app.asana.com/0/72649045549333/1208617860225199/f case networkProtectionEnforceRoutes + + /// https://app.asana.com/0/1208592102886666/1208613627589762/f + case crashReportOptInStatusResetting } extension FeatureFlag: FeatureFlagDescribing { @@ -118,6 +121,8 @@ extension FeatureFlag: FeatureFlagDescribing { return .remoteDevelopment(.subfeature(NetworkProtectionSubfeature.enforceRoutes)) case .adAttributionReporting: return .remoteReleasable(.feature(.adAttributionReporting)) + case .crashReportOptInStatusResetting: + return .internalOnly } } } diff --git a/DuckDuckGo/AppSettings.swift b/DuckDuckGo/AppSettings.swift index 4c7c9d8991..f7242d9a88 100644 --- a/DuckDuckGo/AppSettings.swift +++ b/DuckDuckGo/AppSettings.swift @@ -79,6 +79,7 @@ protocol AppSettings: AnyObject, AppDebugSettings { var autoconsentEnabled: Bool { get set } var crashCollectionOptInStatus: CrashCollectionOptInStatus { get set } + var crashCollectionShouldRevertOptedInStatusTrigger: Int { get set } var duckPlayerMode: DuckPlayerMode { get set } var duckPlayerAskModeOverlayHidden: Bool { get set } diff --git a/DuckDuckGo/AppUserDefaults.swift b/DuckDuckGo/AppUserDefaults.swift index 559a521900..2c17e2ac1e 100644 --- a/DuckDuckGo/AppUserDefaults.swift +++ b/DuckDuckGo/AppUserDefaults.swift @@ -75,6 +75,7 @@ public class AppUserDefaults: AppSettings { static let favoritesDisplayMode = "com.duckduckgo.ios.favoritesDisplayMode" static let crashCollectionOptInStatus = "com.duckduckgo.ios.crashCollectionOptInStatus" + static let crashCollectionShouldRevertOptedInStatusTrigger = "com.duckduckgo.ios.crashCollectionShouldRevertOptedInStatusTrigger" static let duckPlayerMode = "com.duckduckgo.ios.duckPlayerMode" static let duckPlayerAskModeOverlayHidden = "com.duckduckgo.ios.duckPlayerAskModeOverlayHidden" @@ -399,6 +400,19 @@ public class AppUserDefaults: AppSettings { } } + var crashCollectionShouldRevertOptedInStatusTrigger: Int { + get { + if let resetTrigger = userDefaults?.integer(forKey: Keys.crashCollectionShouldRevertOptedInStatusTrigger) { + return resetTrigger + } else { + return 0 + } + } + set { + userDefaults?.setValue(newValue, forKey: Keys.crashCollectionShouldRevertOptedInStatusTrigger) + } + } + var duckPlayerMode: DuckPlayerMode { get { if let value = userDefaults?.string(forKey: Keys.duckPlayerMode), diff --git a/DuckDuckGo/CrashCollectionOnboarding.swift b/DuckDuckGo/CrashCollectionOnboarding.swift index 98a364220b..dd9e54af7c 100644 --- a/DuckDuckGo/CrashCollectionOnboarding.swift +++ b/DuckDuckGo/CrashCollectionOnboarding.swift @@ -20,6 +20,7 @@ import Combine import Foundation import SwiftUI +import BrowserServicesKit enum CrashCollectionOptInStatus: String { case undetermined, optedIn, optedOut @@ -35,16 +36,33 @@ final class CrashCollectionOnboardingViewController: UIHostingController Void) { let isCurrentlyPresenting = viewController.presentedViewController != nil + + if featureFlagger.isFeatureOn(.crashReportOptInStatusResetting) { + if appSettings.crashCollectionOptInStatus == .optedIn && + appSettings.crashCollectionShouldRevertOptedInStatusTrigger < crashCollectionShouldRevertOptedInStatusTriggerTargetValue { + appSettings.crashCollectionOptInStatus = .undetermined + appSettings.crashCollectionShouldRevertOptedInStatusTrigger = crashCollectionShouldRevertOptedInStatusTriggerTargetValue + } + } + guard shouldPresentOnboarding, !isCurrentlyPresenting else { if appSettings.crashCollectionOptInStatus == .optedIn { sendReport() diff --git a/DuckDuckGoTests/AppSettingsMock.swift b/DuckDuckGoTests/AppSettingsMock.swift index 4d9b09321e..13ced3eb65 100644 --- a/DuckDuckGoTests/AppSettingsMock.swift +++ b/DuckDuckGoTests/AppSettingsMock.swift @@ -82,6 +82,8 @@ class AppSettingsMock: AppSettings { var autoconsentEnabled = true var crashCollectionOptInStatus: CrashCollectionOptInStatus = .undetermined + + var crashCollectionShouldRevertOptedInStatusTrigger: Int = 0 var newTabPageSectionsEnabled: Bool = false