📚 Módulo 12: Mobile/PWA - Notificações Push, Prompt de Instalação e Empacotamento TWA (Bubblewrap)

Neste módulo final de encerramento da TecLoja 04, daremos o passo definitivo para unificar a nossa aplicação web corporativa com a experiência nativa de smartphone.

Implementaremos a captura programática do prompt de instalação A2HS (Add to Home Screen) com botões personalizados, integraremos suporte a Notificações Push baseadas na Web Push API do navegador e empacotaremos o nosso PWA em um aplicativo nativo Android assinado utilizando a CLI Bubblewrap (Trusted Web Activity), gerando um binário .apk pronto para distribuição direta ou publicação na Google Play Store.


🗺️ 1. Fluxo de Empacotamento TWA (Trusted Web Activity)

O diagrama abaixo descreve como o Bubblewrap envelopa o nosso PWA HTTPS remoto dentro de uma casca nativa leve do Android, que abre o app sem barras de navegador clássicas:

flowchart TD
    %% Styling
    classDef web fill:#000000,stroke:#333,stroke-width:2px,color:#fff;
    classDef twa fill:#2196F3,stroke:#0D47A1,stroke-width:2px,color:#fff;
    classDef native fill:#4CAF50,stroke:#2E7D32,stroke-width:2px,color:#fff;

    A[PWA Web: Next.js + HTTPS]:::web --> B[Bubblewrap CLI Setup]:::twa
    B --> C[Verificação de Assinatura: assetlinks.json]:::twa
    C --> D[Compilação do Container Nativa Android]:::twa
    D --> E[Assinar com Keystore Digital]:::twa
    E --> F[Gerar app-release-signed.apk]:::native

📲 2. Prompt de Instalação Customizado (A2HS)

Por padrão, os navegadores exibem um prompt discreto de instalação. Criaremos um banner reativo amigável em nosso cabeçalho para incentivar o usuário a instalar o aplicativo.

Implementação do Componente Banner (/components/InstallPrompt.tsx)

'use client';

import { useEffect, useState } from 'react';

export default function InstallPrompt() {
  const [deferredPrompt, setDeferredPrompt] = useState<any>(null);
  const [visivel, setVisivel] = useState(false);

  useEffect(() => {
    const capturarPrompt = (e: Event) => {
      // Impedir que o navegador exiba o prompt nativo padrão imediatamente
      e.preventDefault();
      setDeferredPrompt(e);
      setVisivel(true);
    };

    window.addEventListener('beforeinstallprompt', capturarPrompt);

    return () => {
      window.removeEventListener('beforeinstallprompt', capturarPrompt);
    };
  }, []);

  const realizarInstalacao = async () => {
    if (!deferredPrompt) return;

    // Exibir o prompt nativo salvo previamente
    deferredPrompt.prompt();
    
    // Aguardar a resposta de aceite do usuário
    const { outcome } = await deferredPrompt.userChoice;
    console.log(`Escolha de instalação do usuário: ${outcome}`);
    
    // Resetar o estado
    setDeferredPrompt(null);
    setVisivel(false);
  };

  if (!visivel) return null;

  return (
    <div className="fixed bottom-4 left-4 right-4 z-50 bg-slate-900 border border-slate-800 rounded-xl p-4 shadow-2xl flex items-center justify-between">
      <div>
        <p className="font-bold text-sky-400">Instalar TecLoja 04</p>
        <p className="text-xs text-slate-400">Tenha acesso rápido direto da sua tela inicial</p>
      </div>
      <button 
        onClick={realizarInstalacao}
        className="bg-sky-500 hover:bg-sky-600 text-slate-950 font-bold text-xs px-4 py-2 rounded-lg transition-colors"
      >
        Instalar
      </button>
    </div>
  );
}

🔔 3. Notificações Push Locais com Service Worker

Para disparar avisos de promoções ou confirmação de compras no celular do usuário de forma assíncrona, utilizaremos a API de Notificações nativa.

Solicitando Permissão e Disparando Notificações no Cliente

No script do seu componente ou serviço de checkout, insira a lógica de verificação de permissões:

export async function dispararNotificacaoLocal(titulo: string, corpo: string) {
  if (!('Notification' in window)) {
    console.log('Este navegador não suporta notificações desktop/mobile.');
    return;
  }

  // 1. Solicitar permissão se ainda não concedida
  if (Notification.permission === 'default') {
    await Notification.requestPermission();
  }

  // 2. Disparar notificação através do Service Worker registrado
  if (Notification.permission === 'granted') {
    const registro = await navigator.serviceWorker.ready;
    registro.showNotification(titulo, {
      body: corpo,
      icon: '/icons/icon-192x192.png',
      badge: '/icons/icon-192x192.png',
      vibrate: [200, 100, 200],
    });
  }
}

📦 4. Empacotando para Android via Bubblewrap (TWA)

Para submeter o PWA à Google Play Store, o Google desenvolveu o padrão Trusted Web Activity (TWA). Usaremos a CLI oficial do Google para compilar o APK sem abrir o Android Studio.

Passo a Passo de Empacotamento:

  1. Instalar a CLI Bubblewrap Globalmente:
    npm install -g @bubblewrap/cli
    
  2. Inicializar o Projeto TWA: No terminal, execute o comando apontando para a URL pública de produção de sua aplicação (ex: Vercel):
    bubblewrap init --manifest=https://sua-tecloja.vercel.app/manifest.json
    

    A ferramenta baixará o manifesto e fará perguntas para configurar as propriedades do app Android nativo automaticamente.

  3. Configurar Chaves Digitais do Android: Durante o setup, o Bubblewrap perguntará se deseja criar uma chave Keystore. Confirme a criação e guarde a senha informada.
  4. Compilar o Binário do Aplicativo: Execute o compilador nativo local:
    bubblewrap build
    

    O comando baixará as dependências de build necessárias do Gradle e gerará o arquivo assinado final em: app-release-signed.apk.

  5. Estabelecer Vínculo de Segurança (Digital Asset Links): Para impedir que o Android exiba a barra de endereços do navegador Chrome no topo do seu aplicativo instalado (o que removeria o aspecto de app nativo), você deve provar a posse do domínio.
    • O Bubblewrap gerará um bloco JSON chamado assetlinks.json durante a build.
    • Copie esse arquivo e faça o upload em seu repositório frontend para que fique exposto na URL pública: https://sua-tecloja.vercel.app/.well-known/assetlinks.json
    • Uma vez detectado o arquivo pelo Android, a barra de endereços URL desaparecerá por completo no celular, transformando a navegação na experiência de tela cheia nativa corporativa definitiva!

✅ Pré-Requisitos deste Módulo

Antes de empacotar o PWA com Bubblewrap, confirme que:


🤔 Por que fizemos assim?


🔍 Checkpoint

  1. Aparecimento do Banner: Acesse o site em produção pelo celular e confirme se o banner inferior personalizado oferecendo a instalação (A2HS) aparece e executa a instalação física com sucesso.
  2. Notificação Ativa: Simule a finalização de uma compra de teste e valide se a notificação sonora/vibratória é disparada na bandeja de avisos do smartphone do usuário.

⚠️ Erros Comuns

Erro Causa Solução
A barra de navegação superior do Chrome (URL) insiste em aparecer no app instalado O arquivo assetlinks.json está inacessível no domínio público ou as assinaturas criptográficas informadas diferem do certificado do APK. Certifique-se de expor o arquivo estritamente sob a rota pública /.well-known/assetlinks.json com o tipo de conteúdo MIME application/json.
O Bubblewrap falha pedindo variáveis de ambiente Java/SDK Ausência do JDK ou das dependências nativas configuradas na CLI do sistema. O Bubblewrap gerencia a instalação de pacotes locais. Se falhar, informe o caminho completo do JDK do computador nas variáveis do console do usuário.
O prompt de instalação A2HS não dispara no iOS O sistema operacional Safari do iOS adota políticas rígidas de segurança contra prompts intrusivos. No iOS, a instalação de PWAs deve ser feita manualmente pelo usuário clicando no botão nativo “Compartilhar” e selecionando “Adicionar à Tela de Início”. Adicione dicas visuais explicativas exclusivas para clientes Apple.

Voltar para o Sumário