Offline Content Card Availability

This tutorial explains how to enable offline availability for Content Cards, allowing them to display when your app launches without a network connection.

Overview

When offline availability is enabled, Content Cards fetched from Adobe Journey Optimizer are persisted to the device. On the next app launch, the SDK loads them from disk before any network response arrives, so users see content immediately regardless of connectivity.

data-variant=info
data-slots=text
This feature is opt-in and must be enabled through the Adobe Journey Optimizer extension configuration in Adobe Experience Platform Data Collection (Tags). It requires AEPMessaging version 3.12.0 or later and AEPCore version 3.x.x or later.

Pre-requisites

  1. Integrate and register AEPMessaging extension in your app.
  2. Ensure you are using AEPMessaging version 3.12.0 or later and AEPCore version 3.x.x or later.

Step 1: Enable offline availability in Tags

  1. Sign in to Adobe Experience Platform Data Collection and open Tags.
  2. Select your mobile property and open the Adobe Journey Optimizer extension configuration.
  3. Enable the Content Card Offline Availability setting.
  4. Save and publish your Tags configuration.

Once enabled, Content Cards returned by any successful updatePropositionsForSurfaces call are automatically written to a disk cache.

Step 2: Fetch Content Cards as usual

No code changes are required to enable persistence. Continue calling updatePropositionsForSurfaces as you normally would. When the device is online and the call succeeds, the SDK automatically writes the returned cards to disk.

data-slots=heading, code
data-repeat=2
data-languages=Kotlin, Java

Kotlin

val homePageSurface = Surface("homepage")
Messaging.updatePropositionsForSurfaces(listOf(homePageSurface))

Java

final Surface homePageSurface = new Surface("homepage");
Messaging.updatePropositionsForSurfaces(Collections.singletonList(homePageSurface));

Step 3: Retrieve Content Cards — with or without a network call

This is where offline availability is leveraged. You can collect getContentCardUIFlow without calling updatePropositionsForSurfaces first in the current session. If a prior session successfully fetched cards for the surface and the offline feature was enabled, the SDK loads those cards from the disk cache and emits them immediately — even with no network connection.

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

class AepContentCardViewModel(
    private val contentCardUIProvider: ContentCardUIProvider
) : ViewModel() {

    private val _aepUIList = MutableStateFlow<List<AepUI<*, *>>>(emptyList())
    val aepUIList: StateFlow<List<AepUI<*, *>>> = _aepUIList.asStateFlow()

    // Cards are served from the disk cache if a prior session fetched them successfully —
    // no updatePropositionsForSurfaces call is needed in the current session
    val contentCardFlow = contentCardUIProvider.getContentCardUIFlow()

    init {
        viewModelScope.launch {
            contentCardFlow.collect { result ->
                result.onSuccess { aepUI -> _aepUIList.value = aepUI }
                result.onFailure { /* handle error */ }
            }
        }
    }
}
data-variant=warning
data-slots=text
getContentCardUIFlow only emits cards from the disk cache when all of the following conditions are met: the Content Card Offline Availability setting was enabled in Tags when the cards were originally fetched, a prior session had a successful updatePropositionsForSurfaces call for that surface, and the setting is still enabled in the current session. If the setting is disabled after cards have been persisted, the SDK clears the disk cache and filters out any disk-origin cards — so turning the feature off removes offline availability immediately, even for previously cached content. If none of the conditions are met, the flow emits an empty list until updatePropositionsForSurfaces succeeds in the current session.

Step 4: Clear the cache when needed

Use clearCachedPropositions to remove all persisted Content Cards and the in-memory cache. This is useful, for example, when a user logs out.

data-slots=heading, code
data-repeat=2
data-languages=Kotlin, Java

Kotlin

Messaging.clearCachedPropositions()

Java

Messaging.clearCachedPropositions();
data-variant=info
data-slots=text
Resetting identities via MobileCore.resetIdentities() also automatically clears persisted Content Cards, ensuring cached content is never carried across user identities.

How it works

Scenario
Behavior
App launches online, fetch succeeds
Cards returned from network; written to disk cache
App launches offline
Cards loaded from disk cache; shown while app waits for network
Fetch fails (network error)
Disk cache is preserved unchanged
Fetch succeeds after a failed attempt
Disk cache is updated with the new cards
clearCachedPropositions called
Memory and disk cache are both cleared
MobileCore.resetIdentities() called
Persisted cards are automatically cleared

Tracking and analytics

The SDK sends a decisioning.propositionDisplay event to Adobe Experience Platform when a Content Card is displayed. Each card item in that event includes a servedFromPersistentCache flag under items[].data.characteristics:

Value
Meaning
false
Card was served from a live network response in this session
true
Card was loaded from the disk cache (for example, at cold launch before a network response arrived)

You can use this flag in downstream reporting — for example in Customer Journey Analytics or by querying your Experience Event dataset — to distinguish network-served impressions from cache-served impressions.

The following example shows the relevant portion of a display event as captured in Assurance:

{
  "xdm": {
    "eventType": "decisioning.propositionDisplay",
    "_experience": {
      "decisioning": {
        "propositionEventType": { "display": 1 },
        "propositions": [
          {
            "id": "c1984e87-6c5e-4441-8691-f148af4975d8",
            "scope": "mobileapp://com.example.app/homepage",
            "items": [
              {
                "id": "cd20a906-f444-45fe-bdd8-868acc8a00b0",
                "data": {
                  "characteristics": {
                    "servedFromPersistentCache": true
                  }
                }
              }
            ]
          }
        ]
      }
    }
  }
}

What is and is not persisted

Content type
Persisted for offline availability
Content Cards
Yes
Inbox
No
Code-Based Experiences
No
In-App Messages
No