Ir al contenido

Crear Content Cards

Este artículo describe el enfoque básico que utilizarás al implementar Content Cards personalizadas, así como tres casos de uso comunes. Asume que ya has leído los demás artículos de la guía de personalización de Content Cards para comprender qué se puede hacer de forma predeterminada y qué requiere código personalizado. Es especialmente útil comprender cómo registrar los análisis de tus Content Cards personalizadas.

Creación de una tarjeta

Paso 1: Crear una interfaz personalizada

Primero, crea tu componente HTML personalizado que se utilizará para renderizar las tarjetas.

Primero, crea tu propio fragmento personalizado. El ContentCardsFragment predeterminado solo está diseñado para manejar nuestros tipos predeterminados de Content Cards, pero es un buen punto de partida.

Primero, crea tu propio componente de controlador de vista personalizado. El BrazeContentCardUI.ViewController predeterminado solo está diseñado para manejar nuestros tipos predeterminados de Content Cards, pero es un buen punto de partida.

Paso 2: Suscribirse a las actualizaciones de tarjetas

Registra una función de devolución de llamada para suscribirte a las actualizaciones de datos cuando se refresquen las tarjetas. Puedes analizar los objetos de Content Card y extraer los datos de su carga útil, como title, cardDescription e imageUrl, y luego usar los datos del modelo resultante para rellenar tu interfaz personalizada.

Para obtener los modelos de datos de Content Card, suscríbete a las actualizaciones de Content Cards. Presta especial atención a las siguientes propiedades:

  • id: Representa la cadena del ID de la Content Card. Es el identificador único utilizado para registrar análisis de Content Cards personalizadas.
  • extras: Incluye todos los pares clave-valor del panel de Braze.

Todas las propiedades fuera de id y extras son opcionales para analizar Content Cards personalizadas. Para más información sobre el modelo de datos, consulta el artículo de integración de cada plataforma: Android, iOS, Web.

Usa subscribeToContentCardsEvents para recibir eventos de Content Cards. El SDK llama a tu controlador con un objeto de evento. Filtra por event.type para manejar cada tipo de evento. Para más información sobre los valores de evento, consulta Suscripciones a eventos.

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

function renderCards(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.
    }
  });
}

// - Available in version 7.0.0+
const subscriptionId = braze.subscribeToContentCardsEvents((event) => {
  switch (event.type) {
    case braze.ChannelEventType.CACHE_REPLAY:
      // Sent once, right away, with the cards that are already cached.
      // Render them now instead of waiting for the network.
      renderCards(event.cacheSnapshot.contentCards.cards);
      break;

    case braze.ChannelEventType.CACHE_LOAD:
      // The cache changed without a refresh, such as after changeUser().
      // The snapshot can be empty, so clear cards from the previous user.
      renderCards(event.cacheSnapshot.contentCards.cards);
      break;

    case braze.ChannelEventType.DATA_UPDATED:
      // A refresh finished, even if no cards changed, or a card was dismissed.
      renderCards(event.cacheSnapshot.contentCards.cards);
      break;

    case braze.ChannelEventType.ERROR:
      switch (event.retryState) {
        case braze.RetryState.SDK_WILL_RETRY:
          // The SDK is retrying. Keep the current cards and wait.
          break;
        case braze.RetryState.INTEGRATOR_MAY_RETRY: {
          // The SDK stopped retrying. Try again later, and limit how often you retry.
          const delayMs = event.rateLimitedUntil
            ? Math.max(event.rateLimitedUntil.getTime() - Date.now(), 0)
            : 30000;
          setTimeout(() => braze.requestContentCardsRefresh(), delayMs);
          break;
        }
        case braze.RetryState.DO_NOT_RETRY:
          // The failure is final. For example, Content Cards are disabled for this workspace.
          if (event.reason === braze.ChannelErrorReason.FEATURE_DISABLED) {
            // Hide your Content Cards UI.
          }
          break;
      }
      break;
  }
});

const deprecatedSubscriptionId = braze.subscribeToContentCardsUpdates((updates) => {
  renderCards(updates.cards);
});

braze.openSession();

// Remove the subscription when you no longer need it
// braze.removeSubscription(subscriptionId);

Para saber cuándo se dispara cada evento y qué significan cada motivo de actualización, estado de reintento, acción de análisis y motivo de error, consulta Suscripciones a eventos.

Usa subscribeToContentCardsEvents en Web SDK 7.0.0 y versiones posteriores. subscribeToContentCardsUpdates es el patrón anterior, obsoleto a partir de la versión 7.0.0. El patrón anterior solo entrega las tarjetas actuales, por lo que no puede indicarte por qué cambiaron las tarjetas o cuándo falló un refresco.

Paso 2a: Crear una variable de suscriptor privada

Para suscribirte a las actualizaciones de tarjetas, primero declara una variable privada en tu clase personalizada para mantener tu suscriptor:

// - Available in version 44.0.0+
private IEventSubscriber<ContentCardsEvent> mContentCardsEventSubscriber;

private IEventSubscriber<ContentCardsUpdatedEvent> mContentCardsUpdatedSubscriber;

Paso 2b: Suscribirse a eventos

Añade el siguiente código para suscribirte con subscribeToContentCardsEvents(), normalmente dentro del Activity.onCreate() de tu actividad personalizada de Content Cards. Haz coincidencia de patrones con las subclases de ContentCardsEvent que necesites.

// - Available in version 44.0.0+
// Remove the previous subscriber before rebuilding a new one with our new activity.
Braze.getInstance(context).removeSingleSubscription(mContentCardsEventSubscriber, ContentCardsEvent.class);
mContentCardsEventSubscriber = new IEventSubscriber<ContentCardsEvent>() {
    @Override
    public void trigger(ContentCardsEvent event) {
        if (event instanceof ContentCardsEvent.CacheReplay) {
            handleCards(((ContentCardsEvent.CacheReplay) event).getCacheSnapshot());
        } else if (event instanceof ContentCardsEvent.CacheLoad) {
            handleCards(((ContentCardsEvent.CacheLoad) event).getCacheSnapshot());
        } else if (event instanceof ContentCardsEvent.DataUpdated) {
            handleCards(((ContentCardsEvent.DataUpdated) event).getCacheSnapshot());
        }
    }
};
Braze.getInstance(context).subscribeToContentCardsEvents(mContentCardsEventSubscriber);
Braze.getInstance(context).requestContentCardsRefresh();

// 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<Card> allCards = event.getAllCards();
    }
};
Braze.getInstance(context).subscribeToContentCardsUpdates(mContentCardsUpdatedSubscriber);
Braze.getInstance(context).requestContentCardsRefresh();

private void handleCards(ContentCardsCacheSnapshot cacheSnapshot) {
    List<Card> allCards = cacheSnapshot.getCards();
}

Los eventos llegan en un hilo en segundo plano. Cambia al hilo principal antes de actualizar las vistas.

Paso 2c: Cancelar la suscripción

Cancela la suscripción cuando tu actividad personalizada salga de la vista. Añade el siguiente código al método del ciclo de vida onDestroy() de tu actividad:

// - Available in version 44.0.0+
Braze.getInstance(context).removeSingleSubscription(mContentCardsEventSubscriber, ContentCardsEvent.class);

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

Paso 2a: Crear una variable de suscriptor privada

Para suscribirte a los eventos de tarjetas, primero declara una variable privada en tu clase personalizada para mantener tu suscriptor:

// - Available in version 44.0.0+
private var contentCardsEventSubscriber: IEventSubscriber<ContentCardsEvent>? = null

private var contentCardsUpdatedSubscriber: IEventSubscriber<ContentCardsUpdatedEvent>? = null

Paso 2b: Suscribirse a eventos

Añade el siguiente código para suscribirte con subscribeToContentCardsEvents(), normalmente dentro del Activity.onCreate() de tu actividad personalizada de Content Cards. Haz coincidencia de patrones con las subclases de ContentCardsEvent que necesites.

// - Available in version 44.0.0+
// Remove the previous subscriber before rebuilding a new one with our new activity.
Braze.getInstance(context).removeSingleSubscription(contentCardsEventSubscriber, ContentCardsEvent::class.java)
contentCardsEventSubscriber = IEventSubscriber { event ->
    when (event) {
        is ContentCardsEvent.CacheReplay -> handleCards(event.cacheSnapshot)
        is ContentCardsEvent.CacheLoad -> handleCards(event.cacheSnapshot)
        is ContentCardsEvent.DataUpdated -> handleCards(event.cacheSnapshot)
        else -> {}
    }
}
Braze.getInstance(context).subscribeToContentCardsEvents(contentCardsEventSubscriber)
Braze.getInstance(context).requestContentCardsRefresh()

// Remove the previous subscriber before rebuilding a new one with our new activity.
Braze.getInstance(context).removeSingleSubscription(contentCardsUpdatedSubscriber, ContentCardsUpdatedEvent::class.java)
contentCardsUpdatedSubscriber = IEventSubscriber { event ->
    val allCards = event.allCards
}
Braze.getInstance(context).subscribeToContentCardsUpdates(contentCardsUpdatedSubscriber)
Braze.getInstance(context).requestContentCardsRefresh()

private fun handleCards(cacheSnapshot: ContentCardsCacheSnapshot) {
    val allCards = cacheSnapshot.cards
}

Los eventos llegan en un hilo en segundo plano. Cambia al hilo principal antes de actualizar las vistas.

Paso 2c: Cancelar la suscripción

Cancela la suscripción cuando tu actividad personalizada salga de la vista. Añade el siguiente código al método del ciclo de vida onDestroy() de tu actividad:

// - Available in version 44.0.0+
Braze.getInstance(context).removeSingleSubscription(contentCardsEventSubscriber, ContentCardsEvent::class.java)

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

Para saber cuándo se dispara cada evento y qué significan cada motivo de actualización, estado de reintento, acción de análisis y motivo de error, consulta Suscripciones a eventos.

Usa subscribeToContentCardsEvents en Android SDK 44.0.0 y versiones posteriores. subscribeToContentCardsUpdates es el patrón anterior, obsoleto a partir de la versión 44.0.0.

Para acceder al modelo de datos de Content Cards, llama a contentCards.cards en tu instancia de braze.

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

Además, puedes suscribirte a los eventos de Content Cards para observar cambios de caché, análisis y errores. Puedes hacerlo de dos maneras:

  1. Manteniendo un cancelable; o
  2. Manteniendo un AsyncStream.
Cancelable
// - Available in version 19.0.0+
// This subscription is maintained through a Braze cancellable, which will observe for events 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.subscribeToEvents { [weak self] event in
  switch event {
  case .cacheReplay(let cacheSnapshot):
    // Initial cache snapshot, delivered immediately after subscribing
    break
  case .cacheLoad(let cacheSnapshot):
    // Cache loaded at the start of a user session (for example, after `changeUser()`)
    break
  case .dataUpdated(let cacheSnapshot, let reason):
    // Cache changed after the initial replay
    break
  case .impressionEvent(let card, let action):
    break
  case .clickEvent(let card, let action):
    break
  case .dismissEvent(let card, let action):
    break
  case .error(let reason, let retryState):
    break
  }
}

let cancellable = AppDelegate.braze?.contentCards.subscribeToUpdates { [weak self] contentCards in
  // Implement your completion handler to respond to updates in `contentCards`.
}
AsyncStream
// - Available in version 19.0.0+
Task {
  for await event in AppDelegate.braze?.contentCards.eventsStream ?? AsyncStream { _ in } {
    // Same switch statement as the cancellable example above.
  }
}

let stream: AsyncStream<[Braze.ContentCard]> = AppDelegate.braze?.contentCards.cardsStream

Usa subscribeToEvents(_:) o eventsStream en Swift SDK 19.0.0 y versiones posteriores. subscribeToUpdates(_:) y cardsStream son el patrón anterior, obsoleto a partir de la versión 19.0.0.

NSArray<BRZContentCardRaw *> *contentCards = AppDelegate.braze.contentCards.cards;

Además, si deseas suscribirte a los eventos de Content Cards, puedes llamar a subscribeToEvents:. Cada tipo de evento se conecta a su propia clase (por ejemplo, BRZContentCardsDataUpdatedEvent), que puedes discriminar con isKindOfClass:. La reproducción inicial de la caché y las actualizaciones de datos posteriores se conectan a BRZContentCardsDataUpdatedEvent; comprueba reason contra BRZContentCardsDataUpdatedEvent.cacheReplayReason para distinguirlas:

// - Available in version 19.0.0+
// This subscription is maintained through a Braze cancellable, which will continue to observe for events until the subscription is cancelled.
BRZCancellable *cancellable = [self.braze.contentCards subscribeToEvents:^(BRZContentCardsEvent *event) {
  if ([event isKindOfClass:[BRZContentCardsDataUpdatedEvent class]]) {
    BRZContentCardsDataUpdatedEvent *updated = (BRZContentCardsDataUpdatedEvent *)event;
    if (updated.reason == BRZContentCardsDataUpdatedEvent.cacheReplayReason) {
      // Initial cache snapshot, delivered immediately after subscribing
    } else {
      // Cache changed after the initial replay
    }
  } else if ([event isKindOfClass:[BRZContentCardsCacheLoadEvent class]]) {
    // Cache loaded at the start of a user session (for example, after `changeUser()`)
  }
}];

BRZCancellable *cancellable = [self.braze.contentCards subscribeToUpdates:^(NSArray<BRZContentCardRaw *> *contentCards) {
  // Implement your completion handler to respond to updates in `contentCards`.
}];

Usa subscribeToEvents: en Swift SDK 19.0.0 y versiones posteriores. subscribeToUpdates: es el patrón anterior, obsoleto a partir de la versión 19.0.0.

Para saber cuándo se dispara cada evento y qué significan cada motivo de actualización, estado de reintento, acción de análisis y motivo de error, consulta Suscripciones a eventos.

Paso 3: Implementar análisis

Las impresiones, clics y descartes de Content Cards no se registran automáticamente en tu vista personalizada. Debes implementar cada método respectivo para registrar correctamente todas las métricas en los análisis del panel de Braze.

Paso 4: Probar tu tarjeta (opcional)

Para probar tu Content Card:

  1. Establece un usuario activo en tu aplicación llamando al método changeUser().
  2. En Braze, ve a Campaigns y luego crea una nueva campaña de Content Card.
  3. En tu campaña, selecciona Test e introduce el user-id del usuario de prueba. Cuando estés listo, selecciona Send Test. Podrás lanzar una Content Card en tu dispositivo en breve.

Una campaña de Content Card en Braze que muestra cómo puedes añadir tu propio ID de usuario como destinatario de prueba para probar tu Content Card.

Ubicaciones de Content Cards

Las Content Cards pueden utilizarse de muchas formas diferentes. Tres implementaciones comunes son usarlas como centro de mensajes, como anuncio de imagen dinámica o como carrusel de imágenes. Para cada una de estas ubicaciones, asignarás pares clave-valor (la propiedad extras en el modelo de datos) a tus Content Cards, y según los valores, ajustarás dinámicamente el comportamiento, la apariencia o la funcionalidad de la tarjeta durante el tiempo de ejecución.

Diagrama que muestra tres ejemplos de ubicación de Content Cards: buzón de mensajes, anuncio de imagen dinámica y carrusel de imágenes.

Buzón de mensajes

Las Content Cards pueden utilizarse para simular un centro de mensajes. En este formato, cada mensaje es su propia tarjeta que contiene pares clave-valor que controlan los eventos al hacer clic. Estos pares clave-valor son los identificadores clave que la aplicación examina cuando decide a dónde dirigirse cuando el usuario hace clic en un mensaje del buzón de entrada. Los valores de los pares clave-valor son arbitrarios.

Ejemplo

Por ejemplo, puede que quieras crear dos tarjetas de mensaje: una llamada a la acción para que los usuarios habiliten las recomendaciones de lectura y un código de cupón para tu nuevo Segment de suscriptores.

Las claves como body, title y buttonText pueden tener valores de cadena simples que tus especialistas en marketing pueden configurar. Las claves como terms pueden tener valores que proporcionan una pequeña colección de frases aprobadas por tu departamento legal. Las claves como style y class_type tienen valores de cadena que puedes configurar para determinar cómo se renderiza tu tarjeta en tu aplicación o sitio.

Pares clave-valor para la tarjeta de recomendaciones de lectura:

Clave Valor
body Add your interests to your Politer Weekly profile for personal reading recommendations.
style info
class_type notification_center
card_priority 1

Pares clave-valor para un cupón de nuevo suscriptor:

Clave Valor
title Subscribe for unlimited games
body End of Summer Special - Enjoy 10% off Politer games
buttonText Subscribe Now
style promo
class_type notification_center
card_priority 2
terms new_subscribers_only
Información adicional para Android

En el SDK de Android y FireOS, la lógica del centro de mensajes se basa en el valor class_type que proporcionan los pares clave-valor de Braze. Usando el método createContentCardable, puedes filtrar e identificar estos tipos de clase.

Uso de class_type para el comportamiento al hacer clic
Cuando inflamos los datos de Content Card en nuestras clases personalizadas, usamos la propiedad ContentCardClass de los datos para determinar qué subclase concreta debe usarse para almacenar los datos.

 private fun createContentCardable(metadata: Map<String, Any>, type: ContentCardClass?): ContentCardable?{
        return when(type){
            ContentCardClass.AD -> Ad(metadata)
            ContentCardClass.MESSAGE_WEB_VIEW -> WebViewMessage(metadata)
            ContentCardClass.NOTIFICATION_CENTER -> FullPageMessage(metadata)
            ContentCardClass.ITEM_GROUP -> Group(metadata)
            ContentCardClass.ITEM_TILE -> Tile(metadata)
            ContentCardClass.COUPON -> Coupon(metadata)
            else -> null
        }
    }

Luego, al gestionar la interacción del usuario con la lista de mensajes, podemos usar el tipo de mensaje para determinar qué vista mostrar al usuario.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //...
        listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
           when (val card = dataProvider[position]){
                is WebViewMessage -> {
                    val intent = Intent(this, WebViewActivity::class.java)
                    val bundle = Bundle()
                    bundle.putString(WebViewActivity.INTENT_PAYLOAD, card.contentString)
                    intent.putExtras(bundle)
                    startActivity(intent)
                }
                is FullPageMessage -> {
                    val intent = Intent(this, FullPageContentCard::class.java)
                    val bundle = Bundle()
                    bundle.putString(FullPageContentCard.CONTENT_CARD_IMAGE, card.icon)
                    bundle.putString(FullPageContentCard.CONTENT_CARD_TITLE, card.messageTitle)
                    bundle.putString(FullPageContentCard.CONTENT_CARD_DESCRIPTION, card.cardDescription)
                    intent.putExtras(bundle)
                    startActivity(intent)
                }
            }

        }
    }

Uso de class_type para el comportamiento al hacer clic
Cuando inflamos los datos de Content Card en nuestras clases personalizadas, usamos la propiedad ContentCardClass de los datos para determinar qué subclase concreta debe usarse para almacenar los datos.

private ContentCardable createContentCardable(Map<String, ?> metadata,  ContentCardClass type){
    switch(type){
        case ContentCardClass.AD:{
            return new Ad(metadata);
        }
        case ContentCardClass.MESSAGE_WEB_VIEW:{
            return new WebViewMessage(metadata);
        }
        case ContentCardClass.NOTIFICATION_CENTER:{
            return new FullPageMessage(metadata);
        }
        case ContentCardClass.ITEM_GROUP:{
            return new Group(metadata);
        }
        case ContentCardClass.ITEM_TILE:{
            return new Tile(metadata);
        }
        case ContentCardClass.COUPON:{
            return new Coupon(metadata);
        }
        default:{
            return null;
        }
    }
}

Luego, al gestionar la interacción del usuario con la lista de mensajes, podemos usar el tipo de mensaje para determinar qué vista mostrar al usuario.

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState)
        //...
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id){
               ContentCardable card = dataProvider.get(position);
               if (card instanceof WebViewMessage){
                    Bundle intent = new Intent(this, WebViewActivity.class);
                    Bundle bundle = new Bundle();
                    bundle.putString(WebViewActivity.INTENT_PAYLOAD, card.getContentString());
                    intent.putExtras(bundle);
                    startActivity(intent);
                }
                else if (card instanceof FullPageMessage){
                    Intent intent = new Intent(this, FullPageContentCard.class);
                    Bundle bundle = Bundle();
                    bundle.putString(FullPageContentCard.CONTENT_CARD_IMAGE, card.getIcon());
                    bundle.putString(FullPageContentCard.CONTENT_CARD_TITLE, card.getMessageTitle());
                    bundle.putString(FullPageContentCard.CONTENT_CARD_DESCRIPTION, card.getCardDescription());
                    intent.putExtras(bundle)
                    startActivity(intent)
                }
            }

        });
    }

Puedes configurar Content Cards en tu fuente de carrusel totalmente personalizada, lo que permite a los usuarios deslizar y ver tarjetas destacadas adicionales. De forma predeterminada, las Content Cards se ordenan por fecha de creación (las más recientes primero) y tus usuarios verán todas las tarjetas para las que sean elegibles.

Para implementar un carrusel de Content Cards:

  1. Crea una lógica personalizada que observe los cambios en tus Content Cards y gestione la llegada de Content Cards.
  2. Crea una lógica personalizada del lado del cliente para mostrar un número específico de tarjetas en el carrusel en un momento dado. Por ejemplo, podrías seleccionar los primeros cinco objetos de Content Card del arreglo o introducir pares clave-valor para construir lógica condicional.

Solo imagen

Las Content Cards no tienen que verse como “tarjetas”. Por ejemplo, las Content Cards pueden aparecer como una imagen dinámica que se muestra de forma persistente en tu página de inicio o en la parte superior de páginas designadas.

Para lograr esto, tus especialistas en marketing crearán una Campaign o un paso en Canvas con un tipo de Content Card Solo imagen. Luego, configura pares clave-valor que sean apropiados para usar Content Cards como contenido suplementario.

New Stuff!