Entrada

Integrando notificaciones push y tokens FCM en iOS: una guía práctica desde la experiencia real

Tras múltiples iteraciones, Enolisa logra integrar correctamente el flujo de notificaciones push en iOS con Firebase Cloud Messaging y APNs, consolidando un modelo estable y multiplataforma para mensajería y engagement.

Integrando notificaciones push y tokens FCM en iOS: una guía práctica desde la experiencia real

Enolisa iOS: integración completa de notificaciones push con Firebase Cloud Messaging y APNs

Contexto y punto de partida

La integración de notificaciones push en iOS ha sido uno de los procesos más complejos dentro del desarrollo de Enolisa.
A diferencia de Android, donde Firebase Cloud Messaging (FCM) gestiona todo de forma directa, en iOS se requiere una conexión intermedia con el Apple Push Notification Service (APNs).
Este paso implica permisos del sistema, claves de autenticación y configuración nativa, y si se ejecuta fuera de orden, la app no llega a registrar el token.


Estructura técnica

El flujo final implementado en Enolisa se basa en una arquitectura clara y escalonada:

Etapa Componente Descripción
1️⃣ Apple Developer Activación de Push Notifications y creación de claves APNs (.p8) para Sandbox y Production.
2️⃣ Firebase Console Subida de las dos claves .p8, asociadas al mismo Bundle ID, para entornos Development y Production.
3️⃣ Xcode Activación de las capabilities Push Notifications y Background Modes → Remote notifications.
4️⃣ Flutter Inicialización correcta de flutter_local_notifications (solo para notificaciones locales) y firebase_messaging (para push).
5️⃣ Firestore Persistencia de tokens FCM por usuario, permitiendo trazabilidad completa y refresco dinámico.

Configuración en Apple Developer y Firebase

Claves APNs (.p8)

  • Se generaron dos claves independientes:
    • Una para Sandbox (Development).
    • Otra para Production.
  • Ambas se subieron a Firebase Console → Cloud Messaging, vinculadas al mismo Team ID y Bundle ID.

Configuración en Xcode

  • Target Runner → pestaña Signing & Capabilities:
    • Se añadieron Push Notifications y Background Modes → Remote notifications.
  • En Info.plist:
    1
    2
    3
    4
    5
    6
    
    <key>FirebaseAppDelegateProxyEnabled</key>
    <true/>
    <key>UIBackgroundModes</key>
    <array>
      <string>remote-notification</string>
    </array>
    

Código y flujo en Flutter

Inicialización de notificaciones locales

Se mantiene la inicialización mínima de flutter_local_notifications:

1
2
3
4
5
6
7
8
9
10
11
12
final DarwinInitializationSettings iosInit = const DarwinInitializationSettings(
  requestAlertPermission: false,
  requestBadgePermission: false,
  requestSoundPermission: false,
);

final InitializationSettings initSettings = InitializationSettings(
  android: androidInit,
  iOS: iosInit,
);

await flutterLocalNotificationsPlugin.initialize(initSettings);

Esto permite mostrar notificaciones locales en Enolisa sin interferir con el flujo de permisos del sistema.


Solicitud de permisos push

El punto crítico fue entender que el prompt oficial de iOS solo aparece cuando se llama a:

1
2
3
4
5
await FirebaseMessaging.instance.requestPermission(
  alert: true,
  badge: true,
  sound: true,
);

Este método se conecta al framework nativo UserNotifications, creando automáticamente la entrada “Notificaciones” en los ajustes del sistema.

Para Android, se mantiene la integración con permission_handler:

1
2
3
if (Platform.isAndroid) {
  final status = await Permission.notification.request();
}

Centralización de permisos y tokens

Toda la gestión se unificó en FcmTokenManager (fcm_sync.dart):

1
2
3
4
5
6
7
8
9
10
11
12
13
static Future<void> solicitarPermisosNotificaciones() async {
  if (Platform.isIOS) {
    final settings = await FirebaseMessaging.instance.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );
    EnolisaErrorHandler.log("🔐 iOS push authStatus: ${settings.authorizationStatus}");
  } else if (Platform.isAndroid) {
    final status = await Permission.notification.request();
    EnolisaErrorHandler.log("🔐 (Android) Notif status: $status");
  }
}

El mismo módulo gestiona también el registro del token FCM y su almacenamiento en Firestore:

1
2
final token = await FirebaseMessaging.instance.getToken();
await _db.collection('diarios').doc(user.uid).set({'token': token});

Resultados

  • ✅ El prompt de notificaciones aparece correctamente en iOS tras instalar la app.
  • ✅ La sección Notificaciones figura en los ajustes del sistema.
  • ✅ Se generan y sincronizan los tokens FCM con Firestore.
  • ✅ Envíos de prueba desde Firebase Console llegan correctamente a los dispositivos reales.

Lecciones aprendidas

  1. El permiso real en iOS no lo gestiona permission_handler, sino FirebaseMessaging.requestPermission().
  2. Sin esa llamada, iOS nunca registra la app en APNs y no crea el menú de Notificaciones.
  3. flutter_local_notifications sirve únicamente para mostrar notificaciones locales; no pide permisos.
  4. Es recomendable esperar unos milisegundos antes de pedir getToken() hasta que APNs devuelva su token.
  5. Tener dos claves APNs (Sandbox + Production) evita problemas entre entornos.

Conclusión

El proceso fue largo, pero dejó una base sólida y documentada. Enolisa ya dispone de un flujo push completo, estable y multiplataforma, que permite comunicación proactiva y engagement sin depender de hacks ni permisos inconsistentes.

Cada token, cada notificación, y cada flujo ahora están completamente trazados, con un sistema capaz de crecer en complejidad sin perder control operativo.

Esta entrada está licenciada bajo CC BY 4.0 por el autor.