# iOS: Chat-Path Onboarding — Tracker Blocking Demo > **Asana:** [Ship Review task](https://app.asana.com/1/137249556945/task/1214147157456478) > **Branch stack:** `demo-tracker-blocking-onboarding` → `ui-polish` → `chat-path-dialog-polish` → `sr-feedback` → `uti-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](https://github.com/duckduckgo/apple-browsers/pull/4544) | `demo-tracker-blocking-onboarding` | Open — needs fixes | | [#4591](https://github.com/duckduckgo/apple-browsers/pull/4591) | `demo-tracker-blocking-onboarding-ui-polish` | Approved — needs rebase | | [#4664](https://github.com/duckduckgo/apple-browsers/pull/4664) | `demo-tracker-blocking-onboarding-chat-path-dialog-polish` | Open — needs fix | | [#4668](https://github.com/duckduckgo/apple-browsers/pull/4668) | `demo-tracker-blocking-onboarding-uti-flow` | Open — needs fix | | [#4855](https://github.com/duckduckgo/apple-browsers/pull/4855) | `demo-tracker-blocking-onboarding-sr-feedback` | Open — needs fixes | --- ## Required Changes (not yet done) ### PR #4544 — main chat-path branch - [ ] **Danger CI**: Replace `@UserDefaultsWrapper(key: .daxChatPathVisitSiteSeen)` with `KeyedStoring` protocol pattern — new `@UserDefaultsWrapper` definitions are blocked by Danger rule. - [ ] **Mock refactor** (alessandroboron): `MockDaxDialogsSettings.chatPathPhase` should expose `var _chatPathPhase: DaxDialogs.ChatPathPhase = .visitSite` for direct test control, not replicate real computed logic. - [ ] **Sync restore exclusion**: Exclude users who selected "Restore My Stuff" (sync restore) in `OnboardingIntroViewModel` — Dax dialogs are disabled for those users, breaking the flow. - [ ] **Pixels**: Coordinate with rachelmcr (PR #4687) — may be able to use `variant` parameter on existing pixels instead of adding new chat-path-specific pixel events. ### PR #4664 — chat-path dialog polish - [ ] **Title scope** (alessandroboron): `RebrandedNewTabDaxDialogFactory` title change ("Next, try visiting a site!") must only apply to chat-path flow, not all onboarding flows. ### PR #4668 — UTI (Unified Toggle Input) onboarding flow fixes Fixes to make the chat-path onboarding dialogs work correctly when the Unified Toggle Input is active: - [x] Dialog position: anchor to `coordinator.viewController.view.bottomAnchor` (UTI bar bottom), embed in `unifiedInputContentContainer`. - [x] Dialog not dismissed on navigation: `viewWillDisappear` removes hosting controller parented to `MainViewController`. - [x] UTI bar staying active after URL submission: `preserveOmnibarSession` now checks `!tab.isLoading`. - [x] "You've got this" completion dialog path in UTI mode: `showDuckAIOnboardingCompletionDialogInUTI`. - [x] UTI bar staying active after completion dismiss: call `dismissUnifiedToggleInputToOmnibar` directly. - [x] Stale browsing-dialog flags: `setAsChatFirstPath` now resets all browsing-dialog flags to prevent `chatPathPhase` jumping to `.trackerToEOJ` from stale state. - [ ] **Hardcoded cohort override** (aataraxiaa): `FeatureFlag.swift` has a hardcoded test override — must revert before merge. ### PR #4591 — ui-polish - [x] Approved. Rebase on current base. ### PR #4855 — sr-feedback - [ ] **Hide toolbar + address bar**: During visit-site step, hide both toolbar and address bar via `chromeDelegate?.setBarsHidden(true/false)` — not just lock controls. Costas + Alessandro preference. Simplifies the code too. - [ ] **Use delegate**: `NewTabPageViewController` should access the parent via `NewTabPageControllerDelegate` instead of `(parent as? MainViewController)?` cast. - [ ] **Loading guard**: Move `currentTab?.isLoading != true` check into the `if` condition, not a standalone `guard` (avoids orphaned reference and blank NTP on early exit). - [ ] **Revert Search Experience screen copy**: `searchAndDuckAIOption` was incorrectly changed to `"Toggle between\nSearch and Ask AI"`. Must revert to `"Toggle between\nSearch and Duck.ai"` — the "Ask AI" rename is only for the DuckAIQuery experiment toggle (`DuckAIQueryExperiment.toggleAILabel`). --- ## 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](https://app.asana.com/1/137249556945/task/1214601039604921) once experiment name is confirmed. --- ## Resolved / Dismissed - **GJ6c** ✅ — No longer required. Address bar hidden for visit-site step (GJ3), so user can't trigger a search at that point. Costas confirmed a search after the trackers-blocked dialog is acceptable. - **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** ✅ — "Duck.ai" → "Ask.ai" in DuckAIQuery toggle (onboarding only, not standard toggle). `DuckAIQueryExperiment.toggleAILabel = "Ask.ai"`. - **"Try fire tabs" flash** ✅ — Suppressed via `isStillOnboarding()` in `DaxDialogs` for chat-path users. - **GJ7 (search-only path breakage)** ✅ — 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, toolbar 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 (DuckAIQueryExperiment, SearchExperience) | | `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 | Old | New | Status | |---|---|---|---| | `DuckAIQueryExperiment.toggleAILabel` (onboarding toggle) | `Duck.ai` | `Ask.ai` | ✅ Done | | `searchAndDuckAIOption` (Search Experience screen) | `Toggle between\nSearch and Duck.ai` | *(should remain Duck.ai)* | ❌ Needs revert | --- ## Experiment Config ```json { "experimentName": "TBD", "targets": [{ "localeLanguage": "en", "localeCountry": "US" }], "cohorts": ["control", "treatmentA", "treatmentB"] } ``` > Feature flag: `FeatureFlag.onboardingDuckAIQueryExperiment` > Cohort type: `FeatureFlag.DuckAIQueryExperimentCohort` (`.control`, `.treatmentA`, `.treatmentB`)