7.6 KiB
Pixel Definition: iOS Day-7 Subscription Promo Half-Sheet
Background
- Project: iOS: Show subscription half-sheet promo to new installers who don't see it during onboarding
- Android counterpart: Grow active subscriptions by increasing paywall impressions with a day 7 promo half-sheet — the iOS implementation follows the same approach and target audience as Android
- Origin request: Request origin for iOS subscription promo half-sheet entry point — origin value
funnel_modal_ios__subscriptionnudge(Android equivalent:funnel_newusermodal_android) - We are adding a new half-sheet modal for existing users (7+ days since install) who have never seen any subscription offer. The modal reuses the pre-existing
SubscriptionPromoLaunchViewUI. - Existing subscription promo pixels already cover related audiences:
m_privacy-pro_onboarding_promotion_impression/tap/dismiss— users who see the promo during onboardingm_privacy-pro_skipped_onboarding_promotion_impression/tap/dismiss— reinstallers who skipped onboarding (7+ days)
Objective
- Instrument the day-7 existing-user promo half-sheet with impression, tap, and dismiss pixels.
- These are permanent pixels (no expiry) — the feature is not an experiment.
- The origin value
funnel_modal_ios__subscriptionnudgeflows downstream automatically tom_privacy-pro_offer_screen_impressionandm_subscribewithout additional work.
Android reference
Android uses a single shared pixel (AppPixelName.ONBOARDING_DAX_CTA_SHOWN, ctaId DAX_INTRO_PRIVACY_PRO) for both the reinstaller promo and the day-7 promo, with no origin pixel parameter — origin is only embedded in the subscription purchase URL (https://duckduckgo.com/pro?origin=…) for downstream funnel attribution:
| Android audience | Pixel name | origin in URL |
|---|---|---|
| Reinstaller / skipped onboarding | ONBOARDING_DAX_CTA_SHOWN |
funnel_skippedonboarding_android |
| Day-7 existing users (new) | ONBOARDING_DAX_CTA_SHOWN |
funnel_newusermodal_android |
The two audiences are indistinguishable at the pixel level on Android — analysis relies on the origin flowing to downstream purchase pixels. Source: Android PR #8369, BrowserTabViewModel.kt + CtaViewModel.kt.
Android pixel definition / privacy triage tasks cover Phase 1 paywall instrumentation only (m_subscription_paywall_not_seen_*) — the promo modal itself got no separate pixel definition or privacy triage:
- Define pixels to track users who haven't seen the subscription paywall
- Privacy Triage: Android - Added new subscription_promo_shown pixel param
Option A — Recommended: Reuse existing pixels (mirrors Android approach)
Reuse m_privacy-pro_skipped_onboarding_promotion_impression/tap/dismiss for the day-7 audience. Audience attribution relies on the origin URL parameter (funnel_modal_ios__subscriptionnudge) flowing to downstream purchase pixels — identical to Android's approach.
No new pixels required. No privacy triage required for new pixels.
| Pixel | Trigger |
|---|---|
m_privacy-pro_skipped_onboarding_promotion_impression |
Day-7 half-sheet shown |
m_privacy-pro_skipped_onboarding_promotion_tap |
User taps CTA |
m_privacy-pro_skipped_onboarding_promotion_dismiss |
User dismisses without tapping CTA |
Trade-off: Cross-audience analysis (reinstallers vs day-7 users) is not directly available from pixels alone; it requires joining on origin via downstream m_privacy-pro_offer_screen_impression / m_subscribe pixels.
Option B — Alternative: Separate pixels per audience
Define three new pixels exclusively for the day-7 audience. Provides direct per-audience split in pixel dashboards without filtering by origin. Requires a new privacy triage.
Testing
- Feature is implemented in:
iOS/DuckDuckGo/SubscriptionPromo/SubscriptionPromoExistingUserCoordinator.swift - Test paths:
- Install app, complete or skip onboarding
- Wait 1 minute (1-min override active — revert to 7 days before shipping)
- Background and re-foreground app → promo half-sheet should appear
- Verify
m_privacy-pro_existing_user_promotion_impressionfires on display - Tap CTA → verify
m_privacy-pro_existing_user_promotion_tapfires and subscription flow opens with originfunnel_modal_ios__subscriptionnudge - Re-install and dismiss instead → verify
m_privacy-pro_existing_user_promotion_dismissfires
Pixel definitions
m_privacy-pro_existing_user_promotion_impression
- Trigger: The day-7 promo half-sheet is shown to an existing user (7+ days since install) who has never seen any subscription offer.
- Frequency: Once per install (guarded by
daxDialogsSettings.subscriptionPromotionDialogShown). - Fired from:
SubscriptionPromoExistingUserCoordinator.markLaunchPromptPresented() - Parameters:
appVersion— app version (added by default)ru—true/false— whether the user is a returning user (reinstall)free_trial—true/false— whether the user is eligible for a free trial at time of display
- Suffixes:
_ios,_phone/_tablet
m_privacy-pro_existing_user_promotion_tap
- Trigger: User taps the CTA button ("Try It Free" or "Learn More") on the promo modal.
- Frequency: Standard (can fire once per impression, since the modal is shown at most once).
- Fired from:
SubscriptionPromoExistingUserCoordinator.handleCTAAction() - Parameters: same as impression —
appVersion,ru,free_trial - Suffixes:
_ios,_phone/_tablet
m_privacy-pro_existing_user_promotion_dismiss
- Trigger: User dismisses the promo modal (taps X or swipes down) without tapping the CTA.
- Frequency: Standard.
- Fired from:
SubscriptionPromoExistingUserCoordinator.handleDismissAction()andUIAdaptivePresentationControllerDelegate(swipe-to-dismiss) - Parameters: same as impression —
appVersion,ru,free_trial - Suffixes:
_ios,_phone/_tablet
Behavior rules
- Impression and tap/dismiss are mutually exclusive per install — the modal is shown at most once.
ruandfree_trialare evaluated at time of display, same as in the skipped-onboarding promo.- The dismiss pixel fires on both explicit close (X button) and swipe-to-dismiss; neither path fires the tap pixel.
- If the user taps CTA, tap fires but dismiss does not.
Trade-off: Three new pixels to triage. Structurally identical to m_privacy-pro_skipped_onboarding_promotion_* — no new parameter types are introduced.
Notes and status
- Option A pixels are already committed to
iOS/PixelDefinitions/pixels/definitions/subscription.json5asm_privacy-pro_skipped_onboarding_promotion_*; reusing them for this audience requires no code changes to pixel definitions. - If Option B is chosen, new definitions must be added to
subscription.json5and a privacy triage opened. - Origin
funnel_modal_ios__subscriptionnudgeneeds formal approval via the Origin Proposal form — tracked in the origin request subtask above — regardless of which option is selected. - Privacy triage draft (for Option B): see
ios-subscription-day7-promo-privacy-triage.md.