Files
obsidian-vault/work/wiki/apple-browsers/chat-path-onboarding-tracker-blocking.md
T

6.5 KiB

iOS: Chat-Path Onboarding — Tracker Blocking Demo

Asana: Ship Review task
Branch stack: demo-tracker-blocking-onboardingui-polishchat-path-dialog-polishsr-feedbackuti-flow
Due: 2026-05-15

Overview

A/B experiment adding a tracker-blocking demo step to onboarding for users who pick Duck.ai (chat-path). After the Fire Onboarding step, users see:

  1. "Try visiting a site!" dialog (address bar + toolbar hidden)
  2. Trackers-blocked dialog after a site loads
  3. "You've got this!" EOJ completion dialog
  4. Subscription upsell ("Oh, before I forget...")

Cohorts: Treatment A (address bar top) · Treatment B (address bar bottom) · Control (no change)
Audience: US only · iPhone only · Users who chose Duck.ai in onboarding, in A or B cohort


PR Stack

PR Branch Status
#4544 demo-tracker-blocking-onboarding Open — needs fixes
#4591 demo-tracker-blocking-onboarding-ui-polish Approved — needs rebase
#4664 demo-tracker-blocking-onboarding-chat-path-dialog-polish Open — needs fix
#4668 demo-tracker-blocking-onboarding-uti-flow Open — needs fix
#4855 demo-tracker-blocking-onboarding-sr-feedback Open — needs fixes

Required Changes

PR #4544 — main chat-path branch

  • Danger CI: No @UserDefaultsWrapper usage remains.
  • Mock refactor (alessandroboron): MockDaxDialogsSettings.chatPathPhase is a plain stored property, directly settable in tests.
  • Sync restore exclusion: Uncommented guard case .introDialog(isReturningUser: false) = introSteps.first in OnboardingIntroViewModel.insertExperimentStepIfNeeded(). Excludes all returning users (including sync-restore) from experiment enrollment.
  • return .treatmentA override: Removed hardcoded early return from resolveDuckAIQueryExperimentCohortID(). Now routes through the feature flagger.
  • Pixels: onboardingChatPathTryVisitSiteUnique and onboardingChatPathTrackersBlockedUnique defined in PixelEvent.swift with matching onboarding.json5 entries. No overlap with PR #4687.

PR #4664 — chat-path dialog polish

  • Title scope (alessandroboron): Fixed in commit c15122a1b0RebrandedNewTabDaxDialogFactory.createSubsequentDialog uses the correct title per chat-path vs standard path.

PR #4668 — UTI onboarding flow fixes

  • Dialog position, dismiss on navigation, UTI bar, completion path — all resolved.
  • Hardcoded UTI override: FeatureFlag.swift .unifiedToggleInput confirmed correct — Config(source: .remoteReleasable(...)), no defaultValue: .enabled.

PR #4855 — sr-feedback

  • Hide toolbar + address bar: NewTabPageViewController now calls chromeDelegate?.setBarsHidden(true/false, animated: false, customAnimationDuration: nil) in the show block, dismiss(), and dismissHostingController. Replaces previous setNavigationBarHidden + setChatPathVisitSiteControlsLocked pair.
  • Use delegate: Added newTabPageControllerCurrentTabIsLoading(_ controller:) -> Bool to NewTabPageControllerDelegate (default false). Implemented in MainViewController as currentTab?.isLoading == true. All (parent as? MainViewController)? casts removed from NewTabPageViewController.
  • Loading guard placement: Folded into the outer if condition via the new delegate method. No orphaned lock possible.
  • Revert Search Experience screen copy: UserText.swift searchAndDuckAIOption reverted to "Toggle between\nSearch and Duck.ai". All 26 lproj files updated.
  • DuckAIQuery toggle copy: DuckAIQueryExperiment.toggleAILabel = "Ask AI".

Privacy Config / Experiment Setup (TBD before shipping)

  • Rename experiment in privacy config — create new experiment name, disable old one.
  • Set "targets": [{ "localeLanguage": "en", "localeCountry": "US" }] in the experiment payload.
  • Inform Pete in O-N Live Onboarding Experiment Details once experiment name is confirmed.

Resolved / Dismissed

  • GJ6c — No longer required.
  • GJ3 — Hide address bar + toolbar for "Try visiting a site" step (chat-path only).
  • GJ2 — X button removed from chat-path dialogs (PR #4664).
  • GJ4 — Copy updated to "Next, try visiting a site!" after AI.
  • GJ8 — DuckAIQuery experiment toggle shows "Ask AI" (onboarding only; Search Experience screen keeps "Duck.ai").
  • "Try fire tabs" flash — Suppressed via isStillOnboarding() in DaxDialogs for chat-path users.
  • GJ7 — Returning users guarded from experiment enrollment.

Key Files

File Purpose
iOS/DuckDuckGo/DaxDialogs.swift Core onboarding dialog logic, chat-path phase state machine
iOS/DuckDuckGo/OnboardingFlow/LinearOnboarding/OnboardingIntroViewModel.swift Experiment enrollment, step injection
iOS/DuckDuckGo/NewTabPageViewController.swift Visit-site dialog presentation, bar hiding
iOS/DuckDuckGo/MainViewController+DuckAIExperiment.swift Chat-path completion, EOJ dialog trigger
iOS/Core/FeatureFlag.swift onboardingDuckAIQueryExperiment feature flag + cohorts
iOS/DuckDuckGo/UserText.swift Onboarding copy strings
iOS/DuckDuckGo/OnboardingFlow/LinearOnboarding/OnboardingView+DuckAIExperimentSearchContent.swift DuckAIQuery toggle + experiment UI
iOS/DuckDuckGo/OnboardingFlow/ContextualOnboarding/Rebranding/RebrandedNewTabDaxDialogFactory.swift Chat-path dialog creation

Copy Changes Summary

Location Value Status
DuckAIQueryExperiment.toggleAILabel (onboarding experiment toggle) Ask AI Done
searchAndDuckAIOption (Search Experience screen) Toggle between\nSearch and Duck.ai Done

Experiment Config

{
  "experimentName": "TBD",
  "targets": [{ "localeLanguage": "en", "localeCountry": "US" }],
  "cohorts": ["control", "treatmentA", "treatmentB"]
}

Feature flag: FeatureFlag.onboardingDuckAIQueryExperiment
Cohort type: FeatureFlag.DuckAIQueryExperimentCohort (.control, .treatmentA, .treatmentB)