Como converter WebApp como PWA com notificação por push

Neste artigo, veremos como converter um aplicativo Web ou site em um PWA com uma notificação por push usando o Firebase Cloud Messaging.

No mundo moderno, a maioria dos aplicativos da Web está sendo convertida em um PWA (Progressive Web App) porque fornece recursos como suporte offline, notificação por push, sincronização em segundo plano. Os recursos do PWA tornam nosso aplicativo da Web mais parecido com um aplicativo nativo e proporcionam uma experiência de usuário rica.

Por exemplo, grandes empresas como Twitter e Amazon converteram seu Web App em PWA para maior engajamento do usuário.

O que é um PWA?

PWA = (Aplicativo Web) + (alguns recursos do aplicativo nativo)

PWA é o mesmo aplicativo da Web (HTML+CSS+JS). Funciona da mesma forma que seu aplicativo Web em todos os navegadores, como funcionava anteriormente. Mas pode ter recursos nativos quando seu site é carregado em um navegador moderno. Isso torna seu aplicativo da Web mais poderoso do que antes e também o torna mais escalável porque podemos pré-buscar e armazenar em cache ativos no front-end, reduzindo as solicitações ao seu servidor de back-end.

Como o PWA é diferente do aplicativo Web

  • Instalável: Seu Web App pode ser instalado como um aplicativo nativo
  • Progressivo: Funciona da mesma forma que seu aplicativo da web, mas com alguns recursos nativos
  • Experiência de aplicativo nativo: o usuário pode usar e navegar no aplicativo da Web como um nativo, uma vez instalado.
  • Facilmente acessível: Ao contrário do nosso aplicativo da web, não há necessidade de nossos usuários digitarem endereços da web cada vez que visitam. Uma vez instalado, ele pode ser aberto com um único toque.
  • Cache de aplicativos: Antes do PWA, o único mecanismo de cache com o qual nosso aplicativo da Web implementava é usando o cache HTTP, disponível apenas para o navegador. Mas com o PWA, podemos armazenar em cache as coisas usando o próprio código do lado do cliente, que não está disponível em um aplicativo da web.
  • Publicação na loja (App/Play): O PWA pode ser publicado na Google Play Store e na IOS App Store.

Converter seu aplicativo para PWA só o tornará mais poderoso.

Por que as empresas devem considerar o PWA

Enquanto a maioria dos nossos clientes nos procuram e pedem para desenvolver a solução Web App primeiro e depois pedem aplicativos Android e iOS. Tudo o que vamos fazer é construir a mesma funcionalidade no aplicativo da web para o aplicativo Android/IOS por uma equipe separada, o que leva mais custo de desenvolvimento e mais tempo para o mercado.

Mas algum cliente tem um orçamento limitado ou algum cliente pode pensar que o tempo de lançamento no mercado é mais importante para seu produto.

A maioria dos requisitos do cliente pode ser satisfeita pelos próprios recursos do PWA. Para eles, sugerimos apenas PWA e damos a eles uma ideia de converter seu PWA como um aplicativo Android usando TWA se quiserem implantar no Playstore.

Se sua necessidade realmente precisa de recursos de aplicativos nativos que não podem ser atendidos pelo PWA. Os clientes podem ir e desenvolver ambos os aplicativos como desejarem. Mas mesmo nesse cenário. Eles podem implantar o PWA na Play Store até que o desenvolvimento do Android seja concluído.

Exemplo: Titan Eyeplus

Inicialmente, eles desenvolveram um aplicativo PWA e o implantaram na loja de jogos usando o TWA (Trusted Web Activity). Uma vez que eles concluíram o desenvolvimento do aplicativo Android. Eles implantaram seu aplicativo Android real na loja de jogos. Eles alcançaram o tempo de lançamento no mercado usando o PWA e o custo do desenvolvimento.

Recursos de PWA

O PWA oferece aos nossos aplicativos da Web recursos semelhantes a aplicativos nativos.

As principais características são:

  • Instalável: Um aplicativo da Web instalado como um aplicativo nativo.
  • Cache: o cache de aplicativos é possível, o que dá suporte offline ao nosso aplicativo.
  • Push Notification: Push Notification pode ser enviada de nosso servidor para envolver nossos usuários em nosso site.
  • Geofencing: O aplicativo pode ser notificado por um evento sempre que a localização do dispositivo mudar.
  • Solicitação de pagamento: habilite o pagamento em seu aplicativo com uma ótima experiência de usuário como um aplicativo nativo.
  Como calcular a variação percentual com tabelas dinâmicas no Excel

E muitos outros recursos para vir no futuro.

Outras características são:

  • Atalhos: URLs de acesso rápido adicionados no arquivo de manifesto.
  • API de compartilhamento da Web: permita que seu aplicativo receba dados compartilhados de outros aplicativos.
  • Badge API: para mostrar a contagem de notificações em seu PWA instalado.
  • API Periodic Background Sync: salva os dados do seu usuário até que ele seja conectado à rede.
  • Seletor de contatos: usado para selecionar contatos do celular do usuário.
  • File Picker: Usado para acessar o arquivo no sistema local/móvel

Vantagem do PWA sobre o aplicativo nativo

O aplicativo nativo tem um desempenho melhor que o PWA e tem mais recursos que o PWA. Mas ainda assim, tem algumas vantagens sobre o aplicativo nativo.

  • O PWA é executado em plataformas cruzadas como Android, IOS, Desktop.
  • Reduz seu custo de desenvolvimento.
  • Fácil implantação de recursos em comparação com um aplicativo nativo.
  • Facilmente detectável porque o PWA (site) é amigável para SEO
  • Seguro porque funciona apenas em HTTPS

Desvantagens do PWA sobre o aplicativo nativo

  • Funcionalidades limitadas estão disponíveis em comparação com um aplicativo nativo.
  • Os recursos de PWA não são garantidos para oferecer suporte a todos os dispositivos.
  • A marca do PWA é baixa porque não está disponível na App Store ou na Play Store.

Você pode implantar seu PWA como um aplicativo Android na Play Store usando o Android Atividade confiável na Web (TWA). Vai ajudar a sua marca.

Coisas necessárias para converter Web App em PWA

Para converter, qualquer aplicativo da Web ou site para PWA.

  • Service-Worker: o núcleo de qualquer aplicativo PWA para Caching, Pushes Notification, um proxy para nossos pedidos.
  • Arquivo de manifesto: Contém detalhes sobre sua aplicação web. Ele costumava baixar nosso aplicativo como um aplicativo nativo na tela inicial.
  • Logotipo do aplicativo: imagem de alta qualidade 512 x 512 px para o ícone do seu aplicativo. Logotipo do aplicativo necessário para o PWA na tela inicial, tela inicial, etc. Portanto, temos que criar um conjunto de imagens de proporção 1:1 para o nosso aplicativo usando qualquer ferramenta.
  • Design responsivo: o aplicativo da web deve ser responsivo para funcionar em diferentes tamanhos de tela.

O que é Service Worker:

Um service worker (script do lado do cliente) é um proxy entre seu aplicativo Web e o lado externo, fornecendo notificações push para nosso aplicativo Web e dando suporte ao cache.

O Service Worker é executado independentemente do javascript principal. Portanto, ele não tem acesso à API DOM. Ele só pode acessar API IndexedDB, Buscar API, API de armazenamento em cache. Mas ele pode se comunicar com o thread principal com uma mensagem.

Serviço prestado pelo trabalhador de serviço:

  • Interceptando solicitações HTTP de seu domínio de origem.
  • Receba notificações push do seu servidor.
  • Disponibilidade offline do nosso aplicativo

O service worker controla seu aplicativo e pode manipular suas solicitações, mas é executado de forma independente. Portanto, por esse motivo, o domínio de origem deve ser habilitado com HTTPS para evitar um ataque man-in-the-middle.

O que é o arquivo de manifesto

Um arquivo de manifesto (manifest.json) tem detalhes sobre nosso aplicativo PWA para informar ao navegador.

  • nome: Nome do aplicativo
  • short_name: Nome abreviado para nosso aplicativo. Se fornecido
  • com o nome da propriedade e o nome_curto, o navegador usará o nome_curto.
  • description: Descrição para descrever nossa aplicação.
  • start_url: Para especificar a página inicial do aplicativo quando nosso PWA foi lançado.
  • ícones: Conjunto de imagens para PWA para tela inicial, etc.
  • background_color: Para definir a cor de fundo da tela inicial em nosso aplicativo PWA.
  • display: Para personalizar a interface do nosso navegador para mostrar em nosso aplicativo PWA.
  • theme_color: cor do tema do aplicativo PWA.
  • scope: escopo de URL do nosso aplicativo a ser considerado para o PWA. O padrão é o local do arquivo de manifesto localizado.
  • atalhos: Links rápidos para nosso aplicativo PWA.

Converter aplicativo da Web para PWA

Para fins de demonstração, criei 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
  Como encontrar palavras inteiras no MS Word em vez de strings de texto

Se você já possui algum site ou aplicativo da Web, tente convertê-lo em PWA seguindo as etapas abaixo.

Criar imagens obrigatórias para PWA

Em primeiro lugar, pegue o logotipo do seu aplicativo e corte-o em um tamanho de proporção de 1:1 em 5 tamanhos diferentes. Eu tenho usado https://tools.crawlink.com/tools/pwa-icon-generator/ para obter diferentes tamanhos de imagem rapidamente. Então você também pode usar.

Criar um arquivo de manifesto

Em segundo lugar, crie um arquivo manifest.json para seu aplicativo Web com os detalhes do aplicativo. Para a demonstração, criei um arquivo de manifesto para o site etechpt.com.

{
	"name": "etechpt.com",
	"short_name": "etechpt.com",
	"description": "etechpt.com produces high-quality technology & finance articles, makes tools, and APIs to help businesses and people grow.",
	"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 articles on Security, Sysadmin, Digital Marketing, Cloud Computing, Development, and many other topics.",
			"url": "https://geekflare.com/articles",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Authors",
			"short_name": "Authors",
			"description": "etechpt.com - Authors",
			"url": "/authors",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Tools",
			"short_name": "Tools",
			"description": "etechpt.com - Tools",
			"url": "https://etechpt.com.com/tools",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Deals",
			"short_name": "Deals",
			"description": "etechpt.com - Deals",
			"url": "/deals",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		}
	]
}

Registrar Service-worker

crie um arquivo de script register-service-worker.js e service-worker.js na pasta raiz.

O primeiro, register-service-worker.js é o arquivo javascript que será executado na thread principal que pode acessar a API DOM. Mas service-worker.js é um script de service worker que é executado independentemente do thread principal e seu tempo de vida também é curto. Ele é executado sempre que os eventos chamam os service workers e são executados até concluir o processo.

Ao verificar o arquivo javascript do thread principal, você pode verificar se o service worker está registrado nele. caso contrário, você pode registrar o script do service worker (service-worker.js).

cole o snippet abaixo em register-service-worker.js:

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
        navigator.serviceWorker.register('/service-worker.js');
    });
}

Cole o snippet abaixo em service-worker.js

self.addEventListener('install', (event) => { // event when service worker install
    console.log( 'install', event);
    self.skipWaiting();
});

self.addEventListener('activate', (event) => { // event when service worker activated
    console.log('activate', event);
    return self.clients.claim();
});

self.addEventListener('fetch', function(event) { // HTTP request interceptor
    event.respondWith(fetch(event.request)); // send all http request without any cache logic
    /*event.respondWith(
        caches.match(event.request).then(function(response) {
            return response || fetch(event. request);
        })
    );*/ // cache new request. if already in cache serves with the cache.
});

Não nos concentramos em como habilitar o cache para suporte offline. Falamos apenas sobre como converter aplicativos da Web para PWA.

Adicione o arquivo de manifesto e o script na tag all head da sua página HTML.

<link rel="manifest" href="https://etechpt.com.com/manifest.json">
<script src="/register-service-worker.js"></script>

Atualize a página depois de adicionar. Agora você pode instalar seu aplicativo como abaixo no Chrome móvel.

Na tela inicial, o aplicativo é adicionado.

Se você estiver usando o WordPress. Tente usar o plug-in do conversor de PWA existente. Para vueJS ou reactJS, você pode seguir o método acima ou usar os módulos npm PWA existentes para acelerar seu desenvolvimento. Como os módulos npm do PWA já estão habilitados com cache de suporte offline, etc.

Ativar notificação por push

As notificações push da Web são enviadas ao navegador para fazer com que nossos usuários se envolvam/interajam com nosso aplicativo com mais frequência. Podemos habilitá-lo usando

  • API de notificação: é usado para configurar como nossa notificação push deve ser mostrada ao usuário.
  • API de envio: É usado para receber mensagens de notificação enviadas do nosso servidor para o navegador.

A primeira etapa para habilitar a notificação por push em nosso aplicativo é verificar a API de notificação e obter permissão do usuário para mostrar uma notificação. Para isso, copie e cole o snippet abaixo em seu 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: 'Thanks for allowing push notification !',
                    icon:  '/assets/icons/icon-512x512.png',
                    vibrate: [100, 50, 100],
                    data: {
                      dateOfArrival: Date.now(),
                      primaryKey: 0
                    }
                  };
    
            reg.showNotification(notificationTitle, options);
        });
    }
};

Se tudo deu certo. Você receberá uma notificação do aplicativo.

‘Notificação’ na janela nos informará que a API de notificação é compatível com esse navegador. Notification.permission informará que o usuário tem permissão para mostrar a notificação. Se o usuário permitiu nossa aplicação o valor será ‘concedido’. se o usuário rejeitou o valor será ‘bloqueado’.

Ative o Firebase Cloud Messaging e crie uma assinatura

Agora começa a parte real. Para enviar notificações do seu servidor para o usuário, precisamos de um endpoint/assinatura exclusivo para cada usuário. Para isso, usaremos as mensagens na nuvem do Firebase.

  Como usar o Snap Camera no Google Meet

Como primeiro passo, crie uma conta do Firebase acessando este link https://firebase.google.com/ e pressione começar.

  • Crie um novo projeto com um nome e pressione continuar. Vou criá-lo com o nome etechpt.com.
  • Na próxima etapa, o Google Analytics é ativado por padrão. Você pode alternar que não precisamos disso agora e pressionar continuar. Você pode ativá-lo posteriormente no console do Firebase, se necessário.
  • Depois que o projeto for criado, ele ficará como abaixo.
  • Em seguida, vá para as configurações do projeto e clique em mensagens na nuvem e gere chaves.

    Das etapas acima, você tem 3 chaves.

    • chave do servidor do projeto
    • Chave privada de certificados de push da Web
    • Chave pública de certificados push da Web

    Agora cole o snippet abaixo em register-service-worker.js:

    const updateSubscriptionOnYourServer = subscription => {
        console.log('Write your ajax code here to save the user subscription in your DB', subscription);
        // write your own ajax request method using fetch, jquery, axios to save the subscription in your server for later use.
    };
    
    const subscribeUser = async () => {
        const swRegistration = await navigator.serviceWorker.getRegistration();
        const applicationServerPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY'; // paste your webpush certificate public key
        const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
        swRegistration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey
        })
        .then((subscription) => {
            console.log('User is subscribed newly:', subscription);
            updateSubscriptionOnServer(subscription);
        })
        .catch((err) => {
            if (Notification.permission === 'denied') {
              console.warn('Permission for notifications was denied')
            } else {
              console.error('Failed to subscribe the user: ', 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('User IS Already subscribed.');
                updateSubscriptionOnYourServer(subscription);
            } else {
                console.log('User is NOT subscribed. Subscribe user newly');
                subscribeUser();
            }
        });
    };
    
    checkSubscription();

    Cole o snippet abaixo em service-worker.js.

    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 tudo pronto no front-end. Ao usar a assinatura, você pode enviar notificações push para seu usuário sempre que quiser, até que não tenham sido negados os serviços push.

    Enviar do back-end node.js

    Você pode usar o web-push módulo npm para facilitar.

    Exemplo de trecho para enviar notificação por push do servidor nodeJS.

    const webPush = require('web-push');
        // pushSubscription is nothing but subscription that you sent from your front-end to save it in DB
        const pushSubscription = {"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}};
        //your web certificates public-key
        const vapidPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY';
        //your web certificates private-key
        const vapidPrivateKey = 'web-certificate private key';
    
        var payload = JSON.stringify({
          "options": {
            "body": "PWA push notification testing fom backend",
            "badge": "/assets/icon/icon-152x152.png",
            "icon": "/assets/icon/icon-152x152.png",
            "vibrate": [100, 50, 100],
            "data": {
              "id": "458",
            },
            "actions": [{
              "action": "view",
              "title": "View"
            }, {
              "action": "close",
              "title": "Close"
            }]
          },
          "header": "Notification from 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 : 'Notification sent'});
        }).catch(err => {
          return res.json({status : false, message : err });
        });

    O código acima enviará uma notificação por push para a assinatura. O evento push no service-worker será acionado.

    Empurre do back-end do PHP

    Para backend PHP, você pode usar o web-push-php pacote compositor. Verifique o código de exemplo para enviar notificações push abaixo.

    <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
    
    require __DIR__.'/../vendor/autoload.php';
    use MinishlinkWebPushWebPush;
    use MinishlinkWebPushSubscription;
    
    // subscription stored in DB
    $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' => 'PWA push notification testing fom 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' => 'View',
                      ),
                      1 => 
                      array (
                        'action' => 'close',
                        'title' => 'Close',
                      ),
                    ),
    ),
    'header' => 'Notification from etechpt.com-PWA Demo',
    );
    
    // auth
    $auth = [
        'GCM' => 'your project private-key', // deprecated and optional, it's here only for compatibility reasons
        'VAPID' => [
            'subject' => 'mailto:[email protected]', // can be a mailto: or your website address
            'publicKey' => 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY', // (recommended) uncompressed public key P-256 encoded in Base64-URL
            'privateKey' => 'your web-certificate private-key', // (recommended) in fact the secret multiplier of the private key encoded in Base64-URL
        ],
    ];
    
    $webPush = new WebPush($auth);
    
    $subsrciptionData = json_decode($subsrciptionJson,true);
    
    
    // webpush 6.0
    $webPush->sendOneNotification(
      Subscription::create($subsrciptionData),
      json_encode($payloadData) // optional (defaults null)
    );

    Conclusão

    Espero que isso lhe dê uma idéia sobre como converter aplicativos da Web para PWA. Você pode verificar o código-fonte deste artigo aqui e demonstre aqui. Eu testei a notificação por push enviando-a do back-end com a ajuda do código de exemplo também.