Push notifications em React Native: Firebase vs serviços nativos
1. Fundamentos das notificações push no ecossistema mobile
As notificações push são mensagens enviadas de um servidor para um aplicativo mobile, mesmo quando ele não está em execução. No ecossistema mobile, existem dois serviços principais de push: Apple Push Notification Service (APNs) para iOS e Firebase Cloud Messaging (FCM) para Android. O FCM também funciona como intermediário para APNs em dispositivos iOS.
A arquitetura básica envolve:
- Um provedor de serviços (servidor próprio ou Firebase) que envia a notificação
- Um serviço de push (APNs ou FCM) que recebe e encaminha a mensagem
- O dispositivo que exibe a notificação ao usuário
As notificações locais são geradas e exibidas pelo próprio aplicativo, sem depender de servidores externos. Já as notificações remotas dependem de infraestrutura externa para serem enviadas.
2. Firebase Cloud Messaging (FCM) no React Native
O Firebase oferece uma solução completa e integrada para push notifications. A configuração começa com a criação de um projeto no Firebase Console e a instalação das dependências necessárias.
Instalação das dependências:
npm install @react-native-firebase/app @react-native-firebase/messaging
Configuração básica no iOS (AppDelegate.m):
#import <Firebase.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[FIRApp configure];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
Solicitação de permissão e obtenção do token:
import messaging from '@react-native-firebase/messaging';
async function requestUserPermission() {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
const token = await messaging().getToken();
console.log('FCM Token:', token);
// Enviar token para o backend
}
}
Handlers para diferentes estados do app:
// Foreground
messaging().onMessage(async remoteMessage => {
Alert.alert('Nova notificação', remoteMessage.notification.body);
});
// Background
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Notificação em background:', remoteMessage);
});
// Quando o app é aberto pela notificação
messaging().onNotificationOpenedApp(remoteMessage => {
navigation.navigate('Detalhes', { data: remoteMessage.data });
});
// Quando o app é aberto pela notificação (app fechado)
messaging().getInitialNotification().then(remoteMessage => {
if (remoteMessage) {
navigation.navigate('Detalhes', { data: remoteMessage.data });
}
});
3. Serviços nativos: APNs e FCM sem Firebase
Para quem prefere controle total, é possível implementar push notifications usando apenas os serviços nativos, sem depender do Firebase SDK.
Configuração de APNs no iOS:
No Apple Developer Portal, é necessário:
1. Criar um Certificate Signing Request (CSR)
2. Gerar um certificado APNs no Apple Developer Console
3. Configurar o App ID com Push Notification capability
4. Instalar o certificado no servidor
Implementação direta de FCM no Android:
// AndroidManifest.xml
<service android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Serviço personalizado para receber notificações:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Tratar notificação recebida
if (remoteMessage.getNotification() != null) {
showNotification(remoteMessage.getNotification().getBody());
}
}
@Override
public void onNewToken(String token) {
// Token renovado - enviar para o backend
sendRegistrationToServer(token);
}
}
4. Comparação prática: Firebase vs serviços nativos
| Aspecto | Firebase | Serviços Nativos |
|---|---|---|
| Setup | Plug-and-play, 30 minutos | Horas de configuração |
| Manutenção | Automática (Firebase cuida) | Manual (certificados expiram) |
| Custo | Gratuito (limite de 1M requisições/dia) | Infraestrutura própria |
| Controle | Limitado ao que Firebase oferece | Total sobre o processo |
| Privacidade | Dados passam pelo Google | Controle total dos dados |
Casos de uso ideais:
- Firebase: Startups, MVPs, aplicativos que não lidam com dados sensíveis
- Nativo: Aplicativos financeiros, de saúde, ou com requisitos de compliance
5. Tratamento de notificações em diferentes estados do app
O React Native oferece métodos específicos para lidar com notificações em cada estado do aplicativo.
Notificações em foreground:
// Exibir banner customizado
messaging().onMessage(async remoteMessage => {
const { notification, data } = remoteMessage;
// Exibir notificação local como fallback
notifee.displayNotification({
title: notification.title,
body: notification.body,
android: {
channelId: 'default',
pressAction: { id: 'default' }
}
});
});
Notificações em background e killed state:
// Navegação condicional baseada nos dados da notificação
messaging().onNotificationOpenedApp(remoteMessage => {
const { type, id } = remoteMessage.data;
switch(type) {
case 'message':
navigation.navigate('Chat', { conversationId: id });
break;
case 'order':
navigation.navigate('OrderDetails', { orderId: id });
break;
default:
navigation.navigate('Home');
}
});
6. Sincronização de tokens e gerenciamento de inscrições
Gerenciar tokens de forma eficiente é crucial para o funcionamento das notificações.
Estrutura de dados no backend:
// Modelo de token
{
userId: "user_123",
tokens: [
{
token: "fcm_token_abc",
platform: "android",
deviceId: "device_456",
createdAt: "2024-01-15T10:30:00Z",
lastUsed: "2024-01-15T14:22:00Z"
}
],
topics: ["promocoes", "atualizacoes"]
}
Estratégia de limpeza de tokens inválidos:
// No servidor, ao receber erro de token inválido
async function handleInvalidToken(userId, invalidToken) {
await db.collection('users').updateOne(
{ _id: userId },
{ $pull: { 'tokens': { token: invalidToken } } }
);
}
// No cliente, quando token é renovado
messaging().onTokenRefresh(async newToken => {
await api.updateToken({
oldToken: currentToken,
newToken: newToken
});
setCurrentToken(newToken);
});
7. Segurança e boas práticas em push notifications
Criptografia de payloads:
// Servidor: criptografar dados sensíveis
const encryptedData = encrypt(JSON.stringify(sensitiveData), secretKey);
const message = {
token: userToken,
data: {
encrypted: encryptedData,
iv: initializationVector
},
notification: {
title: 'Notificação Segura',
body: 'Toque para ver detalhes'
}
};
Rate limiting e prevenção de spam:
// Middleware de rate limiting no servidor
const rateLimit = {
windowMs: 60000, // 1 minuto
max: 10, // máximo de notificações por minuto
message: 'Muitas notificações enviadas. Tente novamente mais tarde.'
};
// Verificação de consentimento
function canSendNotification(userId, notificationType) {
const userPreferences = getUserPreferences(userId);
return userPreferences.notificationTypes.includes(notificationType);
}
8. Exemplo prático: implementação híbrida com fallback
Serviço unificado de notificações:
class NotificationService {
async sendNotification(user, message) {
try {
// Tentar FCM primeiro
await this.sendViaFCM(user.fcmToken, message);
} catch (fcmError) {
console.warn('FCM falhou, tentando APNs:', fcmError);
try {
// Fallback para APNs
await this.sendViaAPNs(user.apnsToken, message);
} catch (apnsError) {
console.error('Ambos serviços falharam:', apnsError);
// Adicionar à fila de retry
await this.addToRetryQueue({
userId: user.id,
message: message,
attempts: 1,
maxAttempts: 3
});
}
}
}
async sendViaFCM(token, message) {
const response = await fetch('https://fcm.googleapis.com/fcm/send', {
method: 'POST',
headers: {
'Authorization': `key=${process.env.FCM_SERVER_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: token,
notification: {
title: message.title,
body: message.body
},
data: message.data
})
});
if (!response.ok) throw new Error('FCM request failed');
return response.json();
}
async sendViaAPNs(token, message) {
// Implementação específica para APNs
const apnsMessage = {
aps: {
alert: {
title: message.title,
body: message.body
},
sound: 'default',
badge: 1
},
data: message.data
};
// Enviar para APNs usando certificado ou token JWT
return await this.sendToAPNsServer(token, apnsMessage);
}
async processRetryQueue() {
const failedMessages = await this.getFailedMessages();
for (const failed of failedMessages) {
if (failed.attempts < failed.maxAttempts) {
await this.sendNotification(failed.userId, failed.message);
} else {
// Notificar administrador sobre falha persistente
await this.notifyAdmin(failed);
}
}
}
}
Testes em dispositivos reais:
// Simular cenários de teste
describe('NotificationService', () => {
it('deve fazer fallback para APNs quando FCM falha', async () => {
const service = new NotificationService();
const user = {
fcmToken: 'invalid_token',
apnsToken: 'valid_apns_token'
};
const result = await service.sendNotification(user, {
title: 'Teste',
body: 'Mensagem de teste'
});
expect(result.service).toBe('apns');
});
it('deve adicionar à fila de retry quando ambos falham', async () => {
const service = new NotificationService();
const user = {
fcmToken: 'invalid',
apnsToken: 'invalid'
};
await service.sendNotification(user, { title: 'Test', body: 'Test' });
const queue = await service.getRetryQueue();
expect(queue.length).toBe(1);
expect(queue[0].attempts).toBe(1);
});
});
Conclusão
A escolha entre Firebase e serviços nativos para push notifications em React Native depende das necessidades específicas do projeto. Firebase oferece simplicidade e rapidez de implementação, ideal para startups e MVPs. Serviços nativos proporcionam controle total e privacidade, sendo mais adequados para aplicações enterprise. Uma abordagem híbrida, utilizando FCM como padrão e APNs como fallback, pode oferecer o melhor dos dois mundos, garantindo resiliência e flexibilidade.
Referências
- React Native Firebase Messaging Documentation — Documentação oficial da biblioteca @react-native-firebase/messaging, com exemplos completos de configuração e uso
- Firebase Cloud Messaging HTTP Protocol — Guia oficial da Google para envio de mensagens via APIs REST do FCM
- Apple Push Notification Service Documentation — Documentação oficial da Apple sobre APNs, incluindo certificados e tokens
- React Native Push Notifications Complete Guide — Tutorial abrangente no Medium sobre implementação de push notifications em React Native
- Firebase vs Native Push Notifications: Which One to Choose? — Artigo comparativo detalhando prós e contras de cada abordagem
- Notifee Documentation — Documentação da biblioteca Notifee para notificações locais e customizadas em React Native
- FCM Token Management Best Practices — Guia oficial sobre gerenciamento de tokens FCM, incluindo renovação e limpeza