Enviar notificaciones push a través de enlaces profundos en React Native
Configura enlaces universales y enlaces de aplicación, integra Pusher Beams en una app React Native, y asocia URLs web con pantallas nativas para que al tocar una notificación se abra el lugar correcto.
Una notificación push solo es útil si al tocarla el usuario llega a la pantalla de la que habla. Imagine una aplicación React Native para estudiantes internacionales que eligen universidades: los correos electrónicos quedan sin leer y los SMS son poco fiables entre países, por lo que la notificación push debe funcionar cuando un reclutador escribe. Aquí configurará el enlace profundo en ambas plataformas, adjuntará una URL de sitio web a cada notificación de Pusher Beams y traducirá esa URL a una ruta de React Navigation, probando cada capa en el proceso.
Por qué los enlaces profundos indican la destinación de la notificación
En el momento en que se creó esta configuración, los paquetes de Pusher utilizados aquí no podían reenviar la carga útil personalizada de una notificación al lado JavaScript de una aplicación React Native en Android. La solución más sencilla, similar a una solicitud de integración al Android SDK, consiste en recurrir a los enlaces profundos: la notificación de Android contiene un enlace a un sitio web normal, el toque se trata como un clic en dicho enlace, y el mecanismo de manejo de enlaces profundos existente en la aplicación decide qué pantalla abrir.
Esto podría haber cambiado desde entonces, así que primero revise los SDKs actuales de Beams. La capa de enrutamiento que se muestra a continuación es útil en cualquier caso, ya que también sirve para enlaces provenientes de correos electrónicos, SMS o chats.
Un resumen rápido de los enlaces profundos
El enlace profundo permite que la aplicación reclame enlaces a su propio sitio web y los abra de forma nativa. Un enlace como https://test.com/message/abc debería abrir directamente la aplicación en el mensaje abc en lugar de iniciar un navegador. iOS los denomina enlaces universales, Android los llama enlaces de aplicación, y ambos requieren que su dominio publique un archivo que demuestre que confía en la aplicación.
Configurar el enlace profundo en ambas plataformas
Publicar los archivos de verificación en .well-known
Cree un directorio /.well-known/ en la raíz de su sitio web. Para Android, agregue /.well-known/assetlinks.json, el cual declara que su aplicación puede manejar todos los enlaces del dominio. Sustituya los marcadores de posición por el nombre del paquete de su aplicación y la huella SHA-256 del certificado que lo firma:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "package_name",
"sha256_cert_fingerprints":
["package_cert_fingerprint"]
}
}]
Si Google Play vuelve a firmar sus versiones, utilice la huella digital de la clave de firma de Play y no la de su clave de subida, de lo contrario la verificación fallará únicamente en entornos de producción.
Para iOS, agregue un archivo llamado /.well-known/apple-app-site-association (sin extensión). Este archivo enumera el identificador de su aplicación, que combina el ID del equipo y el ID del paquete, así como las rutas URL que la aplicación debe interceptar:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "appId",
"paths": [ "/paths-you-want-to-support", "/messenger"]
}
]
}
}
Proporcione ambos archivos a través de HTTPS desde el dominio exacto, sin redirecciones. Este es el formato clásico de applinks; Apple ha añadido desde entonces un formato más reciente, por lo que consulte su documentación actual.
Habilitar dominios asociados en iOS
iOS también necesita un derecho de acceso que le indique qué dominios pertenecen a la aplicación. En Xcode, abra Capabilities, active Associated Domains y agregue cada dominio al que desee conectarse, generalmente en el formato applinks:yourdomain.com.
Declare filtros de intención en Android
En Android, los enlaces profundos se declaran con filtros de intento en AndroidManifest.xml. Para capturar los enlaces /messenger/*, la actividad principal necesita un filtro de intento para la acción VIEW con las categorías DEFAULT y BROWSABLE, además de un elemento data que especifique el esquema https, su servidor y un prefijo de ruta de /messenger. Al agregar android:autoVerify="true", se indica al sistema que verifique assetlinks.json para que la aplicación pueda abrir estos enlaces sin mostrar un diálogo de selección.
Escuchar enlaces en el componente raíz
El componente raíz de la aplicación debe realizar dos cosas: leer la URL que inició la app desde un inicio en frío, y escuchar las URLs que llegan mientras ya está en ejecución. El módulo Linking de React Native proporciona ambas funcionalidades, a través de getInitialURL() y un receptor de eventos url. Ambos deben llamar a un mismo manejador, que por ahora puede limitarse a registrar la URL:
const handleDeepLink = (url: string) => console.log(url);
Pruebe los enlaces antes de agregar notificaciones
Verifique el enlace profundo por sí solo antes de añadir notificaciones push encima de él. Un método sencillo es enviar un enlace a usted mismo a través de una aplicación de mensajería como Slack en cada dispositivo de prueba y hacer clic en él. Si la configuración es correcta, el enlace abrirá su aplicación en lugar del navegador, y la URL registrada coincidirá con la que hizo clic. Al aislar esta capa, cualquier notificación mal dirigida posteriormente solo podrá ser un problema de tipo push.
Agregar Pusher Beams a la aplicación
Requisitos previos y el puente comunitario
Necesita una cuenta de Pusher Beams, y debe completar los pasos de configuración para el servicio de notificaciones push de Apple y para Google Firebase.
Tenga en cuenta que cuando se escribió esta integración, Pusher no ofrecía un paquete oficial para React Native. El puente utilizado aquí es el paquete comunitario react-native-pusher-push-notifications, que conecta los SDKs nativos de Beams con JavaScript y no cuenta con el soporte de Pusher. Fue necesario realizar pequeños ajustes para que funcione en esta configuración. En Android, el cambio clave fue una versión modificada del SDK push-notifications-android de Pusher que convierte un toque en la notificación en una apertura de enlace profundo. Un puente no oficial junto con un SDK modificado implica un compromiso de mantenimiento: es necesario fijar las versiones y reconsiderar la elección cuando cambien los SDKs oficiales.
Instalar el SDK de Swift en iOS
El lado de iOS depende del SDK Swift de Pusher, que se instala aquí con Carthage. Añada la siguiente línea a /ios/Cartfile y, a continuación, ejecute carthage bootstrap para descargarlo e instalarlo:
github "pusher/push-notifications-swift" ~> 1.3.0
Esa versión era la actual para esta integración; los proyectos más recientes probablemente utilizarán un SDK más reciente y, posiblemente, otro gestor de dependencias.
Luego siga los pasos de instalación manual del paquete de puente para ambas plataformas, ya que las partes nativas están personalizadas.
Envío de una notificación de prueba
Las notificaciones en iOS solo llegan a dispositivos físicos, no a simuladores, así que tenga un iPhone real a mano.
Para enviar un push de prueba, utilice la Consola de Depuración en el panel de control de Pusher o un cliente HTTP como Postman que invoque la API de publicación de Beams. Basta con un cuerpo de solicitud que contenga una sección para iOS y otra para Android; en esta configuración, la parte de iOS establece un conteo de notificaciones de 5, mientras que la parte de Android incluye la URL del sitio web que debe abrirse.
Cuando todo esté conectado correctamente, iOS procesará el contenido de la notificación por sí mismo, mientras que Android abrirá la aplicación mediante una intención View que contenga la URL de su sitio web. En ambas plataformas, su manejador debería registrar algo como https://yourdomain/messenger/abcde. En iOS, el ícono de la aplicación también debería mostrar un distintivo con el número 5.
Convierte URLs en rutas de React Navigation
El paso final es reemplazar el stub de registro por un enrutamiento real. Una configuración común utiliza React Router en el sitio web y React Navigation en la aplicación React Native. Una pequeña utilidad que mapea las rutas web a las rutas nativas permite que ambos compartan lógica y evita que una URL web renombrada dañe silenciosamente la navegación de la aplicación.
Compartir constantes de rutas entre web y nativo
Ambos conjuntos de código definen sus rutas como constantes. En el sitio web, un generador de rutas devuelve ya sea una ruta concreta o el patrón contra el cual React Router realiza la coincidencia; en el entorno nativo, lo equivalente es un nombre de pantalla, con el ID de la conversación pasado por separado como parámetro de navegación:
// Web:
ROUTE = {
MESSENGER_CONVERSATION: (conversationId?: string) =>
conversationId
? `/messenger/${conversationId}`
: "/messenger/:conversationId"
}// Native:
APP_STACK_ROUTE: {
MESSENGER_CONVERSATION_SCREEN:
"app_stack_routes/messenger_conversation"
}
// native then has a params object with conversationId included
Al expresar ambos lados como constantes, se puede declarar en una sola tabla qué ruta web corresponde a qué pantalla nativa:
const ROUTE_MATCHES: IRouteMatches = [
{
webPath: ROUTE.MESSENGER_CONVERSATION(),
rnPath: APP_STACK_ROUTES.MESSENGER_CONVERSATION_SCREEN
}
];
Al llamar a ROUTE.MESSENGER_CONVERSATION() sin argumentos se devuelve el patrón /messenger/:conversationId; por lo tanto, la tabla almacena el mismo patrón que utiliza el enrutador del sitio web. Al renombrar una ruta web, la asignación se actualiza automáticamente. Para repasar cómo funcionan esos patrones web, consulte Conceptos básicos de React Router.
Atenuación de URLs entrantes
A veces el manejador se dispara más de una vez por toque, por ejemplo cuando tanto la verificación de la URL inicial como el listener indican la existencia de un enlace, lo que genera pantallas duplicadas. Al pasar cada URL a través de un Subject de RxJS y aplicar debounceTime(100), los disparos consecutivos se reducen a una sola llamada, que luego se envía a processUrl:
export const handleDeepLink = (url: string): void => {
if (!url) return;
onChangeUrl$.next(url);
};
const onChangeUrl$: Subject<string> = new Subject<string>();
const urlSubscription: Observable<string> = onChangeUrl$.pipe(debounceTime(100));
urlSubscription.subscribe(processUrl);
El compromiso: dos enlaces diferentes dentro de 100 ms se reducen al último, lo cual es aceptable para toques en notificaciones.
Dividir la URL en partes
processUrl primero divide la URL en protocolo, host, ruta y cadena de consulta utilizando una expresión regular (una herramienta como RegExr ayuda a ajustarla). El desestructurado omite el resultado completo y el grupo de consulta que aún contiene ?:
const REGEX_DECONSTRUCT_URL = /^(.*?):\/\/(.*?)(\/.*?)(\?(.*))?$/;const deconstructedUrl = REGEX_DECONSTRUCT_URL.exec(url);
if (!deconstructedUrl) return;
const [originalUrl, protocol, tld, path, ignore, querystring] = deconstructedUrl;
Tenga en cuenta que este patrón requiere una ruta: un https://yourdomain sin barra final no coincidirá y la función devolverá un resultado anticipadamente. Esto está bien para enlaces de notificaciones, pero es útil saberlo si reutiliza esta función en otro lugar.
Comparar la ruta con la tabla de rutas
Una vez extraídos los fragmentos, el procesador solo procede con enlaces HTTPS en su dominio y luego recorre ROUTE_MATCHES hasta que una entrada acepte esa ruta:
if (protocol === "https" && tld.includes("yourdomain")) {
for (let i = 0; i < ROUTE_MATCHES.length; i++) {
if (matchPath(ROUTE_MATCHES[i], path, querystring)) {
// loop until one matches
break;
}
}
}
La comprobación tld.includes("yourdomain") es práctica pero poco estricta: también aceptaría un host como yourdomain.attacker.example. El sistema operativo verifica los enlaces universales y de aplicación, por lo que el riesgo es limitado; sin embargo, tener una lista blanca de hosts exactos representa una mejora sencilla cuando otros orígenes URL comparten el mismo procesador.
Dentro de matchPath, el patrón webPath se compara con la ruta recibida mediante path-to-regexp, la misma biblioteca en la que se basa React Router. Cuando hay coincidencia, los parámetros extraídos, como conversationId, se convierten en los parámetros de la pantalla correspondiente rnPath, y la aplicación navega hasta allí. Dado que esto se ejecuta fuera de cualquier componente, un pequeño servicio de navegación que contiene la referencia del navegador raíz lo activa, tal como describen los documentos de React Navigation para navegar sin el atributo navigation.
Conclusión
Con estos elementos en su lugar, la aplicación maneja dos tareas a través de una única ruta de código:
- Los enlaces a su sitio web, ya provengan de correo electrónico, SMS o chat, abren la pantalla correspondiente en la aplicación nativa.
Lo que mantiene esto mantenible es tratar la URL del sitio web como la única descripción de un destino, con una tabla que convierte las URLs en rutas nativas. Pruebe cada capa por separado, refuerce la verificación del host y observe los SDK oficiales de Beams: si envían datos a JavaScript, puede descartar la versión modificada y conservar la capa de enrutamiento.
Lecturas relacionadas
- Planificación de la actualización a Expo SDK 58: iOS 27, React Native 0.88 y nuevas herramientas — Un recorrido práctico por la versión beta de Expo SDK 58: qué cambios hay en iOS 27 y React Native 0.88, qué funciones son experimentales y cómo probar la actualización de forma segura.
- Construyendo un componente Select configurable para React Native Paper — Una guía paso a paso para diseñar y hacer de react-native-paper-select un proyecto de código abierto, que aborda la búsqueda, los chips de selección múltiple, las listas seccionadas y los compromisos en cuanto al rendimiento.
- Conectando un módulo Turbo dentro de la aplicación de extremo a extremo con React Native Codegen — Definir una especificación tipada, ejecutar codegen e implementar un módulo Turbo en iOS y Android utilizando métodos síncronos, de Promise, de callback y de emisor de eventos.
- Notificaciones en React Native: Permisos, canales y el ciclo de vida de FCM — Aprenda cómo los permisos, los canales de Android, los tokens de FCM y los manejadores para estados en primer plano, segundo plano y de cierre se integran en un sistema de notificaciones de React Native con Notifee.