Skip to content

Logging analytics

If you would like to display the Content Cards in a completely custom manner, you can implement your own Content Cards presentation UI. However, data such as impressions, clicks, and dismissals are only handled automatically when using the default card models. When implementing completely custom UI, you will need to handle this data manually. To do this, populate your custom UI with data from the Braze data models and manually log analytics like impressions and clicks. Once you understand how to log analytics, you can see common ways Braze customers create custom Content Cards.

Listening for card updates

When implementing your custom Content Cards, you can parse the Content Card objects and extract their payload data such as title, cardDescription, and imageUrl. Then, you can use the resulting model data to populate your custom UI.

To obtain the Content Card data models, subscribe to Content Card updates. There are two properties to pay particular attention to:

  • id: Represents the Content Card ID string. This is the unique identifier used to log analytics from custom Content Cards.
  • extras: Encompasses all the key-value pairs from the Braze dashboard.

All properties outside of id and extras are optional to parse for custom Content Cards. For more information on the data model, see each platform’s integration article: Android, iOS, Web.

Step 1: Create a private subscriber variable

To subscribe to card updates, first declare a private variable in your custom class to hold your subscriber:

1
2
// subscriber variable
private IEventSubscriber<ContentCardsUpdatedEvent> mContentCardsUpdatedSubscriber;

Step 2: Subscribe to updates

Next, add the following code to subscribe to Content Card updates from Braze, typically inside of your custom Content Cards activity’s Activity.onCreate():

1
2
3
4
5
6
7
8
9
10
11
12
13
// Remove the previous subscriber before rebuilding a new one with our new activity.
Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class);
mContentCardsUpdatedSubscriber = new IEventSubscriber<ContentCardsUpdatedEvent>() {
    @Override
    public void trigger(ContentCardsUpdatedEvent event) {
        // List of all Content Cards
        List<Card> allCards = event.getAllCards();

        // Your logic below
    }
};
Braze.getInstance(context).subscribeToContentCardsUpdates(mContentCardsUpdatedSubscriber);
Braze.getInstance(context).requestContentCardsRefresh();

Step 3: Unsubscribe

We also recommend unsubscribing when your custom activity moves out of view. Add the following code to your activity’s onDestroy() lifecycle method:

1
Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class);

Step 1: Create a private subscriber variable

To subscribe to card updates, first declare a private variable in your custom class to hold your subscriber:

1
private var contentCardsUpdatedSubscriber: IEventSubscriber<ContentCardsUpdatedEvent>? = null

Step 2: Subscribe to updates

Next, add the following code to subscribe to Content Card updates from Braze, typically inside of your custom Content Cards activity’s Activity.onCreate():

1
2
3
4
5
6
7
8
9
10
// Remove the previous subscriber before rebuilding a new one with our new activity.
Braze.getInstance(context).subscribeToContentCardsUpdates(contentCardsUpdatedSubscriber)
Braze.getInstance(context).requestContentCardsRefresh()
  // List of all Content Cards
  val allCards = event.allCards

  // Your logic below
}
Braze.getInstance(context).subscribeToContentCardsUpdates(mContentCardsUpdatedSubscriber)
Braze.getInstance(context).requestContentCardsRefresh(true)

Step 3: Unsubscribe

We also recommend unsubscribing when your custom activity moves out of view. Add the following code to your activity’s onDestroy() lifecycle method:

1
Braze.getInstance(context).removeSingleSubscription(contentCardsUpdatedSubscriber, ContentCardsUpdatedEvent::class.java)

To access the Content Cards data model, call contentCards.cards on your braze instance.

1
let cards: [Braze.ContentCard] = AppDelegate.braze?.contentCards.cards

Additionally, you can also maintain a subscription to observe for changes in your Content Cards. You can do so in one of two ways:

  1. Maintaining a cancellable; or
  2. Maintaining an AsyncStream.

Cancellable

1
2
3
4
5
6
// This subscription is maintained through a Braze cancellable, which will observe for changes until the subscription is cancelled.
// You must keep a strong reference to the cancellable to keep the subscription active.
// The subscription is canceled either when the cancellable is deinitialized or when you call its `.cancel()` method.
let cancellable = AppDelegate.braze?.contentCards.subscribeToUpdates { [weak self] contentCards in
  // Implement your completion handler to respond to updates in `contentCards`.
}

AsyncStream

1
let stream: AsyncStream<[Braze.ContentCard]> = AppDelegate.braze?.contentCards.cardsStream
1
NSArray<BRZContentCardRaw *> *contentCards = AppDelegate.braze.contentCards.cards;

Additionally, if you wish to maintain a subscription to your content cards, you can call subscribeToUpdates:

1
2
3
4
// This subscription is maintained through Braze cancellable, which will continue to observe for changes until the subscription is cancelled.
BRZCancellable *cancellable = [self.braze.contentCards subscribeToUpdates:^(NSArray<BRZContentCardRaw *> *contentCards) {
  // Implement your completion handler to respond to updates in `contentCards`.
}];

Register a callback function to subscribe for updates when cards are refreshed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import * as braze from "@braze/web-sdk";

braze.subscribeToContentCardsUpdates((updates) => {
  const cards = updates.cards;
// For example:
  cards.forEach(card => {
    if (card.isControl) {
      // Do not display the control card, but remember to call `logContentCardImpressions([card])`
    }
    else if (card instanceof braze.ClassicCard || card instanceof braze.CaptionedImage) {
      // Use `card.title`, `card.imageUrl`, etc.
    }
    else if (card instanceof braze.ImageOnly) {
      // Use `card.imageUrl`, etc.
    }
  })
});

braze.openSession();

Logging events

Logging valuable metrics like impressions, clicks, and dismissals is quick and simple. Set a custom click listener to manually handle these analytics.

The BrazeManager can reference Braze SDK dependencies such as the Content Card objects array list to get the Card to call the Braze logging methods. Use the ContentCardable base class to easily reference and provide data to the BrazeManager.

To log an impression or click on a card, call Card.logClick() or Card.logImpression() respectively.

You can manually log or set a Content Card as “dismissed” to Braze for a particular card with isDismissed. If a card is already marked as dismissed, it cannot be marked as dismissed again.

To create a custom click listener, create a class that implements IContentCardsActionListener and register it with BrazeContentCardsManager. Implement the onContentCardClicked() method, which will be called when the user clicks a Content Card. Then, instruct Braze to use your Content Card click listener.

For example:

1
2
3
4
5
6
7
8
9
10
11
BrazeContentCardsManager.getInstance().setContentCardsActionListener(new IContentCardsActionListener() {
  @Override
  public boolean onContentCardClicked(Context context, Card card, IAction cardAction) {
    return false;
  }

  @Override
  public void onContentCardDismissed(Context context, Card card) {

  }
});

For example:

1
2
3
4
5
6
7
8
9
BrazeContentCardsManager.getInstance().contentCardsActionListener = object : IContentCardsActionListener {
  override fun onContentCardClicked(context: Context, card: Card, cardAction: IAction): Boolean {
    return false
  }

  override fun onContentCardDismissed(context: Context, card: Card) {

  }
}

Implement the BrazeContentCardUIViewControllerDelegate protocol and set your delegate object as the delegate property of your BrazeContentCardUI.ViewController. This delegate will handle passing the data of your custom object back to Braze to be logged. For an example, see Content Cards UI tutorial.

1
2
3
4
5
6
7
8
9
10
11
12
// Set the delegate when creating the Content Cards controller
contentCardsController.delegate = delegate

// Method to implement in delegate
func contentCard(
    _ controller: BrazeContentCardUI.ViewController,
    shouldProcess clickAction: Braze.ContentCard.ClickAction,
    card: Braze.ContentCard
  ) -> Bool {
  // Intercept the content card click action here.
  return true
}
1
2
3
4
5
6
7
8
9
10
// Set the delegate when creating the Content Cards controller
contentCardsController.delegate = delegate;

// Method to implement in delegate
- (BOOL)contentCardController:(BRZContentCardUIViewController *)controller
                shouldProcess:(NSURL *)url
                         card:(BRZContentCardRaw *)card {
  // Intercept the content card click action here.
  return YES;
}

Log impression events when cards are viewed by users using logContentCardImpressions:

1
2
3
import * as braze from "@braze/web-sdk";

braze.logContentCardImpressions([card1, card2, card3]);

Log card click events when users interact with a card using logContentCardClick:

1
2
3
import * as braze from "@braze/web-sdk";

braze.logContentCardClick(card);
HOW HELPFUL WAS THIS PAGE?
New Stuff!