Ir para o conteúdo

Criar Content Cards

Este artigo discute a abordagem básica que você usará ao implementar Content Cards personalizados, bem como três casos de uso comuns. Ele pressupõe que você já tenha lido os outros artigos do guia de personalização de Content Cards para entender o que pode ser feito por padrão e o que requer código personalizado. É especialmente útil entender como registrar análise de dados para seus Content Cards personalizados.

Criando um cartão

Etapa 1: Crie uma interface personalizada

Primeiro, crie seu componente HTML personalizado que será usado para renderizar os cartões.

Primeiro, crie seu próprio fragment personalizado. O ContentCardsFragment padrão foi projetado apenas para lidar com nossos tipos padrão de Content Cards, mas é um bom ponto de partida.

Primeiro, crie seu próprio componente de view controller personalizado. O BrazeContentCardUI.ViewController padrão foi projetado apenas para lidar com nossos tipos padrão de Content Cards, mas é um bom ponto de partida.

Etapa 2: Inscreva-se para atualizações de cartões

Registre uma função de retorno de chamada para se inscrever em atualizações de dados quando os cartões forem atualizados. Você pode analisar os objetos de Content Cards e extrair seus dados de carga útil, como title, cardDescription e imageUrl, e então usar os dados do modelo resultante para preencher sua interface personalizada.

Para obter os modelos de dados de Content Cards, inscreva-se nas atualizações de Content Cards. Preste atenção especial às seguintes propriedades:

  • id: Representa a string de ID do Content Card. Este é o identificador exclusivo usado para registrar dados de análise de Content Cards personalizados.
  • extras: Abrange todos os pares de chave-valor do dashboard da Braze.

Todas as propriedades fora de id e extras são opcionais para análise de Content Cards personalizados. Para saber mais sobre o modelo de dados, consulte o artigo de integração de cada plataforma: Android, iOS, Web.

Use subscribeToContentCardsEvents para receber eventos de Content Cards. O SDK chama seu handler com um objeto de evento. Use event.type para lidar com cada tipo de evento. Para saber mais sobre os valores de evento, consulte Inscrições de 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 quando cada evento é disparado e o que cada motivo de atualização, estado de nova tentativa, ação de análise e motivo de erro significa, consulte Inscrições de eventos.

Use subscribeToContentCardsEvents no Web SDK 7.0.0 e versões posteriores. subscribeToContentCardsUpdates é o padrão anterior, descontinuado a partir da versão 7.0.0. O padrão anterior só entrega os cartões atuais, então não é possível saber por que os cartões mudaram ou quando uma atualização falhou.

Etapa 2a: Crie uma variável de inscrição privada

Para se inscrever nas atualizações de cartões, primeiro declare uma variável privada em sua classe personalizada para armazenar seu assinante:

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

private IEventSubscriber<ContentCardsUpdatedEvent> mContentCardsUpdatedSubscriber;

Etapa 2b: Inscreva-se nos eventos

Adicione o código a seguir para se inscrever com subscribeToContentCardsEvents(), normalmente dentro do Activity.onCreate() da sua activity personalizada de Content Cards. Faça correspondência de padrão das subclasses de ContentCardsEvent que você precisa.

// - 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();
}

Os eventos chegam em uma thread em segundo plano. Mude para a thread principal antes de atualizar as views.

Etapa 2c: Cancele a inscrição

Cancele a inscrição quando sua activity personalizada sair da tela. Adicione o código a seguir ao método de ciclo de vida onDestroy() da sua activity:

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

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

Etapa 2a: Crie uma variável de inscrição privada

Para se inscrever nos eventos de cartões, primeiro declare uma variável privada em sua classe personalizada para armazenar seu assinante:

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

private var contentCardsUpdatedSubscriber: IEventSubscriber<ContentCardsUpdatedEvent>? = null

Etapa 2b: Inscreva-se nos eventos

Adicione o código a seguir para se inscrever com subscribeToContentCardsEvents(), normalmente dentro do Activity.onCreate() da sua activity personalizada de Content Cards. Faça correspondência de padrão das subclasses de ContentCardsEvent que você precisa.

// - 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
}

Os eventos chegam em uma thread em segundo plano. Mude para a thread principal antes de atualizar as views.

Etapa 2c: Cancele a inscrição

Cancele a inscrição quando sua activity personalizada sair da tela. Adicione o código a seguir ao método de ciclo de vida onDestroy() da sua activity:

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

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

Para saber quando cada evento é disparado e o que cada motivo de atualização, estado de nova tentativa, ação de análise e motivo de erro significa, consulte Inscrições de eventos.

Use subscribeToContentCardsEvents no Android SDK 44.0.0 e versões posteriores. subscribeToContentCardsUpdates é o padrão anterior, descontinuado a partir da versão 44.0.0.

Para acessar o modelo de dados de Content Cards, chame contentCards.cards na sua instância braze.

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

Além disso, você pode se inscrever nos eventos de Content Cards para observar alterações no cache, análises e erros. Você pode fazer isso de duas maneiras:

  1. Mantendo um cancellable; ou
  2. Mantendo um AsyncStream.
Cancellable
// - 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

Use subscribeToEvents(_:) ou eventsStream no Swift SDK 19.0.0 e versões posteriores. subscribeToUpdates(_:) e cardsStream são o padrão anterior, descontinuado a partir da versão 19.0.0.

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

Além disso, se você quiser se inscrever nos eventos de Content Cards, pode chamar subscribeToEvents:. Cada tipo de evento é conectado à sua própria classe (por exemplo, BRZContentCardsDataUpdatedEvent), que você pode discriminar com isKindOfClass:. O replay inicial do cache e as atualizações de dados subsequentes são ambos conectados a BRZContentCardsDataUpdatedEvent; verifique reason em comparação com BRZContentCardsDataUpdatedEvent.cacheReplayReason para diferenciá-los:

// - 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`.
}];

Use subscribeToEvents: no Swift SDK 19.0.0 e versões posteriores. subscribeToUpdates: é o padrão anterior, descontinuado a partir da versão 19.0.0.

Para saber quando cada evento é disparado e o que cada motivo de atualização, estado de nova tentativa, ação de análise e motivo de erro significa, consulte Inscrições de eventos.

Etapa 3: Implemente a análise de dados

As impressões, cliques e descartes de Content Cards não são registrados automaticamente em sua view personalizada. Você deve implementar cada método correspondente para registrar corretamente todas as métricas de volta na análise de dados do dashboard da Braze.

Etapa 4: Teste seu cartão (opcional)

Para testar seu Content Card:

  1. Defina um usuário ativo em seu app chamando o método changeUser().
  2. Na Braze, acesse Campaigns e crie uma nova Campaign de Content Card.
  3. Na sua Campaign, selecione Test e insira o user-id do usuário teste. Quando estiver pronto, selecione Send Test. Você poderá lançar um Content Card no seu dispositivo em breve.

Uma Campaign de Content Card da Braze mostrando que você pode adicionar seu próprio ID de usuário como destinatário de teste para testar seu Content Card.

Posicionamentos de Content Cards

Os Content Cards podem ser usados de diversas maneiras. Três implementações comuns são como uma central de mensagens, um anúncio de imagem dinâmica ou um carrossel de imagens. Para cada um desses posicionamentos, você atribuirá pares de chave-valor (a propriedade extras no modelo de dados) aos seus Content Cards, e com base nos valores, ajustará dinamicamente o comportamento, a aparência ou a funcionalidade do cartão durante a execução.

Diagrama mostrando três exemplos de posicionamento de Content Cards: caixa de entrada de mensagens, anúncio de imagem dinâmica e carrossel de imagens.

Central de mensagens

Os Content Cards podem ser usados para simular uma central de mensagens. Nesse formato, cada mensagem é seu próprio cartão que contém pares de chave-valor que controlam eventos ao clicar. Esses pares de chave-valor são os identificadores-chave que o aplicativo analisa para decidir para onde direcionar o usuário quando ele clica em uma mensagem da caixa de entrada. Os valores dos pares de chave-valor são arbitrários.

Exemplo

Por exemplo, digamos que você queira criar dois cartões de mensagem: uma chamada para ação incentivando os usuários a ativar recomendações de leitura e um código de cupom para o seu Segment de novos assinantes.

Chaves como body, title e buttonText podem ter valores de string simples definidos pelos seus profissionais de marketing. Chaves como terms podem ter valores com uma pequena coleção de frases aprovadas pelo departamento jurídico. Chaves como style e class_type possuem valores de string que você pode definir para determinar como o cartão será renderizado no seu app ou site.

Pares de chave-valor para o cartão de recomendação de leitura:

Chave Valor
body Adicione seus interesses ao seu perfil Politer Weekly para receber recomendações de leitura personalizadas.
style info
class_type notification_center
card_priority 1

Pares de chave-valor para um cupom de novo assinante:

Chave Valor
title Assine para jogos ilimitados
body Promoção de fim de verão - Aproveite 10% de desconto nos jogos Politer
buttonText Assinar agora
style promo
class_type notification_center
card_priority 2
terms new_subscribers_only
Informações adicionais para Android

No SDK para Android e FireOS, a lógica da central de mensagens é conduzida pelo valor class_type fornecido pelos pares de chave-valor da Braze. Usando o método createContentCardable, você pode filtrar e identificar esses tipos de classe.

Usando class_type para comportamento ao clicar
Quando inflamos os dados do Content Card em nossas classes personalizadas, usamos a propriedade ContentCardClass dos dados para determinar qual subclasse concreta deve ser usada para armazenar os dados.

 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
        }
    }

Então, ao lidar com a interação do usuário com a lista de mensagens, podemos usar o tipo da mensagem para determinar qual visualização exibir para o usuário.

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)
                }
            }

        }
    }

Usando class_type para comportamento ao clicar
Quando inflamos os dados do Content Card em nossas classes personalizadas, usamos a propriedade ContentCardClass dos dados para determinar qual subclasse concreta deve ser usada para armazenar os dados.

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;
        }
    }
}

Então, ao lidar com a interação do usuário com a lista de mensagens, podemos usar o tipo da mensagem para determinar qual visualização exibir para o usuário.

@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)
                }
            }

        });
    }

Você pode configurar Content Cards em um feed de carrossel totalmente personalizado, permitindo que os usuários deslizem e visualizem cartões em destaque adicionais. Por padrão, os Content Cards são ordenados pela data de criação (mais recentes primeiro), e seus usuários verão todos os cartões para os quais são elegíveis.

Para implementar um carrossel de Content Cards:

  1. Crie uma lógica personalizada que observe alterações nos seus Content Cards e trate a chegada de Content Cards.
  2. Crie uma lógica personalizada no lado do cliente para exibir um número específico de cartões no carrossel a qualquer momento. Por exemplo, você pode selecionar os cinco primeiros objetos de Content Card do array ou introduzir pares de chave-valor para criar lógica condicional.

Somente imagem

Os Content Cards não precisam parecer “cartões”. Por exemplo, os Content Cards podem aparecer como uma imagem dinâmica exibida permanentemente na sua página inicial ou no topo de páginas específicas.

Para conseguir isso, seus profissionais de marketing criarão uma Campaign ou etapa do Canvas com um Content Card do tipo Somente imagem. Em seguida, defina pares de chave-valor apropriados para usar Content Cards como conteúdo complementar.

New Stuff!