Tracking & Analytics (Web + Native)¶
This document describes how advertising/marketing tracking and analytics providers are wired in the investor app across native (iOS/Android) and web (React Native Web) platforms.
Architecture overview¶
┌─────────────────────────────────────────────────────────────┐
│ Root Provider Stack (_layout.tsx) │
│ │
│ ... > ProductTracking(PostHog) > AdvertisingTracking > ... │
│ ... > NavigationTrackingProvider > ... │
│ │
│ (authenticated) │
│ ... > UserTrackingProvider > ... │
└─────────────────────────────────────────────────────────────┘
Platform-specific implementations use Metro's .web.tsx resolution — no runtime platform checks needed.
Providers¶
AdvertisingTrackingProvider¶
| Native | Web | |
|---|---|---|
| File | src/providers/AdvertisingTrackingProvider/AdvertisingTrackingProvider.tsx |
...AdvertisingTrackingProvider.web.tsx |
| SDK | Facebook SDK + Firebase Analytics | Google Tag Manager (GTM) |
| Consent | ATT (App Tracking Transparency) | None in code (handled by GTM consent mode) |
| Init | Requests ATT → enables advertiser tracking + analytics collection | Loads GTM snippet if advertisingTrackingEnabled && GTM_ID |
| Events | AppEventsLogger (Facebook) + @react-native-firebase/analytics |
dataLayer.push({ event, ...params }) |
Event mapping (web): Brick event names are mapped to GTM-facing names via toGtmEventName():
| App event | GTM event |
|---|---|
AccountCreation |
register-form-submitted |
| (others) | see AdvertisingTrackingProvider.web.tsx |
GTM tags: Facebook Pixel and Google Analytics are configured as tags inside the GTM container — they are not loaded separately in app code.
Customer.io¶
| Native | Web | |
|---|---|---|
| File | src/core/customerIO/utils.tsx |
src/core/customerIO/utils.web.tsx + snippet.web.ts |
| SDK | customerio-reactnative + Expo plugin |
Customer.io CDP JS snippet (EU region) |
| Init | Expo plugin auto-initializes at native build time | loadCioSnippet(apiKey) runs at module evaluation |
| Screen views | CustomerIO.screen(name) |
window.cioanalytics.page(name) |
| Identify | CustomerIO.identify(userId) |
window.cioanalytics.identify(userId) |
| Reset | CustomerIO.clearIdentify() |
window.cioanalytics.reset() |
Integration points:
NavigationTrackingProvidercallstrackScreenView(viewName)on route changes (both platforms)UserTrackingProvidercallsidentifyUser(user.id)when the authenticated user is available- Push notifications pre-prompt (
useDisplayPrePromptIfApplicable) is a no-op on web (index.web.ts)
ProductTrackingProvider (PostHog)¶
Single implementation (ProductTrackingProvider.tsx), no .web.tsx split. Uses PostHog for product analytics events. Not part of advertising tracking.
Configuration¶
All values come from Doppler and are injected as EXPO_PUBLIC_* env vars (baked into the bundle at build time):
| Variable | Required | Purpose |
|---|---|---|
EXPO_PUBLIC_ADVERTISING_TRACKING_ENABLED |
Yes | Must be literal 'true' to enable advertising features |
EXPO_PUBLIC_GTM_ID |
Web only | GTM container ID. If unset, web advertising is disabled |
EXPO_PUBLIC_CUSTOMER_IO_API_KEY |
Yes | CDP write key (used by both native plugin and web snippet) |
EXPO_PUBLIC_CUSTOMER_IO_SITE_ID |
Yes | Site ID for native SDK |
Web builds (Docker)¶
The Dockerfile passes these as ARGs so they are available at build time:
ARG EXPO_PUBLIC_GTM_ID
ARG EXPO_PUBLIC_ADVERTISING_TRACKING_ENABLED
ARG EXPO_PUBLIC_CUSTOMER_IO_API_KEY
ARG EXPO_PUBLIC_CUSTOMER_IO_SITE_ID
Native builds (Expo)¶
Customer.io is configured in app.config.ts via customerio-expo-plugin:
["customerio-expo-plugin", {
config: {
cdpApiKey: process.env.EXPO_PUBLIC_CUSTOMER_IO_API_KEY,
siteId: process.env.EXPO_PUBLIC_CUSTOMER_IO_SITE_ID,
region: "eu",
},
}]
File map¶
src/
├── core/providers/
│ └── AdvertisingTrackingProvider/
│ ├── AdvertisingTrackingProvider.tsx (native: ATT + Facebook + Firebase)
│ ├── AdvertisingTrackingProvider.web.tsx (web: GTM dataLayer)
│ ├── gtm.web.ts (GTM snippet loader + pushToDataLayer)
│ └── types.ts (event names + params map)
├── core/customerIO/
│ ├── utils.tsx (native: customerio-reactnative)
│ ├── utils.web.tsx (web: CDP JS analytics)
│ ├── snippet.web.ts (web: script injection)
│ ├── index.ts (native: push pre-prompt)
│ └── index.web.ts (web: no-op push pre-prompt)
├── core/providers/
│ ├── NavigationTrackingProvider/ (screen views → Customer.io + Datadog)
│ └── UserTrackingProvider.tsx (identify on auth → Customer.io)
└── core/config/
└── config.ts (AppConfig: reads EXPO_PUBLIC_* env vars)
Adding a new tracking event¶
- Add the event name to
BricksAdvertisingEventintypes.ts - Add param types (if any) to
AdvertisingEventParamsMapintypes.ts - In native: add the case to
logAdvertisingEvent(usesAppEventsLogger+ Firebase) - In web: add the GTM event name mapping in
toGtmEventName()(AdvertisingTrackingProvider.web.tsx) - Configure the corresponding tag in the GTM container (external, not in code)
Troubleshooting¶
- GTM not firing on web dev: Check that
EXPO_PUBLIC_GTM_IDis set in your Dopplerdevelopementconfig - Customer.io not identifying:
UserTrackingProvideronly mounts inside the authenticated layout — unauthenticated screens won't identify - Native events not appearing: Verify ATT is granted (simulator always grants). Check Firebase Analytics debug view
- Web events missing in GTM: Use GTM Preview mode to verify
dataLayer.pushcalls are reaching the container