Práctica: Desarrollar un módulo personalizado para notificaciones push

Video
30 min~4 min lectura

Reproductor de video

Concepto clave

Los módulos personalizados en Expo son extensiones nativas que permiten acceder a funcionalidades del dispositivo que no están disponibles en las APIs estándar de React Native. Piensa en ellos como adaptadores especializados que conectan tu aplicación JavaScript con el hardware o servicios nativos del sistema operativo, similar a cómo un traductor profesional conecta dos idiomas en una negociación internacional.

Para las notificaciones push, un módulo personalizado te permite controlar aspectos avanzados como la gestión de tokens, el manejo de notificaciones en primer plano, o la integración con servicios de terceros. A diferencia del módulo estándar de Expo, un módulo personalizado te da control total sobre el flujo, permitiendo optimizaciones específicas para tu caso de uso, como priorizar ciertos tipos de notificaciones o sincronizar con tu backend de manera más eficiente.

Cómo funciona en la práctica

El desarrollo de un módulo personalizado sigue un flujo estructurado. Primero, defines la interfaz JavaScript que usarás en tu aplicación React Native. Luego, implementas la lógica nativa en Java (para Android) y Objective-C/Swift (para iOS). Finalmente, configuras el módulo en tu proyecto Expo usando el archivo expo-module.config.json.

Paso a paso: 1) Crea la estructura del módulo con expo init y configura el proyecto nativo. 2) Define los métodos que expondrás a JavaScript, como registerForPushNotificationsAsync o handleNotificationReceived. 3) Implementa el código nativo para cada plataforma, manejando permisos, tokens y eventos. 4) Prueba el módulo en un proyecto Expo usando expo-dev-client para desarrollo rápido.

Codigo en accion

Aquí tienes un ejemplo funcional de la interfaz JavaScript del módulo:

// CustomPushModule.js
export default {
  async registerForPushNotificationsAsync() {
    if (!ExpoModules.CustomPushModule) {
      throw new Error('Módulo no disponible');
    }
    return await ExpoModules.CustomPushModule.registerForPushNotificationsAsync();
  },
  
  async getPushToken() {
    return await ExpoModules.CustomPushModule.getPushToken();
  },
  
  addNotificationReceivedListener(listener) {
    return ExpoModules.CustomPushModule.addListener('onNotificationReceived', listener);
  }
};

Y la implementación nativa en Android (Java):

// CustomPushModule.java
package expo.modules.custompush;

import android.content.Context;
import com.google.firebase.messaging.FirebaseMessaging;
import org.unimodules.core.ExportedModule;
import org.unimodules.core.Promise;
import org.unimodules.core.interfaces.ExpoMethod;

public class CustomPushModule extends ExportedModule {
  private Context mContext;
  
  public CustomPushModule(Context context) {
    super(context);
    mContext = context;
  }
  
  @Override
  public String getName() {
    return "CustomPushModule";
  }
  
  @ExpoMethod
  public void registerForPushNotificationsAsync(Promise promise) {
    FirebaseMessaging.getInstance().getToken()
      .addOnCompleteListener(task -> {
        if (task.isSuccessful()) {
          promise.resolve(task.getResult());
        } else {
          promise.reject("TOKEN_ERROR", "Error al obtener token", task.getException());
        }
      });
  }
}

Errores comunes

  • No probar en ambas plataformas: Un módulo que funciona en Android puede fallar en iOS debido a diferencias en permisos o APIs. Solución: Usa expo run:android y expo run:ios para pruebas tempranas.
  • Manejo incorrecto de promesas en código nativo: No resolver o rechazar promesas puede causar memory leaks. Solución: Siempre llama a promise.resolve() o promise.reject() en cada camino de ejecución.
  • Olvidar configurar permisos en info.plist/AndroidManifest.xml: Las notificaciones push requieren permisos explícitos. Solución: Verifica que UIBackgroundModes (iOS) y uses-permission (Android) estén correctamente definidos.
  • No manejar versiones de Expo SDK: Los módulos pueden romperse con actualizaciones. Solución: Fija versiones en package.json y prueba con cada actualización mayor.

Checklist de dominio

  1. He creado un módulo personalizado que expone al menos 3 métodos JavaScript desde código nativo.
  2. He implementado la lógica para obtener un token de notificaciones push en Android e iOS.
  3. He configurado correctamente el archivo expo-module.config.json con las plataformas soportadas.
  4. He probado el módulo en un proyecto Expo usando expo-dev-client y verificado que las notificaciones llegan.
  5. He manejado errores comunes como permisos denegados o falta de conexión en el código nativo.
  6. He documentado el módulo con ejemplos de uso para otros desarrolladores.
  7. He optimizado el rendimiento evitando llamadas innecesarias a APIs nativas.

Implementa un módulo personalizado para notificaciones push con manejo de prioridades

En este ejercicio, crearás un módulo personalizado que no solo maneje notificaciones push, sino que también permita asignar prioridades (alta, media, baja) y sincronice el estado con un backend simulado.

  1. Configura el proyecto: Crea un nuevo módulo Expo usando expo init --template expo-module y nómbralo custom-push-priority.
  2. Define la interfaz JavaScript: En src/CustomPushPriorityModule.ts, exporta métodos para: registerForPushNotificationsAsync(), setNotificationPriority(notificationId, priority), y syncWithBackend().
  3. Implementa el código nativo: Para Android (Java), usa FirebaseMessaging para obtener el token y añade lógica para almacenar prioridades en SharedPreferences. Para iOS (Swift), usa UserNotifications y UserDefaults.
  4. Configura el módulo: Añade las plataformas soportadas en expo-module.config.json y define los permisos necesarios.
  5. Prueba la integración: En un proyecto Expo separado, instala el módulo localmente con npm install ../ruta/al/modulo y usa expo-dev-client para enviar notificaciones de prueba con diferentes prioridades.
  6. Documenta y refactoriza: Crea un README.md con ejemplos y optimiza el código para evitar llamadas redundantes a las APIs nativas.
Pistas
  • Usa expo-modules-core para manejar la comunicación entre JavaScript y nativo de manera eficiente.
  • Para simular el backend, puedes usar una función que devuelva un Promise con datos estáticos, sin necesidad de un servidor real.
  • Considera usar AsyncStorage en el lado JavaScript para cachear las prioridades y reducir llamadas nativas.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.