# Configurando o Pushwoosh InboxKit para iOS

*Disponível desde o SDK do iOS [7.0.40](https://github.com/Pushwoosh/pushwoosh-ios-sdk/releases/tag/7.0.40).*

O Pushwoosh InboxKit oferece uma tela de caixa de entrada moderna em UIKit sobre o backend de caixa de entrada existente. Seis layouts de célula padrão cobrem os formatos comuns de cartões de conteúdo — de banners simples a carrosséis de imagens, vídeo em linha e passes do Apple Wallet — botões de CTA em linha lidam com as interações mais comuns, e toda a superfície está aberta para subclassificação se você precisar de um visual personalizado.

<img src="/setting-up-pushwoosh-inboxkit-ios-feed.webp" alt="Feed do InboxKit mostrando cartões de banner, com legenda, clássico, carrossel, vídeo e Apple Wallet" width="300" style="display: block; margin: 0 auto;"/>

<p style="text-align: center; opacity: 0.7; font-size: 0.875rem; margin-top: 0.5rem;">Feed padrão do InboxKit com cartões de banner, com legenda, clássico, carrossel, vídeo e Apple Wallet.</p>

## Quando usar o InboxKit

Use o InboxKit para qualquer nova integração com iOS. É o substituto recomendado para o módulo mais antigo em Objective-C [PushwooshInboxUI](/pt/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/).

O InboxKit oferece a você:

- Seis tipos de célula integrados — banner, com legenda, clássico, carrossel, vídeo e Apple Wallet — selecionados por mensagem através do `displayType` do payload, ou forçados a partir do código via `attributes.forceCellKind`. Veja [Tipos de cartão](#tipos-de-cartao) para a lista completa. (O cartão Apple Wallet é exclusivo para iOS.)
- Botões de CTA em linha com um enum tipado `PushwooshInboxButtonAction` (`openURL`, `dismiss`, `markRead`, `custom`). O SDK lida com os três primeiros automaticamente; seu delegate roteia `custom` para sua própria lógica.
- Suporte à fixação: mensagens com `actionParams["pinned"] == true` flutuam para o topo do feed e renderizam um glifo de pino.
- Deslizar para excluir, puxar para atualizar, marcar como lido automaticamente ao desaparecer — tudo alternável via `PushwooshInboxKitAttributes`.
- Armazenamento persistente: exclusões e estado de leitura sobrevivem a uma reinicialização do processo, mesmo que a chamada de rede ainda não tenha sido confirmada.
- Uma classe base aberta `PushwooshInboxCell` para layouts totalmente personalizados.

O contrato do servidor permanece inalterado — o mesmo backend de caixa de entrada do Pushwoosh, payloads e ferramentas do painel funcionam como antes.

## Escolha seu método de integração

- [**Configurar o InboxKit com o Swift Package Manager**](/pt/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-swift-package-manager/) — recomendado para novos projetos.
- [**Configurar o InboxKit com o CocoaPods**](/pt/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-cocoapods/) — para projetos que já usam o CocoaPods.

## Tipos de cartão

O InboxKit escolhe um layout de célula por mensagem. O resolvedor padrão lê `displayType` do payload do push — coloque-o dentro do objeto `data`, que o SDK entrega sob `actionParams`. Quando `displayType` está ausente, o resolvedor recorre a uma heurística: imagem + sem título → banner, imagem + título → com legenda, caso contrário, clássico. Para forçar um layout para todo o feed a partir do código, defina `attributes.forceCellKind`.

Cada layout rico degrada-se graciosamente: se o payload necessário estiver ausente ou malformado, o cartão volta para `classic` em vez de renderizar um espaço reservado vazio (e um `WARN` é registrado).

| `displayType` | Layout | Campo de payload obrigatório | Degrada para |
|---|---|---|---|
| `banner` | Imagem de sangria total, sem texto | imagem (`inbox_image` ou `data.image`) | `classic` quando não há imagem |
| `captioned` | Imagem no topo, título + corpo abaixo | imagem (`inbox_image` ou `data.image`) | `classic` quando não há imagem |
| `classic` | Avatar inicial colorido + título + corpo | — | — |
| `carousel` | Galeria de múltiplas imagens deslizável | `data.carousel` (array de slides) | `classic` quando não há slides |
| `video` | Pôster com ícone de play, player em tela cheia ao tocar | `data.video` (`url` + `poster` opcional) | `classic` quando não há descritor |
| `wallet` | Botão "Adicionar à Carteira da Apple" (somente iOS) | `data.wallet` (URL do `.pkpass`) | `classic` quando não há URL do passe |

{/* TODO(screenshot): replace each placeholder below with a per-card screenshot. Suggested filenames: setting-up-pushwoosh-inboxkit-ios-card-banner.webp, -captioned.webp, -classic.webp, -carousel.webp, -video.webp, -wallet.webp — then swap each <div> for an <img src="/<filename>" width="240" .../>. */}
<div style="display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; margin: 1.5rem 0;">
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-banner.webp" alt="Cartão de banner do InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>Cartão de banner</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-captioned.webp" alt="Cartão com legenda do InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>Cartão com legenda</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-classic.webp" alt="Cartão clássico do InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>Cartão clássico</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-carousel.webp" alt="Cartão de carrossel do InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>Cartão de carrossel</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-video.webp" alt="Cartão de vídeo do InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>Cartão de vídeo</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-wallet.webp" alt="Cartão Apple Wallet do InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>Cartão Apple Wallet</strong></figcaption></figure>
</div>

Os cartões de banner, com legenda e clássicos são controlados pelos campos de mensagem padrão (imagem, título, corpo) mais o array opcional `buttons` — veja [Adicionar botões de CTA em linha](#adicionar-botoes-de-cta-em-linha). Os cartões de carrossel, vídeo e Apple Wallet carregam dados estruturados extras dentro de `data`, documentados abaixo.

### Cartão de carrossel

Um carrossel renderiza várias imagens de uma única mensagem — uma galeria deslizável com legendas por slide e destinos de toque opcionais. Os slides ficam em `data.carousel`. Cada slide precisa de uma `image`; `title` (sobreposição de legenda) e `url` (deep link aberto ao tocar) são opcionais. Um slide sem imagem é descartado; um toque em um slide sem `url` recai para a ação de linha padrão da mensagem.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "New arrivals",
      "content": "Swipe through this week's drops",
      "inbox_days": 7,
      "data": {
        "displayType": "carousel",
        "carousel": [
          { "image": "https://cdn.example.com/inbox/1.jpg", "title": "New in", "url": "myapp://product/1" },
          { "image": "https://cdn.example.com/inbox/2.jpg", "title": "On sale", "url": "myapp://product/2" },
          { "image": "https://cdn.example.com/inbox/3.jpg" }
        ]
      },
      "platforms": [1]
    }]
  }
}
```

### Cartão de vídeo

Um cartão de vídeo mostra uma imagem de pôster com um ícone de play; tocá-lo abre um player em tela cheia (com som, mesmo com o interruptor de silêncio ativado). O descritor fica em `data.video`: `url` é obrigatório e deve ser um stream ou arquivo `http`/`https`; `poster` é uma imagem de pré-visualização opcional.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "Watch the reveal",
      "content": "Tap to play",
      "inbox_days": 7,
      "data": {
        "displayType": "video",
        "video": {
          "url": "https://cdn.example.com/inbox/clip.mp4",
          "poster": "https://cdn.example.com/inbox/poster.jpg"
        }
      },
      "platforms": [1]
    }]
  }
}
```

### Cartão Apple Wallet

O cartão Apple Wallet mostra uma imagem de herói opcional, título e corpo acima do botão oficial **Adicionar à Carteira da Apple**. Tocar no botão baixa o `.pkpass` e apresenta a folha do sistema para adicionar passes. Use-o para entregar cupons, cartões de fidelidade, ingressos ou cartões de embarque diretamente da caixa de entrada. O cartão é exclusivo para iOS / Mac Catalyst — em outras plataformas, a mensagem é renderizada como um cartão clássico.

A URL do passe fica em `data.wallet`, seja como uma string simples ou como um objeto com um campo `pass`. Um `data.image` opcional adiciona a imagem de herói. O botão se esconde automaticamente quando não há URL de passe ou o dispositivo não pode adicionar passes.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "Your loyalty card is ready",
      "content": "Add it to Apple Wallet in one tap",
      "inbox_days": 7,
      "data": {
        "displayType": "wallet",
        "image": "https://cdn.example.com/inbox/loyalty.png",
        "wallet": "https://passes.example.com/v1/passes/pass.com.example.loyalty/abc123?token=…"
      },
      "platforms": [1]
    }]
  }
}
```

O resultado é reportado ao seu delegate:

```swift
extension MyInboxHost: PushwooshInboxKitDelegate {

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didAddWalletPassFor message: PWInboxMessageProtocol) {
        // O passe agora está na Carteira do usuário — mostre uma confirmação se desejar.
    }

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didFailToAddWalletPassFor message: PWInboxMessageProtocol,
                  error: Error?) {
        // O download falhou — exiba uma nova tentativa, registre em log, etc.
    }
}
```

Ambos os callbacks são opcionais (eles carregam implementações vazias padrão). Um usuário cancelando a folha do sistema não é nem sucesso nem falha, então nenhum callback é disparado nesse caso.

## Acessibilidade

As células do InboxKit estão prontas para o VoiceOver desde o início. Os cartões de banner, com legenda e clássicos expõem seu título, corpo e data através dos rótulos subjacentes, e os botões de CTA em linha leem seus próprios títulos. Os cartões ricos adicionam semântica explícita:

- **Vídeo** — o pôster é exposto como um único elemento de botão rotulado "Reproduzir vídeo" (atributos `.button` + `.startsMediaSession`), então o VoiceOver o anuncia como um controle de mídia em vez de uma imagem simples.
- **Carrossel** — cada slide é um elemento de botão cujo rótulo de acessibilidade é a legenda do slide, ou "Slide" quando não tem nenhuma. O indicador de página anuncia a posição atual como "*n* de *total*".
- **Apple Wallet** — o botão **Adicionar à Carteira da Apple** é o `PKAddPassButton` padrão da Apple, que carrega seu próprio rótulo de VoiceOver localizado.

Para testes de UI e automação, dois `accessibilityIdentifier`s estáveis são definidos: `inboxkit.video.play` no pôster do vídeo e `inboxkit.wallet.add` no botão da Carteira.

## Ler dados personalizados de uma mensagem

Para fazer um push aparecer na caixa de entrada, a solicitação `createMessage` da [API de Mensagens](/pt/developer/api-reference/messages-api/) deve incluir `inbox_image`, `inbox_date` ou `inbox_days` — sem um desses campos, o push é entregue como uma notificação regular e nunca chega ao feed da caixa de entrada. Dados personalizados de formato livre vão sob a chave `data`, que o SDK entrega ao cliente como o parâmetro `u`:

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "Summer sale",
      "content": "30% off everything — limited time only",
      "inbox_image": "https://cdn.example.com/inbox/summer.png",
      "inbox_days": 7,
      "data": {
        "displayType": "captioned",
        "promo_id": "SUMMER2026",
        "screen": "promo_details"
      },
      "platforms": [1]
    }]
  }
}
```

O SDK expõe esse objeto na mensagem da caixa de entrada através de `actionParams`. Leia-o do delegate quando o usuário tocar na linha ou em um CTA em linha:

```swift
extension MyInboxHost: PushwooshInboxKitDelegate {

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didSelect message: PWInboxMessageProtocol) -> Bool {
        guard let params = message.actionParams as? [String: Any] else { return true }

        // O objeto `data` personalizado chega sob a chave "u" —
        // seja como um dicionário aninhado ou como uma string codificada em JSON,
        // dependendo de como o payload foi construído anteriormente.
        let custom: [String: Any]? = {
            if let dict = params["u"] as? [String: Any] { return dict }
            if let raw = params["u"] as? String,
               let bytes = raw.data(using: .utf8),
               let parsed = try? JSONSerialization.jsonObject(with: bytes) as? [String: Any] {
                return parsed
            }
            return nil
        }()

        if let promoId = custom?["promo_id"] as? String {
            navigateToPromo(promoId)
            return false   // nós lidamos com o toque; o SDK não deve executar a ação padrão
        }
        return true
    }
}
```

A mesma busca `actionParams["u"]` funciona dentro de `inboxKit(_:didTapButton:onMessage:)` para botões de CTA em linha. Para os casos de CTA tipados (`openURL`, `dismiss`, `markRead`), o SDK já executa a ação padrão — retorne `true` para manter esse comportamento, ou `false` para suprimi-lo e executar o seu próprio.

## Adicionar botões de CTA em linha

Uma mensagem pode carregar até três botões de chamada para ação (CTA) em linha. Os botões ficam ao lado de outros dados personalizados dentro de `data` como um array `buttons`. O SDK os renderiza automaticamente dentro das células com legenda e clássicas:

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "New promo card",
      "content": "Tap a button to claim or save",
      "inbox_image": "https://cdn.example.com/inbox/promo.png",
      "inbox_days": 7,
      "data": {
        "displayType": "captioned",
        "promo_id": "SUMMER2026",
        "buttons": [
          { "title": "Claim", "url": "https://example.com/promo/SUMMER2026" },
          { "title": "Read",  "action": "markRead" },
          { "title": "Save",  "action": "custom", "tag": "save_promo" }
        ]
      },
      "platforms": [1]
    }]
  }
}
```

Cada objeto de botão tem estes campos:

| Campo | Tipo | Quando |
|---|---|---|
| `title` | string | Obrigatório. Rótulo visível do botão. |
| `url` | string | Uma URL analisável e não vazia produz uma ação `openURL`. O SDK a abre via `UIApplication.shared.open`, a menos que seu delegate a suprima. |
| `action` | string | Token de ação explícito: `dismiss` (remove a mensagem do feed), `markRead` (marca a mensagem como lida) ou `custom` (tratado pelo host). Não diferencia maiúsculas de minúsculas. |
| Qualquer outra coisa | any | Quando `action` é `custom`, todas as chaves no objeto do botão, exceto `title` e `action`, são encaminhadas para o seu delegate como o payload personalizado — combine uma chave com o profissional de marketing (por exemplo, `tag`) e despache com base nela. |

Prioridade de resolução: primeiro o token `action` explícito, depois `url` se não estiver vazio, caso contrário, o botão cai em `custom` carregando o payload completo (menos `title` e `action`).

Intercepte os toques do seu delegate. A propriedade `button.action` é o enum tipado `PushwooshInboxButtonAction`:

```swift
extension MyInboxHost: PushwooshInboxKitDelegate {

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didTapButton button: PushwooshInboxButton,
                  onMessage message: PWInboxMessageProtocol) -> Bool {
        switch button.action {
        case .openURL(let url):
            // O comportamento padrão está bom — deixe o SDK abrir a URL.
            return true

        case .dismiss, .markRead:
            // O SDK lida com ambos. Retorne false se quiser substituir.
            return true

        case .custom(let payload):
            // Botão personalizado definido pelo profissional de marketing. Despache com base em uma chave que vocês combinaram.
            if let tag = payload["tag"] as? String {
                switch tag {
                case "save_promo":
                    saveCurrentPromoLocally(message: message)
                default:
                    break
                }
            }
            return true   // ignorado para custom — o SDK nunca executa uma ação padrão aqui
        }
    }
}
```