Pesquisa de site

Como lidar com notificações Web Push em sites e PWAs


As notificações push são comuns na web moderna. Eles permitem que você comunique informações oportunas ao usuário, mesmo que seu site não esteja realmente aberto. O navegador do usuário lida com eventos push recebidos e exibe notificações usando superfícies de interface do usuário do sistema, como o Windows Action Center e a tela de bloqueio do Android.

A implementação do Web Push em seu site ou PWA requer a combinação de duas APIs de navegador distintas. O código responsável por assinar e receber notificações usa o componente Push API dos service workers. Esse código é executado continuamente em segundo plano e será invocado pelo navegador quando uma nova notificação precisar ser tratada.

Quando um evento é recebido, o service worker deve usar a API de notificação para realmente exibir a notificação. Isso cria um alerta visual por meio de interfaces no nível do sistema operacional.

Aqui está um guia completo para fazer o Web Push funcionar em seu site. Assumiremos que você já possui um componente do lado do servidor que pode registrar assinaturas push e enviar seus alertas.

O trabalhador de serviço

Vamos começar com o service worker. Os service workers têm várias funções – eles podem armazenar dados em cache para uso offline, executar sincronizações periódicas em segundo plano e atuar como manipuladores de notificação. Service workers usam uma arquitetura orientada a eventos. Depois de registrado por um site, o navegador do usuário invocará o service worker em segundo plano quando os eventos aos quais ele se inscrever forem gerados.

Para Web Push, um evento principal é necessário: push. Isso recebe um objeto PushEvent que permite acessar a carga enviada do servidor.

self.addEventListener("push", e => {
 
    const payload = JSON.parse(e.data.text());
 
    e.waitUntil(self.registration.showNotification(
        payload.title,
        {
            body: payload.body,
            icon: "/icon.png"
        }
    ));
 
});

O código acima configura um service worker capaz de reagir a eventos push recebidos. Ele espera que o servidor envie cargas JSON com esta aparência:

{
    "title": "Title text for the notification",
    "body": "This is the longer text of the notification."
}

Quando um evento push é recebido, o service worker exibe uma notificação do navegador chamando a função showNotification() disponível em sua propriedade self.registration. A função é agrupada em uma chamada waitUntil() para que o navegador aguarde a exibição da notificação antes de encerrar o service worker.

A função showNotification() recebe dois argumentos: o texto do título da notificação e um objeto de opções. Duas opções são passadas neste exemplo, algum texto de corpo mais longo e um ícone para exibir na notificação. Muitas outras opções estão disponíveis que permitem configurar padrões de vibração, emblemas personalizados e requisitos de interação. Nem todos os navegadores e sistemas operacionais suportam todos os recursos expostos pela API.

Complete o lado do service worker do código registrando-o novamente em seu JavaScript principal:

if (navigator.serviceWorker) {
    // replace with the path to your service worker file
    navigator.serviceWorker.register("/sw.js").catch(() => {
        console.error("Couldn't register the service worker.")
    });
}

Este código deve ser executado em cada carregamento de página. Ele garante que o navegador ofereça suporte aos service workers e, em seguida, registra o arquivo do worker. Os navegadores atualizarão automaticamente o service worker sempre que a cópia do servidor exibir diferenças de bytes em relação à versão atualmente instalada.

Inscrevendo-se para assinaturas push

Agora você precisa assinar o navegador para enviar notificações. O código a seguir pertence ao seu arquivo JavaScript principal, fora do service worker.

async function subscribeToPush() {
    if (navigator.serviceWorker) {
 
        const reg = await navigator.serviceWorker.getRegistration();
 
        if (reg && reg.pushManager) {
 
            const subscription = await reg.pushManager.getSubscription();
 
            if (!subscription) {
 
                const key = await fetch("https://example.com/vapid_key");
                const keyData = await key.text();
 
                const sub = await reg.pushManager.subscribe({
                    applicationServerKey: keyData,
                    userVisibleOnly: true
                });
 
                await fetch("https://example.com/push_subscribe", {
                    method: "POST",
                    headers: {"Content-Type": "application/json"},
                    body: JSON.stringify({
                        endpoint: sub.endpoint,
                        expirationTime: sub.expirationTime,
                        keys: sub.toJSON().keys
                    })
                });
 
            }
 
        }
 
    }
}

Em seguida, chame sua função para inscrever o navegador para enviar notificações:

await subscribeToPush();

Vamos ver o que o código de assinatura está fazendo. As primeiras linhas verificam a presença de um service worker, recuperam seu registro e detectam o suporte à notificação por push. pushManager não será definido em navegadores que não suportam Web Push.

Chamar pushManager.getSubscription() retorna uma promessa que resolve um objeto que descreve a assinatura push atual do navegador para seu site. Se isso já estiver definido, não precisamos inscrever o usuário novamente.

O fluxo de assinatura real começa com a solicitação de busca das chaves VAPID do servidor. A especificação VAPID é um mecanismo que permite ao navegador verificar se os eventos push estão realmente vindo do seu servidor. Você deve expor um endpoint de API de servidor que forneça uma chave VAPID. Isso é fornecido à função pushManager.subscribe() para que o navegador conheça a chave confiável. A opção separada userVisibleOnly indica que exibiremos apenas notificações visíveis na tela.

A chamada pushManager.subscribe() retorna um objeto PushSubscription que descreve sua nova assinatura. Esses dados são enviados ao servidor em outra solicitação de busca. Em um aplicativo real, você também enviaria o ID do usuário ativo para poder vincular a assinatura push ao dispositivo dele.

Seu código do lado do servidor para enviar uma notificação por push a um usuário deve ser algo como isto:

  1. Consulte seu armazenamento de dados para todas as assinaturas push vinculadas ao usuário de destino.
  2. Envie sua carga útil de notificação para o endpoint indicado por cada assinatura, certificando-se de incluir as chaves de autenticação da assinatura (chaves nos dados enviados pelo navegador durante a assinatura). Assine o evento com a mesma chave VAPID que você enviou ao navegador.

O endpoint de cada assinatura fará referência à plataforma de entrega de notificação do fornecedor do navegador. Esta URL já inclui um identificador exclusivo para a assinatura. Quando você envia uma carga para o endpoint, o processo em segundo plano do navegador eventualmente receberá os dados e chamará seu service worker. Para o Chrome no Android, o processo do navegador é integrado diretamente ao daemon de notificação do sistema.

Quando inscrever o usuário?

Ao configurar os fluxos de assinatura, lembre-se de que o usuário terá que reconhecer um prompt de permissão do navegador antes que o registro seja concluído. Muitos navegadores ocultam ou rejeitam automaticamente solicitações de permissão não solicitadas; em qualquer caso, pedir a um usuário que se inscreva no momento em que ele acessa seu site pode não gerar o resultado desejado.

Você obtém a melhor chance de uma inscrição bem-sucedida associando solicitações de assinatura a uma ação direta do usuário. Considere fornecer um banner no aplicativo que explique os benefícios de ativar as notificações e ofereça um botão “Ativar agora”. Você pode verificar se o usuário já está inscrito e ocultar o banner com a função pushManager.getSubscription() mostrada acima.

Clicar no botão ativar deve chamar sua função de assinatura. O processo pode levar alguns segundos enquanto o navegador configura o registro e suas chamadas de rede são concluídas. A exibição de um botão giratório de carregamento durante esse período ajudará a manter o usuário informado.

Os usuários também devem ter uma maneira de cancelar a inscrição. Embora possam revogar a permissão do navegador a qualquer momento, alguns usuários procurarão uma opção no aplicativo, especialmente se tiverem instalado seu site como um PWA.

Aqui está uma implementação simples de cancelamento de inscrição:

async function unsubscribePush() {
 
    const reg = await navigator.serviceWorker.getRegistration();
    const subscription = await reg.pushManager.getSubscription();
 
    if (subscription) {
        await subscription.unsubscribe();
        await fetch(`https://example.com/push_unsubscribe/${subscription.endpoint}`, {method: "DELETE"});
    }
    else {
        // already subscribed
    }
 
}

Chamar unsubscribe() em um PushSubscription cancela a assinatura, revertendo o navegador para seu estado padrão. Seu service worker deixará de receber eventos push. O endpoint da assinatura é enviado ao seu servidor para que você possa removê-lo do armazenamento de dados e evitar o envio de dados para o que agora é um URL morto.

Lidando com expirações e renovações

Você deve ter notado a propriedade expirationTime no objeto PushSubscription criado pelo navegador. Isso nem sempre será definido; quando estiver, o dispositivo deixará de receber notificações após esse período.

Na prática, expirationTime não é usado atualmente nos principais navegadores. Os tokens produzidos pelo Chrome não expiram até que sejam cancelados manualmente, então expirationTime é sempre null. O Firefox também não define expirationTime, mas seu serviço de notificação pode substituir assinaturas durante sua vida útil.

Você pode responder ao navegador alterando sua assinatura push ativa implementando o evento pushsubscriptionchange em seu service worker. Infelizmente existem duas versões deste evento: a implementação original, atualmente utilizada pelo Firefox, e a nova v2, ainda não suportada em nenhum navegador.

A especificação original tem sérios problemas de usabilidade que dificultam a resposta ao evento. Quando você recebe um evento v1, o navegador excluiu a assinatura original e você precisa criar uma nova manualmente. O problema é que, sem acesso à assinatura expirada, você não pode emitir uma solicitação de substituição para o seu servidor - você não tem como acessar o URL endpoint antigo.

A especificação v2 resolve isso fornecendo um evento com as propriedades oldSubscription e newSubscription. Ao receber o evento, a assinatura antiga foi cancelada, mas você ainda pode acessar suas propriedades. A nova assinatura agora é criada para você pelo navegador.

Aqui está um exemplo de implementação de pushsubscriptionchange com a nova especificação:

self.addEventListener("pushsubscriptionchange", e => {
    e.waitUntil(async () => {
        await fetch("https://example.com/push_change", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                auth: (e.newSubscription.toJSON().keys?.auth || null),
                endpoint: e.newSubscription.endpoint,
                endpointOld: e.oldSubscription.endpoint,
                expirationTime: e.newSubscription.expirationTime,
                p256dh: (e.newSubscription.toJSON().keys?.p256dh || null)
            })
        });
    });
});

Os endpoints são exclusivos para que seu servidor possa pesquisar a assinatura antiga e atualizar suas propriedades com as da nova assinatura. Se você quiser adicionar suporte para a especificação antiga também, precisará rastrear manualmente o ponto de extremidade da assinatura ativa fora da API push. Armazená-lo em localStorage ou IndexedDB permitirá que você o acesse dentro de seu manipulador pushsubscriptionchange para que você possa solicitar ao servidor que substitua a assinatura.

A especificação revisada é muito mais fácil de implementar do que sua contraparte mais antiga. Mesmo que ainda não seja suportado em navegadores, vale a pena adicioná-lo ao seu service worker de qualquer maneira. Algumas linhas de código irão proteger o seu push em relação aos novos lançamentos do navegador.

Adicionando botões de ação

As notificações por push podem incluir botões interativos que permitem ao usuário realizar ações imediatas. Aqui está uma chamada showNotification() que cria uma:

self.registration.showNotification(
    "Notification with actions",
    {
        body: "This notification has a button.",
        actions: [
            {
                action: "/home",
                title: "Go to Homescreen",
                icon: "/home.png"
            }
        ]
    }
);

Cada notificação pode incluir várias ações, cada uma com um rótulo, ícone e ação. A última propriedade deve identificar uma ação que seu aplicativo pode iniciar em resposta ao pressionamento do usuário.

Quando o usuário toca em uma ação, seu service worker recebe um evento notificationclick:

self.addEventListener("notificationclick", e => {
    const uri = e.action;
    const notification = e.notification;
    notification.close();
    clients.openWindow(`${self.location.origin}${action}`);
});

Estamos usando a propriedade action para declarar um URI para o qual o usuário pode navegar. Uma nova guia é aberta para o URI quando a notificação é pressionada. Chamar notification.close() garante que a notificação também seja descartada. Caso contrário, algumas plataformas farão com que o usuário o deslize manualmente.

Resumo

A implementação do Web Push pode parecer assustadora se você ainda não trabalhou com as APIs relevantes. Mais do que as preocupações técnicas, você deve manter a experiência do usuário em primeiro plano e certificar-se de comunicar por que vale a pena ativar as notificações.

A assinatura e o cancelamento da assinatura para push ocorrem no código JavaScript principal do seu aplicativo, usando as APIs navigator.serviceWorker. O código que responde a novos eventos push e exibe notificações do navegador reside no próprio service worker.

O Web Push agora é suportado pela maioria dos principais navegadores da Web, sendo o Safari a exceção proeminente. Lembre-se de que as notificações serão processadas de maneira diferente em cada família de navegador e sistema operacional, portanto, não presuma que um recurso específico da API showNotification() estará disponível universalmente.

Artigos relacionados:


Todos os direitos reservados. © Linux-Console.net • 2019-2024