📚 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:
- Instalar a CLI Bubblewrap Globalmente:
npm install -g @bubblewrap/cli - 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.jsonA ferramenta baixará o manifesto e fará perguntas para configurar as propriedades do app Android nativo automaticamente.
- 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.
- Compilar o Binário do Aplicativo:
Execute o compilador nativo local:
bubblewrap buildO comando baixará as dependências de build necessárias do Gradle e gerará o arquivo assinado final em:
app-release-signed.apk. - 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.jsondurante 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!
- O Bubblewrap gerará um bloco JSON chamado
✅ Pré-Requisitos deste Módulo
Antes de empacotar o PWA com Bubblewrap, confirme que:
- A aplicação está hospedada publicamente com HTTPS válido ativo (obrigatório para TWAs).
- Você testou o carregamento e cacheamento de assets offline (Módulo 11).
🤔 Por que fizemos assim?
- Por que usar Bubblewrap (TWA) e não Capacitor ou Cordova? O Capacitor e Cordova geram builds que incluem uma WebView pesada e isolada dentro do aplicativo, criando sua própria sessão de cache de login separada do navegador comum. A TWA (Bubblewrap) compartilha o mesmo motor e sessão de cookies do Chrome principal do celular. Isso significa que se o cliente já estiver logado no site da TecLoja 04 pelo navegador e instalar o aplicativo TWA, ele já iniciará o app nativo totalmente logado, melhorando absurdamente a retenção de usuários.
- Por que o arquivo
assetlinks.jsoné obrigatório? Segurança cibernética. Sem a prova criptográfica declarada no domínio público via HTTPS, qualquer hacker conseguiria criar uma casca nativa apontando para o site do seu banco ou e-commerce e roubar credenciais enganando as vítimas. O arquivo de vínculo garante que apenas o dono legítimo do domínio consiga rodar a casca nativa sem a barra de endereços do browser.
🔍 Checkpoint
- 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.
- 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. |