Programação Por Equipe DevBras

Capa do artigo: Notificação local ou push: quando o aviso do app precisa vir do servidor

Notificação local ou push: quando o aviso do app precisa vir do servidor

Saiba quando uma notificação local resolve, quando só push resolve e como implementar o lembrete local no .NET MAUI com permissão pedida na hora certa, reagendamento e app funcionando sem permissão.

O app da clínica ganhou notificações. O lembrete "sua consulta é daqui a 1 hora" funciona perfeitamente, até com o celular no modo avião. Então a recepção remarca a consulta da dona Lúcia para outro dia, e ela aparece na clínica no horário antigo, guiada pelo lembrete que o próprio app disparou. O aviso "sua consulta foi remarcada" nunca chegou, porque ninguém tinha implementado o caminho do servidor até o celular.

A resposta curta: notificação local só sabe o que o aparelho já sabia quando ela foi agendada. Ela é ótima para lembrar de algo que o app conhece (uma consulta confirmada, um remédio, um treino). Quando a informação nasce no servidor e precisa chegar com o app fechado, o caminho é push, via Firebase Cloud Messaging no Android e APNs no iOS, com infraestrutura e custo próprios. Misturar os dois conceitos é o que cria a expectativa errada.

O que cada tipo de notificação faz por baixo

Uma notificação local é um pedido que o app faz ao sistema operacional: "às 13h de quinta, mostre este texto". No Android, isso costuma ser implementado com o agendador de alarmes do sistema e o gerenciador de notificações, como mostra a documentação do .NET MAUI; no iOS, com o centro de notificações do usuário. Na hora marcada, o sistema mostra o aviso mesmo que o app não esteja aberto. Mas o conteúdo foi decidido no momento do agendamento. Se a consulta mudou depois, o aviso agendado não sabe.

Uma notificação push faz o caminho inverso. O seu servidor manda uma mensagem para o serviço da plataforma (FCM ou APNs), endereçada a um token que identifica aquela instalação do app. O serviço entrega ao aparelho, e o sistema exibe. Para isso você precisa registrar o token de cada instalação no seu backend, guardar credenciais da plataforma no servidor, lidar com tokens que expiram e decidir quem recebe o quê. É outro projeto, não uma opção a mais na mesma API.

Como decidir entre os dois

  • O app já tem a informação e o horário é conhecido? Local. Lembrete de consulta confirmada, alarme de medicação, meta diária.
  • A informação nasce fora do aparelho? Push. Remarcação, mensagem do médico, resultado de exame pronto.
  • Só importa com o app aberto? Nenhum dos dois: atualize a tela em tempo real, como em notificação em tempo real com SignalR.
  • O aviso é importante para o negócio? Além da notificação, registre o fato no sistema. Notificação pode ser silenciada ou perdida, como explica o aviso é extra, o registro é que fica.

O que a plataforma exige de uma notificação local

No Android, desde a versão 8, toda notificação precisa de um canal, que a pessoa pode silenciar separadamente nas configurações. A partir do Android 13, mostrar notificação exige a permissão POST_NOTIFICATIONS, pedida em tempo de execução, como câmera ou localização. Alarmes agendados não sobrevivem a uma reinicialização do aparelho, e o modo de economia de energia pode atrasar alarmes que não são exatos. Alarmes exatos, em versões recentes, dependem de uma permissão especial que o sistema ou a pessoa podem negar. No iOS, a permissão também é pedida explicitamente, e há um limite de notificações locais pendentes por app.

Conclusão prática: trate o lembrete local como "melhor esforço com tolerância de alguns minutos" e nunca como o único lugar onde a pessoa fica sabendo de algo.

Como implementar o lembrete local no MAUI

No app da clínica, o lembrete é agendado quando a pessoa confirma a consulta e reconstruído sempre que o app abre, a partir da agenda que veio do servidor.

  1. Declare a permissão no manifesto do Android e crie o canal de notificação na inicialização.
  2. Esconda a plataforma atrás de uma interface, com uma implementação por sistema (o exemplo da documentação do MAUI para Android é um bom ponto de partida).
  3. Peça a permissão no momento do motivo: quando a pessoa toca em "Confirmar consulta" e o app explica que pode avisar uma hora antes.
  4. Se a permissão for negada, o app continua funcionando e mostra a consulta na agenda, com um aviso discreto.
  5. Use o id da consulta como id da notificação, para poder cancelar e substituir.
  6. Ao abrir o app, cancele e reagende todos os lembretes a partir da agenda atualizada. Isso cobre remarcações vistas no servidor e alarmes perdidos na reinicialização.

Permissão no manifesto

<!-- Platforms/Android/AndroidManifest.xml -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

A interface e o uso no ViewModel

public interface ILembreteService
{
    Task<bool> PedirPermissaoAsync();
    void Agendar(int id, string titulo, string mensagem, DateTime quando);
    void Cancelar(int id);
}

public partial class AgendaViewModel(IAgendaApi api, ILembreteService lembretes)
{
    public string? Aviso { get; private set; }

    public async Task ConfirmarConsultaAsync(Consulta consulta)
    {
        await api.ConfirmarAsync(consulta.Id);

        var quando = consulta.Inicio.AddHours(-1);
        if (quando <= DateTime.Now) return;

        if (!await lembretes.PedirPermissaoAsync())
        {
            Aviso = "Notificações desativadas. A consulta continua na aba Agenda.";
            return;
        }

        lembretes.Agendar(consulta.Id, "Consulta em 1 hora",
            consulta.Profissional + " - " + consulta.Local, quando);
    }

    public async Task SincronizarLembretesAsync()
    {
        var consultas = await api.ListarConfirmadasAsync();
        foreach (var c in consultas)
        {
            lembretes.Cancelar(c.Id);
            var quando = c.Inicio.AddHours(-1);
            if (quando > DateTime.Now)
                lembretes.Agendar(c.Id, "Consulta em 1 hora", c.Profissional + " - " + c.Local, quando);
        }
    }
}

Pedindo a permissão no Android

public async Task<bool> PedirPermissaoAsync()
{
    var status = await Permissions.CheckStatusAsync<Permissions.PostNotifications>();
    if (status != PermissionStatus.Granted)
        status = await Permissions.RequestAsync<Permissions.PostNotifications>();
    return status == PermissionStatus.Granted;
}

O pedido acontece depois que a pessoa entendeu o benefício. Se você pedir na primeira tela, antes de qualquer contexto, a chance de recusa é alta, e uma recusa no Android pode impedir que o sistema mostre o pedido de novo; daí em diante, só pelas configurações do aparelho. Note também que o SincronizarLembretesAsync é o que mantém o lembrete honesto: a remarcação da dona Lúcia teria cancelado o lembrete antigo na próxima vez que ela abrisse o app. Mas se ela não abrisse, só um push resolveria.

Quando chega a hora do push

Se a remarcação precisa chegar com o app fechado, o desenho passa a ter: registro do token do aparelho no seu backend, associado ao paciente; envio pelo servidor via FCM e APNs (diretamente ou por um serviço intermediário, como os hubs de notificação dos provedores de nuvem); remoção de tokens inválidos; e a mesma regra de permissão do lado do aparelho. O conteúdo do push deve ser mínimo: "Sua consulta foi alterada. Abra o app para ver." Dado de saúde no texto da notificação aparece na tela bloqueada para quem estiver perto do celular.

Erros comuns

  • Vender notificação local como "o app avisa quando algo mudar". A pessoa confia no aviso e perde a informação que nasceu no servidor.
  • Pedir permissão na primeira tela. A recusa vem antes do motivo e, no Android, pode ser definitiva até a pessoa mexer nas configurações.
  • Agendar e nunca reagendar. Uma reinicialização apaga os alarmes, e remarcações deixam lembretes com horário errado.
  • Agendar dezenas de lembretes de uma vez. Você esbarra no limite da plataforma e irrita a pessoa, que desliga tudo.
  • App que trava ou fica inútil sem permissão. Notificação é conveniência; a agenda precisa funcionar sem ela.
  • Dado sensível no texto do aviso. Nome do exame ou diagnóstico aparece na tela bloqueada.

Checklist

  • Cada aviso do app está classificado como local (o aparelho já sabe) ou push (nasce no servidor).
  • O manifesto declara POST_NOTIFICATIONS e o app cria o canal na inicialização.
  • A permissão é pedida no momento em que o benefício fica claro.
  • Com a permissão negada, o app continua funcionando e diz onde a informação está.
  • Lembretes usam ids estáveis e são cancelados e reagendados quando o app abre.
  • O texto da notificação não expõe dado sensível na tela bloqueada.
  • Você testou com o aparelho reiniciado e com a permissão negada.

Pegue a lista de avisos que o seu app promete na loja de aplicativos e marque cada um: esse aviso existiria se o servidor nunca mais falasse com o aparelho depois do agendamento?

Avalie este artigo

Referência: https://learn.microsoft.com/pt-br/dotnet/maui/platform-integration/local-notifications?view=net-maui-8.0&pivots=devices-android