Transformando Seu Site em um PWA com Notificações Push Usando Firebase
Neste artigo, vamos explorar o processo de conversão de uma aplicação web ou site em um PWA (Progressive Web App), implementando notificações push através do Firebase Cloud Messaging.
Atualmente, a tendência é a migração de diversas aplicações web para PWAs devido às vantagens que oferecem, como suporte offline, notificações push e sincronização em segundo plano. Essas funcionalidades enriquecem a experiência do usuário, aproximando a aplicação web do comportamento de um aplicativo nativo.
Grandes corporações como Twitter e Amazon já adotaram essa abordagem, convertendo seus aplicativos web em PWAs para aumentar o engajamento de seus usuários.
O que Define um PWA?
Um PWA pode ser entendido como a combinação de um aplicativo web tradicional com funcionalidades adicionais típicas de aplicativos nativos.
Basicamente, um PWA é construído com as mesmas tecnologias da web (HTML, CSS e JavaScript) e funciona de forma semelhante em todos os navegadores. A diferença está na capacidade de oferecer recursos nativos quando acessado através de um navegador moderno. Isso resulta em um aplicativo web mais poderoso e escalável, com a possibilidade de armazenar ativos em cache no frontend, diminuindo a carga sobre o servidor backend.
Diferenças Cruciais entre um PWA e um Aplicativo Web
- Instalável: Um PWA pode ser instalado como um aplicativo nativo no dispositivo do usuário.
- Progressivo: Funciona como um aplicativo web, mas com a adição de funcionalidades nativas.
- Experiência Nativa: A experiência de navegação e uso se assemelha à de um aplicativo nativo.
- Acesso Simplificado: Após instalado, o PWA pode ser acessado com um único toque, eliminando a necessidade de digitar o endereço web.
- Cache Aprimorado: PWAs oferecem um sistema de cache mais robusto do que o cache HTTP tradicional, permitindo o armazenamento em cache diretamente no lado do cliente.
- Publicação em Lojas: PWAs podem ser publicados tanto na Google Play Store quanto na App Store da Apple.
A conversão de seu aplicativo em PWA é um passo em direção a um produto mais robusto e funcional.
Por Que Empresas Devem Considerar PWAs?
Muitas vezes, clientes solicitam o desenvolvimento de um aplicativo web e, posteriormente, aplicativos para Android e iOS. Este cenário implica em replicar as mesmas funcionalidades em diferentes plataformas, resultando em custos adicionais de desenvolvimento e maior tempo de lançamento no mercado.
No entanto, alguns clientes possuem orçamentos limitados ou priorizam a rapidez no lançamento. Nesses casos, as funcionalidades de um PWA podem ser suficientes para atender a maioria dos requisitos, servindo como uma alternativa viável. Sugere-se então a conversão para PWA, com a opção de transformá-lo em um aplicativo Android através do TWA (Trusted Web Activity), caso a publicação na Play Store seja desejada.
Se as necessidades do cliente demandarem recursos de aplicativos nativos não disponíveis em PWAs, o desenvolvimento de aplicativos para Android e iOS é recomendado. Mesmo assim, um PWA pode ser lançado na Play Store como uma solução temporária, enquanto o desenvolvimento dos aplicativos nativos é finalizado.
Exemplo: Titan Eyeplus
Essa empresa iniciou com o desenvolvimento de um PWA, publicando-o na Play Store via TWA. Após o desenvolvimento do aplicativo Android, lançaram o aplicativo nativo, otimizando tempo e custos.
Funcionalidades dos PWAs
PWAs oferecem funcionalidades que aproximam as aplicações web da experiência de aplicativos nativos.
As principais funcionalidades incluem:
- Instalável: Aplicações web que podem ser instaladas como aplicativos nativos.
- Cache: Possibilidade de cache de dados, garantindo suporte offline.
- Notificações Push: Envio de notificações do servidor para engajar o usuário com o site.
- Geofencing: Notificações com base na mudança de localização do dispositivo.
- Solicitação de Pagamento: Pagamentos facilitados dentro do aplicativo com experiência de usuário similar a aplicativos nativos.
E outras funcionalidades que serão implementadas no futuro.
Outras funcionalidades:
- Atalhos: URLs de acesso rápido adicionados no arquivo de manifesto.
- Web Share API: Permite que o aplicativo compartilhe dados com outros aplicativos.
- Badge API: Mostra o número de notificações em um PWA instalado.
- API de Sincronização Periódica em Segundo Plano: Salva dados do usuário até a conexão com a rede.
- Seletor de Contatos: Permite selecionar contatos do celular.
- File Picker: Permite acessar arquivos no sistema local.
Vantagens de PWAs sobre Aplicativos Nativos
Embora aplicativos nativos geralmente tenham melhor desempenho e mais funcionalidades, PWAs possuem vantagens importantes:
- Compatibilidade Multiplataforma: PWAs funcionam em Android, iOS e Desktop.
- Redução de Custos: Diminui os custos de desenvolvimento.
- Implantação Simplificada: Facilidade na implementação de novas funcionalidades.
- Facilidade de Descoberta: PWAs são otimizados para SEO, o que facilita sua descoberta.
- Segurança: Funcionamento exclusivo através de HTTPS.
Desvantagens de PWAs sobre Aplicativos Nativos
- Funcionalidades Limitadas: Menos funcionalidades disponíveis em comparação com aplicativos nativos.
- Suporte Variável: Nem todos os dispositivos suportam todas as funcionalidades de PWAs.
- Menor Visibilidade: PWAs não são facilmente encontrados em lojas de aplicativos.
No entanto, é possível publicar um PWA como aplicativo Android na Play Store através do Android Trusted Web Activity (TWA), o que auxilia na melhoria da visibilidade.
O Que É Necessário Para Converter um Aplicativo Web em PWA?
Para transformar um aplicativo web ou site em PWA, você precisará:
- Service Worker: Essencial para caching, notificações push e como um proxy para requisições.
- Arquivo de Manifesto: Contém informações sobre o aplicativo, facilitando a instalação na tela inicial.
- Logotipo do Aplicativo: Uma imagem de alta qualidade (512x512px) para o ícone do aplicativo.
- Design Responsivo: O aplicativo deve ser adaptável a diferentes tamanhos de tela.
O Que É um Service Worker?
Um service worker (script do lado do cliente) atua como um intermediário entre o aplicativo web e o exterior, habilitando notificações push e cache de dados.
Ele opera independentemente do JavaScript principal, sem acesso à DOM API, tendo acesso apenas a IndexedDB API, Fetch API e CacheStorage API. A comunicação com a thread principal é feita por meio de mensagens.
Serviços Fornecidos pelo Service Worker:
- Interceptação de requisições HTTP do domínio de origem.
- Recebimento de notificações push do servidor.
- Disponibilização offline do aplicativo.
O service worker monitora e manipula requisições, mas opera de forma independente, daí a necessidade de HTTPS para evitar ataques “man-in-the-middle”.
O Que É o Arquivo de Manifesto?
O arquivo de manifesto (manifest.json) contém detalhes sobre o PWA para informar o navegador.
- name: Nome do aplicativo.
- short_name: Nome abreviado do aplicativo.
- description: Descrição do aplicativo.
- start_url: Página inicial do aplicativo.
- icons: Imagens para a tela inicial, etc.
- background_color: Cor de fundo da tela inicial.
- display: Personalização da interface do navegador.
- theme_color: Cor do tema do aplicativo.
- scope: Escopo da URL para considerar o PWA.
- shortcuts: Links rápidos para o aplicativo.
Convertendo um Aplicativo Web para PWA
Para fins de demonstração, foi criada uma estrutura de pastas do site etechpt.com com arquivos estáticos:
- index.html – página inicial
- artigos/
- index.html – página de artigos
- autores/
- index.html – página de autores
- Ferramentas/
- index.html – página de ferramentas
- ofertas/
- index.html – página de ofertas
Se você já possui um site ou aplicativo web, siga os passos abaixo para convertê-lo em PWA.
Criando as Imagens Necessárias para o PWA
Primeiro, prepare o logotipo do seu aplicativo em formato 1:1, em cinco tamanhos diferentes. É possível utilizar ferramentas como https://tools.crawlink.com/tools/pwa-icon-generator/ para facilitar esse processo.
Criando o Arquivo de Manifesto
Em seguida, crie um arquivo manifest.json com os detalhes do aplicativo. Para o exemplo do site etechpt.com:
{
"name": "etechpt.com",
"short_name": "etechpt.com",
"description": "etechpt.com produz artigos de alta qualidade sobre tecnologia e finanças, e oferece ferramentas e APIs para o crescimento de negócios e pessoas.",
"start_url": "/",
"icons": [{
"src": "assets/icon/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "assets/icon/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "assets/icon/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}, {
"src": "assets/icon/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
}, {
"src": "assets/icon/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}],
"background_color": "#EDF2F4",
"display": "standalone",
"theme_color": "#B20422",
"scope": "/",
"shortcuts": [{
"name": "Articles",
"short_name": "Articles",
"description": "1595 artigos sobre Segurança, Sysadmin, Marketing Digital, Computação em Nuvem, Desenvolvimento e outros.",
"url": "https://geekflare.com/articles",
"icons": [{
"src": "/assets/icon/icon-152x152.png",
"sizes": "152x152"
}]
},
{
"name": "Authors",
"short_name": "Authors",
"description": "etechpt.com - Autores",
"url": "/authors",
"icons": [{
"src": "/assets/icon/icon-152x152.png",
"sizes": "152x152"
}]
},
{
"name": "Tools",
"short_name": "Tools",
"description": "etechpt.com - Ferramentas",
"url": "https://etechpt.com.com/tools",
"icons": [{
"src": "/assets/icon/icon-152x152.png",
"sizes": "152x152"
}]
},
{
"name": "Deals",
"short_name": "Deals",
"description": "etechpt.com - Ofertas",
"url": "/deals",
"icons": [{
"src": "/assets/icon/icon-152x152.png",
"sizes": "152x152"
}]
}
]
}
Registrando o Service Worker
Crie dois arquivos: register-service-worker.js e service-worker.js, na pasta raiz.
register-service-worker.js será executado na thread principal, com acesso à API DOM, enquanto service-worker.js será executado de forma independente, com um ciclo de vida curto, sendo ativado por eventos.
No arquivo register-service-worker.js, adicione o seguinte código:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js');
});
}
Em service-worker.js, adicione:
self.addEventListener('install', (event) => { // evento quando o service worker instala
console.log( 'install', event);
self.skipWaiting();
});
self.addEventListener('activate', (event) => { // evento quando o service worker é ativado
console.log('activate', event);
return self.clients.claim();
});
self.addEventListener('fetch', function(event) { // Interceptador de requisição HTTP
event.respondWith(fetch(event.request)); // envia toda requisição http sem logica de cache
/*event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event. request);
})
);*/ // cacheia nova requisição. Se já estiver no cache, serve com o cache.
});
Por enquanto, o foco é a conversão para PWA, e não a implementação de cache offline.
Adicione o arquivo de manifesto e o script na tag <head> da página HTML.
<link rel="manifest" href="https://etechpt.com.com/manifest.json"> <script src="/register-service-worker.js"></script>
Atualize a página e você poderá instalar o aplicativo, como exemplificado no Chrome móvel.
O aplicativo será adicionado à tela inicial.

Para usuários de WordPress, plugins de conversão para PWA podem ser utilizados. Em VueJS ou ReactJS, o mesmo método pode ser seguido ou então módulos npm de PWA para acelerar o desenvolvimento, que já incluem cache offline, entre outros.
Habilitando Notificações Push
As notificações push da web são essenciais para o engajamento do usuário com o aplicativo. Elas são habilitadas através de:
- Notification API: Para configurar como as notificações serão exibidas.
- Push API: Para receber mensagens do servidor.
Primeiramente, é necessário verificar a Notification API e obter permissão do usuário. Adicione o código abaixo em register-service-worker.js:
if ('Notification' in window && Notification.permission != 'granted') {
console.log('Ask user permission')
Notification.requestPermission(status => {
console.log('Status:'+status)
displayNotification('Notification Enabled');
});
}
const displayNotification = notificationTitle => {
console.log('display notification')
if (Notification.permission == 'granted') {
navigator.serviceWorker.getRegistration().then(reg => {
console.log(reg)
const options = {
body: 'Obrigado por permitir notificações push!',
icon: '/assets/icons/icon-512x512.png',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 0
}
};
reg.showNotification(notificationTitle, options);
});
}
};
Se tudo estiver correto, uma notificação será exibida.

A propriedade ‘Notification’ na window indica o suporte à API de notificação, e Notification.permission revela a permissão do usuário. O valor será ‘granted’ se a permissão foi concedida e ‘denied’ caso contrário.
Ativando o Firebase Cloud Messaging e Criando uma Assinatura
Para enviar notificações do servidor, é necessário um endpoint único para cada usuário. Para isso, utilizaremos o Firebase Cloud Messaging.
Primeiro, crie uma conta no Firebase em https://firebase.google.com/.
- Crie um novo projeto com um nome. No exemplo, ‘etechpt.com’.
- O Google Analytics é ativado por padrão. É possível desativá-lo e habilitá-lo posteriormente no console do Firebase.
- Após a criação, o projeto será exibido conforme abaixo.

Acesse as configurações do projeto e clique em mensagens na nuvem para gerar as chaves.

Você terá acesso a três chaves:
- Chave do servidor do projeto.
- Chave privada de certificados push da web.
- Chave pública de certificados push da web.
Cole o seguinte código em register-service-worker.js:
const updateSubscriptionOnYourServer = subscription => {
console.log('Insira aqui seu código ajax para salvar a assinatura do usuário em seu banco de dados', subscription);
// escreva aqui sua própria requisição ajax para salvar a assinatura no seu servidor para uso futuro.
};
const subscribeUser = async () => {
const swRegistration = await navigator.serviceWorker.getRegistration();
const applicationServerPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY'; // cole aqui a chave publica do seu certificado webpush
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey
})
.then((subscription) => {
console.log('Usuário inscrito:', subscription);
updateSubscriptionOnServer(subscription);
})
.catch((err) => {
if (Notification.permission === 'denied') {
console.warn('A permissão para notificações foi negada')
} else {
console.error('Falha na inscrição do usuário: ', err)
}
});
};
const urlB64ToUint8Array = (base64String) => {
const padding = '='.repeat((4 - base64String.length % 4) % 4)
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/')
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const checkSubscription = async () => {
const swRegistration = await navigator.serviceWorker.getRegistration();
swRegistration.pushManager.getSubscription()
.then(subscription => {
if (!!subscription) {
console.log('Usuário JÁ está inscrito.');
updateSubscriptionOnYourServer(subscription);
} else {
console.log('Usuário NÃO está inscrito. Inscrevendo usuário.');
subscribeUser();
}
});
};
checkSubscription();
Em service-worker.js, cole o código abaixo:
self.addEventListener('push', (event) => {
const json = JSON.parse(event.data.text())
console.log('Push Data', event.data.text())
self.registration.showNotification(json.header, json.options)
});
Agora o frontend está configurado. Com a assinatura, você pode enviar notificações push quando desejar, até que o usuário revogue a permissão.
Enviando Notificações do Backend com Node.js
Para facilitar, utilize o módulo npm web-push.
Exemplo de código para envio de notificações a partir de um servidor Node.js:
const webPush = require('web-push');
// pushSubscription é a assinatura que você enviou do frontend para salvar no banco de dados
const pushSubscription = {"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}};
//chave publica do seu certificado web
const vapidPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY';
//chave privada do seu certificado web
const vapidPrivateKey = 'chave privada do certificado web';
var payload = JSON.stringify({
"options": {
"body": "Teste de notificação push PWA do backend",
"badge": "/assets/icon/icon-152x152.png",
"icon": "/assets/icon/icon-152x152.png",
"vibrate": [100, 50, 100],
"data": {
"id": "458",
},
"actions": [{
"action": "view",
"title": "Visualizar"
}, {
"action": "close",
"title": "Fechar"
}]
},
"header": "Notificação de etechpt.com-PWA Demo"
});
var options = {
vapidDetails: {
subject: 'mailto:[email protected]',
publicKey: vapidPublicKey,
privateKey: vapidPrivateKey
},
TTL: 60
};
webPush.sendNotification(
pushSubscription,
payload,
options
).then(data => {
return res.json({status : true, message : 'Notificação enviada'});
}).catch(err => {
return res.json({status : false, message : err });
});
O código acima enviará uma notificação push para a assinatura, ativando o evento push no service worker.
Enviando Notificações do Backend com PHP
Para o backend PHP, utilize o pacote composer web-push-php. Confira um exemplo de código para envio de notificações:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require __DIR__.'/../vendor/autoload.php';
use MinishlinkWebPushWebPush;
use MinishlinkWebPushSubscription;
// assinatura armazenada no banco de dados
$subsrciptionJson = '{"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}}';
$payloadData = array (
'options' => array (
'body' => 'Teste de notificação push PWA do backend',
'badge' => '/assets/icon/icon-152x152.png',
'icon' => '/assets/icon/icon-152x152.png',
'vibrate' =>
array (
0 => 100,
1 => 50,
2 => 100,
),
'data' =>
array (
'id' => '458',
),
'actions' =>
array (
0 =>
array (
'action' => 'view',
'title' => 'Visualizar',
),
1 =>
array (
'action' => 'close',
'title' => 'Fechar',
),
),
),
'header' => 'Notificação de etechpt.com-PWA Demo',
);
// auth
$auth = [
'GCM' => 'sua chave privada do projeto', // deprecated e opcional, aqui apenas por compatibilidade
'VAPID' => [
'subject' => 'mailto:[email protected]', // pode ser um mailto: ou seu site
'publicKey' => 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY', // (recomendado) chave publica P-256 não comprimida codificada em Base64-URL
'privateKey' => 'sua chave privada do certificado web', // (recomendado) multiplicador secreto da chave privada codificado em Base64-URL
],
];
$webPush = new WebPush($auth);
$subsrciptionData = json_decode($subsrciptionJson,true);
// webpush 6.0
$webPush->sendOneNotification(
Subscription::create($subsrciptionData),
json_encode($payloadData) // opcional (padrão null)
);
Conclusão
Este guia detalha como transformar aplicativos web em PWAs, com a adição de notificações push. O código fonte desse artigo está disponível aqui, e a demonstração pode ser acessada aqui. A funcionalidade de notificação push foi testada através do envio de mensagens do backend com o auxílio dos exemplos de código apresentados.