Notificações do iOS podem perder permissão para enviar se o usuário estiver ativo no momento

As notificações do iOS podem perder a permissão de push se a notificação for suprimida. O código do Discourse está configurado para suprimir opcionalmente as notificações e, portanto, perderá a permissão de push quando isso acontecer três vezes.

Aqui está o código para o service worker de notificação push.

Este código tem um bug crítico, na linha 178. Lá, o service worker verifica se o usuário está ativo (ou seja, não ocioso). Nesse caso, o evento push retorna false sem mostrar uma notificação.

(Ele também verifica payload.hide_when_active, mas acontece que hide_when_active é sempre verdadeiro, e, portanto, este código sempre retorna false quando o usuário está ativo.)

A Apple proíbe pushes silenciosos, revogando a permissão de push após três eventos que não mostram notificações

Isso não é aceitável de acordo com as regras da Apple para notificações push.

https://webkit.org/blog/12945/meet-web-push/

Poder e privacidade

Tanto o projeto de código aberto WebKit quanto a Apple tratam a privacidade como um direito humano fundamental. Assim como outros recursos privilegiados da plataforma web, solicitar uma assinatura de push requer um gesto explícito do usuário. Também exige que você defina o sinalizador userVisibleOnly como true e cumpra essa promessa mostrando sempre uma notificação em resposta a uma mensagem push.

A API Web Push não é um convite para execução em segundo plano silenciosa, pois isso violaria a confiança do usuário e afetaria a vida útil da bateria do usuário.

Violações da promessa userVisibleOnly resultarão na revogação de uma assinatura de push.

(ênfase minha)

Isso é explicado em mais detalhes no vídeo WWDC da Apple sobre Web Pushes, em 9:57

https://developer.apple.com/videos/play/wwdc2022/10098/?time=596

Note que estamos declarando explicitamente que prometemos sempre tornar os pushes visíveis para o usuário. Embora o padrão para a API JavaScript Push acomode opcionalmente a execução em segundo plano silenciosa em resposta a um push, a maioria dos navegadores não suporta isso. O Safari não suporta isso.

… e depois em 13:35:

https://developer.apple.com/videos/play/wwdc2022/10098/?time=814

Como mencionado quando mostrei o código sobre como solicitar uma assinatura de push, você deve prometer que os pushes serão visíveis para o usuário. Lidar com um evento de push não é um convite para que seu JavaScript obtenha execução em segundo plano silenciosa. Fazer isso violaria tanto a confiança do usuário quanto a vida útil da bateria do usuário. Ao lidar com um evento de push, você é, na verdade, obrigado a postar uma notificação no Notification Center. Outros navegadores têm contramedidas contra a violação da promessa de tornar os pushes visíveis para o usuário, e o Safari também. Na compilação beta do macOS Ventura, após três eventos de push nos quais você falhar em postar uma notificação em tempo hábil, a assinatura de push do seu site será revogada. Você precisará passar pelo fluxo de permissão novamente.

(ênfase minha)

A Apple recomenda mostrar notificações imediatamente, não após o fechamento de notificações

O código recomendado pela Apple se parece com isto, em 11:39:

https://developer.apple.com/videos/play/wwdc2022/10098/?time=699

self.addEventListener('push', (event) => {
    let pushMessageJSON = event.data.json();

    // Nosso servidor coloca tudo o que é necessário para mostrar a notificação
    // em nossos dados JSON.
    event.waitUntil(self.registration.showNotification(pushMessageJSON.title, {
        body: pushMessageJSON.body,
        tag: pushMessageJSON.tag,
        actions: [{
            action: pushMessageJSON.actionURL,
            title: pushMessageJSON.actionTitle,
        }]
    }));
}

Lembre-se de que, quando assinamos o push, nosso JavaScript prometeu que eles seriam sempre visíveis para o usuário? Isso significa que devemos sempre mostrar uma notificação nativa da plataforma em resposta a cada push. É melhor fazer isso o mais cedo possível em seu manipulador de eventos push.

O código do Discourse não está seguindo a melhor prática recomendada. O código do Discourse primeiro fecha todas as notificações e só então mostra uma notificação.

O Discourse deve sempre chamar showNotification em resposta a um evento de push, e deve sempre fazê-lo o mais rápido possível.

8 curtidas

@Falco / @featheredtoast o que acham disso?

Qual seria o impacto de removermos tanto a verificação de fechamento quanto a de inatividade?

No geral, não tenho certeza alguma sobre essa verificação de inatividade, sinto que ela só está causando confusão.

No pior cenário, se exigirmos isso no Android ou Desktop (não Safari), sempre poderemos adicionar uma condicional aqui, mas minha sensação é que deveríamos simplesmente remover o código aqui.

Excelente depuração @dfabulich :hugs:

3 curtidas

Eu observo que tudo bem fazer o fechamento, eu acho, desde que seja feito depois de chamar showNotification.

… mas, honestamente, não vejo o sentido de fechar notificações. Tanto faz, deixe-as se acumularem na bandeja de notificações, na minha opinião.

Observação: Esta API não deve ser usada apenas para remover a notificação da tela após um tempo fixo, pois este método também removerá a notificação de qualquer bandeja de notificações, impedindo que os usuários interajam com ela após ter sido exibida inicialmente. Um uso válido para esta API seria remover uma notificação que não é mais relevante (por exemplo, o usuário já leu a notificação na página da web no caso de um aplicativo de mensagens ou a próxima música já está tocando em um aplicativo de música).

3 curtidas

Pelo que li sobre este assunto durante o fim de semana, não podemos nem usar async/promises no manipulador de eventos push, devemos mostrá-lo o mais rápido possível ou a Apple revogará seus direitos de notificação.

Tudo isso está de acordo com o que @dfabulich diz.

Acho que estamos sendo muito “inteligentes” para o nosso próprio bem, devemos realmente remover todas as verificações e apenas mostrar a notificação.

6 curtidas

Não consigo agradecer o suficiente a @dfabulich por descobrir isso!

3 curtidas

OK, tenho um PR para isso.

@Falco / @featheredtoast devemos mesclá-lo?

Se precisarmos pular as notificações push, teremos que fazer isso no servidor, não no cliente. A lógica de colapso sempre foi um tanto questionável, os aplicativos não costumam fazer isso.

2 curtidas

Ah, sim, faz muito mais sentido enviar notificações apenas quando elas precisam ser exibidas. Deveria suprimir notificações excessivas em outros lugares eventualmente, mas estou satisfeito em mesclar isso aqui :+1:

Re: colapsar notificações, é uma pena perder, mas… idealmente, seríamos capazes de fazer algo semelhante à exclusão automática de outros aplicativos à medida que o usuário navega pelas notificações em qualquer dispositivo conectado, mas isso pode exigir um pouco de trabalho extra. A intenção é a mesma de nunca ter uma enxurrada de notificações antigas se acumulando. Novamente, tudo bem, mas precisamos revisitar isso mais tarde, dependendo de quanta dor isso causar.

2 curtidas