15 minutes
h1

Choosing the wrong data collection architecture for Adobe Analytics on mobile locks your team into painful rework later. This article walks through the key SDK decisions, implementation patterns, and trade-offs practitioners need to get right from the start, whether you’re building on React Native, Android, or iOS.

Introduction: The architecture decision that shapes everything

If you’ve been working with Adobe Analytics on mobile apps for any length of time, you’ve probably hit that moment where someone asks: “Should we just use the old SDK or do we migrate to AEP?” If you’re building a brand-new React Native app today, that question has already been answered for you, just not by you.

Adobe ended support for the ACP-prefixed SDK libraries on August 31, 2023. If your team is building in React Native, Flutter or Xamarin, the legacy approach is off the table. The only supported path is the Adobe Experience Platform (AEP) Mobile SDK.

Why this decision matters more than most teams realize

For teams starting with the AEP Mobile SDK, the most important early question is: “How should we structure app payloads so Adobe Analytics works quickly today, while preserving flexibility for Customer Journey Analytics (CJA), RTCDP or broader AEP tomorrow?” Your architecture choice directly affects:

In practice, your architecture choice directly affects:

NOTE
This is less about right vs wrong and more about choosing the right adjustment for where your business is today - and where it's heading next

The big reality is that Adobe gives you multiple valid implementation paths and the best option genuinely depends on your end goal. Before a single line of tracking code is written, your team needs to have an honest conversation about:

This article is the guide I wish had existed when I started working through this implementation. It covers the full picture: why the architecture decision matters, how to set things up correctly on React Native Android and iOS, how to structure your XDM payloads for both immediate Adobe Analytics wins and long-term CJA and RTCDP readiness and how to validate everything with Adobe Assurance before your team ships.

Part 1: Why the Old SDK is gone and what replaced it

The end of ACP SDK support

Since April 2023, Apple has required all App Store submissions to be built with Xcode 14.1 or later. The ACP-based SDK extensions were built with older Xcode versions and became incompatible with iOS and iPadOS. Adobe officially ended support on August 31, 2023 for:

The official migration guidance is documented at Adobe's SDK End of Support page. It contains the migration matrix for each deprecated SDK - worth bookmarking.

The Version history that matters

Adobe's mobile SDK has gone through three major generations, and knowing which era you're in changes everything about how you implement:

Default alt

The full version comparison is documented in the AEP Mobile SDK Getting Started guide.

The critical react native clarification

Even with the newer AEP-prefixed libraries in React Native, the standalone Analytics extension is not supported. React Native apps must route Analytics data through the Edge Network, using either:

See the Current SDK Versions for React Native for the current state of what's supported and what's not.

What this means practically: React Native apps must route Analytics data through the Edge Network, using either the Edge Bridge extension (which preserves your trackState/trackAction calls) or the full Edge Network extension (which uses Edge.sendEvent with XDM).

Part 2: Three ways to send data - choosing your architecture

Approach 1: Edge network extension (full XDM)

The most modern and future-proof approach. Your app sends events using Edge.sendEvent() with an XDM-structured payload through the Edge Network to Analytics via your configured Datastream.

API:        Edge.sendEvent()

Extensions needed: Edge Network + Identity for Edge Network

XDM transform:   Client-side

CJA/RTCDP ready:  Yes

Platform support: iOS, Android, tvOS, Flutter, React Native

Approach 2: Edge bridge extension (migration-friendly)

If your team already has trackState and trackAction calls baked into the app and can’t afford a full rewrite, Edge Bridge intercepts those calls. It forwards the data through the Edge Network to Analytics — no XDM restructuring required on the app side.

API:        MobileCore.trackAction() / MobileCore.trackState()

Extensions needed: Edge Bridge + Edge Network + Identity for Edge Network

XDM transform:   Not required for Analytics

(server-side Data Prep if needed for other services)

CJA/RTCDP ready:  Yes (with Data Prep mapping)

Platform support: iOS, Android, tvOS, Flutter, React Native

Approach 3: Direct Analytics extension (legacy - limited platform support)

The traditional approach that maps directly to Adobe Analytics without going through the Edge Network. Still supported on iOS, Android and tvOS — but NOT supported on Flutter or React Native.

API:        MobileCore.trackAction() / MobileCore.trackState()

Extensions needed: Analytics extension + Identity for Experience Cloud ID Service

Platform support:  iOS  Android, tvOS ONLY - NOT Flutter, NOT React Native

The full extensions comparison is available in Adobe's Analytics implementation overview for the AEP Mobile SDK.

Part 3: The architecture decision that matters most

Legacy trackState/trackAction vs modern Edge.sendEvent

In the old SDK world, the method call itself told Adobe what kind of hit to send:

// Legacy - method call determines hit type:

trackState("Home", { "app.membershipTier": "gold" })  // → page view hit

trackAction("Login", { "app.loginStatus": "logged_in" }) // → custom link hit

// Then context data flows through Processing Rules:

app.membershipTier → eVar##

app.loginStatus   → eVar##

IMPORTANT
Adobe didn't remove the concept of page views vs interactions - it simply shifted that responsibility from the function name to the XDM payload design.

In the new AEP world with Edge.sendEvent(), Adobe no longer cares which function you called. It cares about the structure of your XDM payload:

// Modern AEP - payload structure determines hit type:

Edge.sendEvent() + ExperienceEvent / XDM / data object / Datastream / Data Prep

// Screen view equivalent (replaces trackState):

xdm.web.webPageDetails.name → sets the screen/page name

// Interaction equivalent (replaces trackAction):

xdm.web.webInteraction.name → sets the interaction name

xdm.web.webInteraction.type → "other" for in-app interactions

NOTE
You do NOT need trackState / trackAction APIs anymore if using Edge.sendEvent() correctly. Correct XDM mapping determines hit type, not the function call.

XDM vs Data Object vs Context Data: Choosing the right pattern

Unmapped XDM + context data: move fast without waiting on schema

Developers send custom fields inside XDM namespaces (xdm.app.*, xdm.user.*), and Adobe automatically forwards any field that doesn’t map to an Analytics variable as a context data variable (a.x.*). Your Analytics team then maps those keys via Processing Rules or Data Prep — with zero app releases required.

// Developer sends (no formal schema needed upfront):

xdm.app.contentSection = "travel"

xdm.user.sessionType  = "authenticated"

// Adobe auto-forwards unmapped fields as context data:

a.x.app.contentsection → available in Processing Rules

a.x.user.sessiontype  → available in Processing Rules

// Processing Rules / Data Prep maps them:

a.x.user.sessiontype → eVar##

Benefits:

Watch out for:

Friendly XDM + Data Prep: built for CJA and RTCDP readiness

Developers send business-readable field names inside custom XDM namespaces. The Analytics team maps those clean, meaningful names to Analytics variables in Data Prep, no Processing Rules required, and the same data is already structured for CJA and RTCDP when they come online.

// Developer sends (business-readable, inside XDM namespaces):

xdm.user.sessionType  = "authenticated"

xdm.app.membershipTier = "gold"

xdm.app.screenName   = "home-screen"

// Data Prep maps them to Analytics variables:

xdm.user.sessionType  → eVar##

xdm.app.membershipTier → eVar##

Benefits:

Watch out for:

Data object: when deployment speed takes priority

The most natural approach for developers from a web data layer background. Two sub-variants: direct Analytics variable mapping via the __adobe.analytics namespace, or freeform business-named keys mapped via Data Prep (recommended).

{

"data": {

"__adobe": {

"analytics": {

"eVar7": "gold",

"events": "event3"

}

}

}

Freeform variant - business-named keys, mapped via Data Prep (recommended over the direct variant):

{

"data": {

"membershipTier": "gold",

"sessionType": "authenticated"

}

// Data Prep maps: data.membershipTier → eVar##

Benefits:

Watch out for:

Key context data rule: what Adobe does with unmapped XDM fields

Adobe is explicit: “If using the XDM object, all fields that don’t map to an Adobe Analytics variable are automatically included as context data variables.” This is why many AA-first teams can move quickly without waiting for perfect schema design. Send the data now, map the variables when you’re ready.

Technical limits to plan around:

IMPORTANT
For CJA, RTCDP, or broader AEP use cases, proper schema is still the cleaner long-term choice. The unmapped XDM shortcut is excellent for getting AA live fast — not as a permanent governance strategy.

The Golden Rule: Business meaning first, variables later

Regardless of which pattern you choose, institutionalize this principle from day one:

Developers should always send business meaning. Never use Analytics variable names.

// Wrong — app code is coupled to Analytics variable numbers:

"eVar7": "gold"

// Right — app code expresses business meaning:

"membershipTier": "gold"

// The Analytics team maps it — without touching app code:

membershipTier → eVar## (in Data Prep or Processing Rules)

When the Analytics team reassigns variable numbers, and they will, nothing in your app code needs to change. The mapping lives in Data Prep or Processing Rules, completely decoupled from your release cycle.

Architecture decision guide

Default alt

NOTE
Use Edge.sendEvent as your universal event pipe. Let developers send meaningful business data, leverage Adobe’s automatic XDM-to-contextData behaviour when speed matters and use Data Prep plus schema governance to build an implementation that supports Adobe Analytics today while staying ready for CJA and RTCDP tomorrow.

Part 4: Setting up Adobe Analytics in a React Native App

Step 1: Install the required Packages

The React Native AEP SDK requires version 0.60.0 or later:

npm install @adobe/react-native-aepcore

npm install @adobe/react-native-aepedge

npm install @adobe/react-native-aepedgebridge

npm install @adobe/react-native-aepedgeidentity

npm install @adobe/react-native-aepedgeconsent

Always check developer.adobe.com/client-sdks/home/current-sdk-versions/#react-native — versions move quickly. Starting from AEP React Native 7.x, JavaScript-layer initialization is sufficient; no native-platform initialization is needed.

Step 2: Initialize the SDK

import { MobileCore, LogLevel } from "@adobe/react-native-aepcore";

import { Edge } from "@adobe/react-native-aepedge";

import { EdgeBridge } from "@adobe/react-native-aepedgebridge";

MobileCore.setLogLevel(LogLevel.VERBOSE);

MobileCore.initializeWithAppId("your-environment-id-here");

// Or with options:

MobileCore.initialize({ appId: "your-environment-id-here", lifecycleAutomaticTrackingEnabled: true });

Step 3: Lifecycle tracking

Wire lifecycle events into your app’s foreground/background transitions:

// On app foreground:

MobileCore.lifecycleStart({});

// On app background:

MobileCore.lifecyclePause();

Step 4: Tracking screen views and interactions

For the Edge Bridge approach (preserving trackState/trackAction):

// Screen view:

MobileCore.trackState("Home Screen", {

"app.section": "home", "user.sessionType": "authenticated"

});

// User interaction:

MobileCore.trackAction("Search Submitted", {

"search.term": "business travel", "search.resultCount": "12"

});

For the full Edge.sendEvent() approach (XDM-native):

import { Edge, ExperienceEvent } from "@adobe/react-native-aepedge";

const xdmData = {

eventType: "web.webPageDetails.pageViews",

web: { webPageDetails: { name: "home-screen", siteSection: "home" } },

user: { sessionType: "authenticated" }

};

Edge.sendEvent(new ExperienceEvent(xdmData));

Step 5: Identity management

import { Identity } from "@adobe/react-native-aepedgeidentity";

// Sync a customer identifier:

Identity.syncIdentifiers({

"customerid": { id: "user-abc-123", authState: "authenticated" }

});

// Retrieve the ECID:

Identity.getExperienceCloudId().then(ecid => console.log("ECID:", ecid));

Part 5: Setting up Adobe Analytics in an Android app

Step 1: Add gradle dependencies

Using the BOM is strongly recommended. It keeps all AEP extensions version-aligned automatically:

implementation platform("com.adobe.marketing.mobile3.+")

implementation "com.adobe.marketing.mobile:core"

implementation "com.adobe.marketing.mobile:edgeidentity"

implementation "com.adobe.marketing.mobile:edgeconsent"

implementation "com.adobe.marketing.mobile:identity"

implementation "com.adobe.marketing.mobile:lifecycle"

implementation "com.adobe.marketing.mobile:edge"

implementation "com.adobe.marketing.mobile:assurance"

Step 2: Initialize the SDK

In your Application class’s onCreate() method, MobileCore.initialize() automatically registers extensions and handles lifecycle tracking:

MobileCore.setLogLevel(LoggingMode.DEBUG);

MobileCore.initialize(this, "your-environment-id-here");

Use different environment IDs for development, staging and production. Remove verbose logging before shipping to production.

Step 3: App permissions

Add these to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Step 4: Lifecycle and tracking events

// Lifecycle in each Activity:

public void onResume() {

MobileCore.setApplication(getApplication());

MobileCore.lifecycleStart(null);

}

public void onPause() { MobileCore.lifecyclePause(); }

// Page view (screen view):

xdmData.put("eventType", "web.webPageDetails.pageViews");

// xdm.web.webPageDetails.name, siteSection, etc.

ExperienceEvent event = new ExperienceEvent.Builder().setXdmSchema(xdmData).build();

Edge.sendEvent(event, null);

Part 6: Setting Up Adobe Analytics in an iOS App (Swift)

Step 1: Add dependencies

For CocoaPods

use_frameworks!

pod "AEPCore",        "~> 5.0"

pod "AEPEdge",        "~> 5.0"

pod "AEPEdgeIdentity", "~> 5.0"

pod "AEPEdgeConsent",  "~> 5.0"

pod "AEPAssurance",    "~> 5.0"

pod "AEPLifecycle",    "~> 5.0"

pod "AEPIdentity",     "~> 5.0"

Step 2: Initialize the SDK

In your Application class’s onCreate() method, MobileCore.initialize() automatically registers extensions and handles lifecycle tracking:

MobileCore.setLogLevel(LoggingMode.DEBUG);

MobileCore.initialize(this, "your-environment-id-here");

For Swift Package Manager, see developer.adobe.com/client-sdks/resources/manage-spm-dependencies/.

Step 2: Initialize in AppDelegate

import AEPCore, AEPEdge, AEPEdgeIdentity, AEPEdgeConsent

import AEPIdentity, AEPLifecycle, AEPAssurance

MobileCore.setLogLevel(.debug)

MobileCore.initialize(appId: "your-environment-id-here")

Step 3: Lifecycle and tracking events (iOS)

// Lifecycle calls:

func applicationWillEnterForeground(_ application: UIApplication) {

MobileCore.lifecycleStart(additionalContextData: nil)

}

func applicationDidEnterBackground(_ application: UIApplication) {

MobileCore.lifecyclePause()

}

// Guard against background launches counting as sessions:

if application.applicationState != .background {

MobileCore.lifecycleStart(additionalContextData: nil)

}

// Screen view:

var xdmData: [String: Any] = [

"eventType": "web.webPageDetails.pageViews",

"web": ["webPageDetails": ["name": "home-screen", "siteSection": "home"]]

]

Edge.sendEvent(experienceEvent: ExperienceEvent(xdm: xdmData))

Part 7: Backend setup — schemas, datastreams & mobile property

Step 1: Create an XDM schema

In Adobe Experience Platform, navigate to Data Management > Schemas. Create a schema using the XDM Experience Event class and add these field groups:

Step 2: Create a dataset and configure a datastream

Create a dataset from your schema (use a clear naming convention that distinguishes it from web datasets, e.g. “Mobile App Events — Production”). Then in the Data Collection UI, create a Datastream and configure services:

Full Datastreams configuration guide: experienceleague.adobe.com/en/docs/experience-platform/datastreams/overview.

Step 3: Create a mobile property in Adobe Tags

In the Data Collection UI, create a new Tags property with the Mobile platform selected. Install these extensions:

After configuring extensions, publish through the library workflow (Development → Staging → Production). Each environment generates its own Environment ID. This is what you pass to MobileCore.initialize() in your app code.

Part 8: Validating your implementation with Adobe Assurance

Adobe Assurance (formerly Project Griffon) lets you see every event that fires from the app, inspect the full payload and confirm data is reaching Analytics correctly, all without touching production.

Setting up an assurance session

Go to experience.adobe.com/assurance and log in. Click Create Session, give the session a name and enter your app’s Base URL (e.g. myapp://). Scan the QR code or open the app via link, enter the PIN in the session dialog and tap Connect.

What to look for during validation

If data isn’t appearing in Analytics reports, Assurance is the fastest way to determine whether the issue is in the app (event not firing, wrong payload) or backend configuration (Datastream misconfigured, Processing Rules not applied).

Part 9: Hybrid app considerations — webviews and ECID stitching

If your app includes webviews, pass the ECID through to the webview URL so Analytics ties those sessions to the same visitor as the native app. Without this, Analytics sees them as separate visitors, inflating visitor counts and breaking any funnel analysis spanning both surfaces.

// Android:

Identity.appendVisitorInfoForURL("https://your-site.com/page", url -> {

webView.loadUrl(url);

});

// iOS (Swift):

Identity.appendTo(url: URL(string: "https://your-site.com/page")!) { url, error in

webView.load(URLRequest(url: url!))

}

Part 10: Common questions from the field

Can I reuse the same Report Suite as my previous app?

Yes, but consider the implications. The new implementation may fire different lifecycle events and hit types. Running both implementations against the same Report Suite simultaneously will produce messy data during the transition. The cleanest approach is a separate Report Suite for the new implementation until the migration is complete.

Do I need to define all custom fields in XDM schema upfront?

Not necessarily for AA-only implementations — unmapped XDM fields pass through as context data (a.x.*). But if CJA or RTCDP is on the roadmap, schema governance from the start saves significant rework. At minimum, define your key business dimensions as custom field groups even if not every field maps immediately.

What’s the difference between Identity for Edge Network and Identity for Experience Cloud ID Service?

They serve different purposes and should NOT be mixed. Use Identity for Edge Network (AEPEdgeIdentity) when routing through the Edge Network extension. Use Identity for Experience Cloud ID Service (AEPIdentity) with the direct Analytics extension.

When should I use Data Prep vs Processing Rules?

Data Prep is the more modern, scalable approach for new implementations. It configures field mappings in the Datastream before data reaches Analytics, supports transformations and conditional logic, and is essential for CJA and RTCDP. Processing Rules remain useful for simple mapping scenarios and are still fully supported.

How do I validate that context data is reaching Analytics?

Assurance is your first port of call — it shows the raw payload including all context data keys. For end-to-end validation, use the Processing Rules UI to preview incoming context data variable values. Real-time reports can confirm whether hits are landing.

Summary: Key decisions and where to focus

Default alt

The AEP Mobile SDK is well thought out once you understand the mental model shift from the old hit-based paradigm to the XDM event paradigm. The hardest part isn’t the code... It’s the architecture conversation that needs to happen before the code.

Take the time to get the backend setup right (schemas, datastreams, mobile property), validate everything with Assurance before you ship, and your team will thank you six months from now when the Analytics data is clean, the mappings are easy to maintain, and the path to CJA is already paved.