Skip to content

Commit

Permalink
Autofill onByDefault rollout for new installs (#2056)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1205296228305939/f
Tech Design URL:
CC:

Description:
Adds support for rolling out Autofill as on by default for new installs based on the incremental feature flag onByDefault
  • Loading branch information
amddg44 authored Oct 8, 2023
1 parent d84c38d commit a0980bf
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 21 deletions.
3 changes: 3 additions & 0 deletions Core/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum FeatureFlag: String {
case autofillInlineIconCredentials
case autofillAccessCredentialManagement
case autofillPasswordGeneration
case autofillOnByDefault
case incontextSignup
case appTrackingProtection
case networkProtection
Expand All @@ -48,6 +49,8 @@ extension FeatureFlag: FeatureFlagSourceProviding {
return .remoteReleasable(.subfeature(AutofillSubfeature.accessCredentialManagement))
case .autofillPasswordGeneration:
return .remoteReleasable(.subfeature(AutofillSubfeature.autofillPasswordGeneration))
case .autofillOnByDefault:
return .remoteReleasable(.subfeature(AutofillSubfeature.onByDefault))
case .incontextSignup:
return .remoteReleasable(.feature(.incontextSignup))
}
Expand Down
4 changes: 2 additions & 2 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8955,8 +8955,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 80.4.1;
branch = "anya/autofill-on-default-flag";
kind = branch;
};
};
C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
"package": "BrowserServicesKit",
"repositoryURL": "https://github.com/DuckDuckGo/BrowserServicesKit",
"state": {
"branch": null,
"revision": "9dea0583dc6269971fb4728bd3efa1ed53f88306",
"version": "80.4.1"
"branch": "anya/autofill-on-default-flag",
"revision": "885f327b2fc1793eda26308fa3cb81995ba1b403",
"version": null
}
},
{
Expand All @@ -33,8 +33,8 @@
"repositoryURL": "https://github.com/duckduckgo/content-scope-scripts",
"state": {
"branch": null,
"revision": "8def15fe8a4c2fb76730f640507e9fd1d6c1f8a7",
"version": "4.32.0"
"revision": "74b6142c016be354144f28551de41b50c4864b1f",
"version": "4.37.0"
}
},
{
Expand Down
6 changes: 6 additions & 0 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
ThemeManager.shared.updateUserInterfaceStyle(window: window)

appIsLaunching = true

// Temporary logic for rollout of Autofill as on by default for new installs only
if AppDependencyProvider.shared.appSettings.autofillIsNewInstallForOnByDefault == nil {
AppDependencyProvider.shared.appSettings.setAutofillIsNewInstallForOnByDefault()
}

return true
}

Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ protocol AppSettings: AnyObject {
var autofillCredentialsEnabled: Bool { get set }
var autofillCredentialsSavePromptShowAtLeastOnce: Bool { get set }
var autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary: Bool { get set }
var autofillIsNewInstallForOnByDefault: Bool? { get set }
func setAutofillIsNewInstallForOnByDefault()

var voiceSearchEnabled: Bool { get set }

Expand Down
34 changes: 28 additions & 6 deletions DuckDuckGo/AppUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class AppUserDefaults: AppSettings {
static let currentFireButtonAnimationKey = "com.duckduckgo.app.currentFireButtonAnimationKey"

static let autofillCredentialsEnabled = "com.duckduckgo.ios.autofillCredentialsEnabled"
static let autofillIsNewInstallForOnByDefault = "com.duckduckgo.ios.autofillIsNewInstallForOnByDefault"
}

private struct DebugKeys {
Expand All @@ -70,6 +71,8 @@ public class AppUserDefaults: AppSettings {
return UserDefaults(suiteName: groupName)
}

lazy var featureFlagger = AppDependencyProvider.shared.featureFlagger

init(groupName: String = "group.com.duckduckgo.app") {
self.groupName = groupName
}
Expand Down Expand Up @@ -182,16 +185,22 @@ public class AppUserDefaults: AppSettings {
return
}
if !autofillCredentialsSavePromptShowAtLeastOnce {
autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = true
autofillCredentialsEnabled = true
if let isNewInstall = autofillIsNewInstallForOnByDefault,
isNewInstall,
featureFlagger.isFeatureOn(.autofillOnByDefault) {
autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = true
autofillCredentialsEnabled = true
}
}
}

var autofillCredentialsEnabled: Bool {
get {
// In future, we'll use setAutofillCredentialsEnabledAutomaticallyIfNecessary() here to automatically turn on autofill for people
// That haven't seen the save prompt before.
// For now, whilst internal testing is still happening, it's still set to default to be enabled
// setAutofillCredentialsEnabledAutomaticallyIfNecessary() used here to automatically turn on autofill for people if:
// 1. They haven't seen the save prompt before
// 2. They are a new install
// 3. The feature flag is enabled
setAutofillCredentialsEnabledAutomaticallyIfNecessary()
return userDefaults?.object(forKey: Keys.autofillCredentialsEnabled) as? Bool ?? false
}

Expand All @@ -205,7 +214,20 @@ public class AppUserDefaults: AppSettings {

@UserDefaultsWrapper(key: .autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary, defaultValue: false)
var autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary: Bool


var autofillIsNewInstallForOnByDefault: Bool? {
get {
return userDefaults?.object(forKey: Keys.autofillIsNewInstallForOnByDefault) as? Bool
}
set {
userDefaults?.set(newValue, forKey: Keys.autofillIsNewInstallForOnByDefault)
}
}

func setAutofillIsNewInstallForOnByDefault() {
autofillIsNewInstallForOnByDefault = StatisticsUserDefaults().installDate == nil
}

@UserDefaultsWrapper(key: .voiceSearchEnabled, defaultValue: false)
var voiceSearchEnabled: Bool

Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGoTests/AppSettingsMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class AppSettingsMock: AppSettings {

var autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary: Bool = false

var autofillIsNewInstallForOnByDefault: Bool?

func setAutofillIsNewInstallForOnByDefault() { }

var autocomplete: Bool = true

var currentThemeName: DuckDuckGo.ThemeName = .systemDefault
Expand Down
72 changes: 64 additions & 8 deletions DuckDuckGoTests/AppUserDefaultsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,22 @@

import XCTest
@testable import DuckDuckGo
import BrowserServicesKit

class AppUserDefaultsTests: XCTestCase {

let testGroupName = "test"
var internalUserDeciderStore: MockInternalUserStoring!

override func setUp() {
super.setUp()
UserDefaults(suiteName: testGroupName)?.removePersistentDomain(forName: testGroupName)
internalUserDeciderStore = MockInternalUserStoring()
}

override func tearDown() {
internalUserDeciderStore = nil
super.tearDown()
}

func testWhenLinkPreviewsIsSetThenItIsPersisted() {
Expand Down Expand Up @@ -93,17 +101,46 @@ class AppUserDefaultsTests: XCTestCase {
let appUserDefaults = AppUserDefaults(groupName: testGroupName)
XCTAssertFalse(appUserDefaults.autofillCredentialsEnabled)
}

/*
These tests aren't required until we make autofill default to off, and then enable turning it on automatically
func testWhenAutofillCredentialsIsDisabledAndHasNotBeenTurnedOnAutomaticallyBeforeThenAutofillCredentialsEnabled() {

func testWhenAutofillCredentialsIsDisabledAndHasNotBeenTurnedOnAutomaticallyBeforeWhenSavePromptShownThenDefaultAutofillStateIsFalse() {
let appUserDefaults = AppUserDefaults(groupName: testGroupName)
appUserDefaults.autofillCredentialsEnabled = false
appUserDefaults.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = false
appUserDefaults.autofillCredentialsSavePromptShowAtLeastOnce = true

XCTAssertFalse(appUserDefaults.autofillCredentialsEnabled)
}

func testWhenAutofillCredentialsIsDisabledAndHasNotBeenTurnedOnAutomaticallyBeforeAndPromptHasNotBeenSeenAndIsNotNewInstallThenDefaultAutofillStateIsFalse() {
let appUserDefaults = AppUserDefaults(groupName: testGroupName)
appUserDefaults.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = false
appUserDefaults.autofillCredentialsSavePromptShowAtLeastOnce = false
appUserDefaults.autofillIsNewInstallForOnByDefault = false

XCTAssertFalse(appUserDefaults.autofillCredentialsEnabled)
}

func testWhenAutofillCredentialsIsDisabledAndHasNotBeenTurnedOnAutomaticallyBeforeAndPromptHasNotBeenSeenAndIsNewInstallAndFeatureFlagDisabledThenDefaultAutofillStateIsFalse() {
let appUserDefaults = AppUserDefaults(groupName: testGroupName)
appUserDefaults.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = false
XCTAssertEqual(appUserDefaults.autofillCredentialsEnabled, true)
appUserDefaults.autofillCredentialsSavePromptShowAtLeastOnce = false
appUserDefaults.autofillIsNewInstallForOnByDefault = true
let featureFlagger = createFeatureFlagger(withSubfeatureEnabled: false)
appUserDefaults.featureFlagger = featureFlagger

XCTAssertFalse(appUserDefaults.autofillCredentialsEnabled)
}


func testWhenAutofillCredentialsIsDisabledAndHasNotBeenTurnedOnAutomaticallyBeforeAndPromptHasNotBeenSeenAndIsNewInstallAndFeatureFlagEnabledThenDefaultAutofillStateIsTrue() {
let appUserDefaults = AppUserDefaults(groupName: testGroupName)
appUserDefaults.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = false
appUserDefaults.autofillCredentialsSavePromptShowAtLeastOnce = false
appUserDefaults.autofillIsNewInstallForOnByDefault = true
let featureFlagger = createFeatureFlagger(withSubfeatureEnabled: true)
appUserDefaults.featureFlagger = featureFlagger

XCTAssertTrue(appUserDefaults.autofillCredentialsEnabled)
}

func testWhenAutofillCredentialsIsDisabledAndHasNotBeenTurnedOnAutomaticallyBeforeAndPromptHasBeenSeenThenAutofillCredentialsStaysDisabled() {
let appUserDefaults = AppUserDefaults(groupName: testGroupName)
appUserDefaults.autofillCredentialsEnabled = false
Expand All @@ -119,6 +156,25 @@ class AppUserDefaultsTests: XCTestCase {
appUserDefaults.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = true
XCTAssertEqual(appUserDefaults.autofillCredentialsEnabled, false)
}
*/


// MARK: - Mock Creation

private func createFeatureFlagger(withSubfeatureEnabled enabled: Bool) -> DefaultFeatureFlagger {
let mockManager = MockPrivacyConfigurationManager()
mockManager.privacyConfig = mockConfiguration(subfeatureEnabled: enabled)

let internalUserDecider = DefaultInternalUserDecider(store: internalUserDeciderStore)
return DefaultFeatureFlagger(internalUserDecider: internalUserDecider, privacyConfig: mockManager.privacyConfig)
}

private func mockConfiguration(subfeatureEnabled: Bool) -> PrivacyConfiguration {
let mockPrivacyConfiguration = MockPrivacyConfiguration()
mockPrivacyConfiguration.isSubfeatureKeyEnabled = { _, _ in
return subfeatureEnabled
}

return mockPrivacyConfiguration
}

}

0 comments on commit a0980bf

Please sign in to comment.