SDK de React Native - Soporte de enlaces profundos

Documento

Añadir soporte para enlaces profundos

Losenlaces profundos dirigen a los usuarios a contenidos específicos dentro de su aplicación. Cuando los usuarios tocan un enlace profundo en un dispositivo con su aplicación instalada, la aplicación se abre directamente al contenido deseado, como una página de producto o una experiencia específica.

Los enlaces de seguimiento singulares admiten tanto enlaces profundos estándar (para aplicaciones instaladas) como enlaces profundos diferidos (para nuevas instalaciones). Para más información, consulte las Preguntas frecuentes sobre deep linking y las Preguntas frecuentes sobre enlaces singulares.

Vías de implementación: Esta guía cubre las implementaciones Bare React Native y Expo. La distinción existe porque Expo abstrae la gestión del código nativo para simplificar el desarrollo de React Native, mientras que bare React Native proporciona acceso directo a las carpetas nativas de iOS y Android. Elige la ruta de implementación adecuada para la estructura de tu proyecto.


Requisitos

Requisitos previos

Completa los requisitos previos de Singular Links para habilitar la vinculación profunda para tu aplicación.

Notas:

  • Este artículo asume que su organización está utilizando Singular Links - la tecnología de enlaces de seguimiento de Singular. Los clientes más antiguos pueden estar utilizando enlaces de seguimiento heredados.
  • Los destinos de enlaces profundos de su aplicación deben configurarse en la página Aplicaciones de Singular (consulte Configuración de su aplicación para el seguimiento de atribución).

Implementación de Singular Links Handler

El controlador de SingularLink proporciona un mecanismo de devolución de llamada para recuperar los parámetros de enlace profundo, enlace profundo diferido, passthrough y URL de los enlaces de seguimiento de Singular cuando se abre la aplicación.

Parámetros disponibles:

  • Enlace profundo (_dl): La URL de destino dentro de su aplicación para los usuarios que hagan clic en el enlace.
  • Enlace profundo diferido (_ddl): La URL de destino para los usuarios que instalan la aplicación después de hacer clic en el enlace
  • Passthrough (_p): Datos personalizados pasados a través del enlace de seguimiento para un contexto adicional
  • urlParameters: Objeto de parámetros de consulta de un Enlace Singular que abrió la app. No aplicable a escenarios de Deferred Deep Link.

Implementación desnuda de React Native

Configure la vinculación profunda para proyectos React Native desnudos con acceso directo al código nativo de iOS y Android.

Configuración de la plataforma iOS

Actualización de iOS AppDelegate

Habilite Singular SDK para procesar los datos relacionados con el lanzamiento y gestionar los enlaces profundos pasando los objetos launchOptions y userActivity a Singular SDK en su archivo AppDelegate.

AppDelegate.swiftAppDelegate.mm
import Singular

// Deferred Deep link and Short Link support
func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
  
  if let singularBridge = NSClassFromString("SingularBridge") {
    let selector = NSSelectorFromString("startSessionWithLaunchOptions:")
    if singularBridge.responds(to: selector) {
      singularBridge.perform(selector, with: launchOptions, afterDelay: 1)
    }
  }
  
  factory.startReactNative(
    withModuleName: "AppName", // Update with your App Name
    in: window,
    launchOptions: launchOptions
  )

  return true
}

// Universal Links (NSUserActivity)
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
  
  if let url = userActivity.webpageURL {
    print("Singular: Universal link received:", url.absoluteString)
    if let singularBridge = NSClassFromString("SingularBridge") {
      let selector = NSSelectorFromString("startSessionWithUserActivity:")
      if singularBridge.responds(to: selector) {
        singularBridge.perform(selector, with: userActivity, afterDelay: 1)
      }
    }
  }
    
  return RCTLinkingManager.application(
      application,
      continue: userActivity,
      restorationHandler: restorationHandler
    )
}

Estos métodos garantizan que el SDK de Singular reciba información crítica sobre cómo y por qué se lanzó su aplicación, que Singular utiliza para el seguimiento de la atribución y la navegación de enlaces profundos.


Configuración de la plataforma Android

Actualizar Android MainActivity

Habilite Singular SDK para procesar los datos relacionados con el lanzamiento y gestionar los enlaces profundos modificando el archivo MainActivity para pasar el objeto Intent a Singular SDK.

MainActivity.ktMainActivity.java
// Add as part of the imports at the top of the class
import android.content.Intent
import net.singular.react_native.SingularBridgeModule

// Add to the MainActivity class
override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    if (intent.data != null) {
        setIntent(intent)
        SingularBridgeModule.onNewIntent(intent)
    }
}

El objeto Intent contiene información sobre cómo y por qué se lanzó su aplicación, que Singular utiliza para el seguimiento de atribución y la navegación de enlaces profundos.


Configuración del SDK

Añadir callback withSingularLink

Configure la devolución de llamada withSingularLink para gestionar los datos de enlaces profundos entrantes y diferidos durante la inicialización del SDK.

New ArchitectureOld Architecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import { NativeEventEmitter } from 'react-native';

function App() {
  useEffect(() => {
    // Create config
    const config: SingularConfig = {
      apikey: 'YOUR_SDK_KEY',
      secret: 'YOUR_SDK_SECRET'
      // Note: Deep link handlers are NOT set in config for TurboModule
    };

    NativeSingular.init(config);

    // Set up deep link handler using event emitter
    const emitter = new NativeEventEmitter(NativeSingular);
    const linkListener = emitter.addListener('SingularLinkHandler', (params) => {
      console.log('Singular Link resolved:', params);

      // Extract deep link parameters
      console.log('Singular: Deep link received:', params.deeplink);
      console.log('Singular: Passthrough data:', params.passthrough);
      console.log('Singular: Is deferred:', params.isDeferred);
      console.log('Singular: urlParameters:', params.urlParameters);

      // Handle deep link navigation
    });

    // Clean up event listener on unmount
    return () => {
      linkListener.remove();
    };
  }, []);

  return (
    // Your app components
    null
  );
}

Nota: La devolución de llamada withSingularLink sólo se activa cuando la aplicación se abre a través de un Singular Link. Para obtener más información, consulte las preguntas frecuentes sobre Singular Links.

Para obtener la documentación completa del método, consulte la referencia withSingularLink.


Implementación de Expo

Configure la vinculación profunda para proyectos Expo utilizando archivos de configuración y plugins personalizados para gestionar automáticamente las modificaciones del código nativo.

Configuración de la aplicación

Actualizar App.json para Deep Linking

Debido a la abstracción de Expo de la gestión del código nativo, añada configuraciones de vinculación profunda específicas de la plataforma en el archivo app.json.

Configuración de iOS

  • Añada la capacidad associatedDomains con su dominio de enlace de seguimiento Singular
  • Añada el esquema para la compatibilidad con esquemas de URL personalizados (fallback)

Configuración de Android

  • Actualice el valor de host para que coincida con su dominio de enlaces Singular (por ejemplo, example.sng.link)
  • Configure el esquema en el filtro de última intención para el soporte de enlace de esquema tradicional
app.json
{
  "expo": {
    "name": "MySimpleApp",
    "slug": "mysimpleapp",
    "scheme": "ios-app-scheme",
    "ios": {
      "associatedDomains": [
        "applinks:example.sng.link"
      ]
    },
    "android": {
      "intentFilters": [
        {
          "action": "VIEW",
          "autoVerify": true,
          "data": [
            {
              "scheme": "http",
              "host": "example.sng.link",
              "pathPrefix": "/A"
            },
            {
              "scheme": "https",
              "host": "example.sng.link",
              "pathPrefix": "/A"
            },
            {
              "scheme": "http",
              "host": "example.sng.link",
              "pathPrefix": "/B"
            },
            {
              "scheme": "https",
              "host": "example.sng.link",
              "pathPrefix": "/B"
            },
            {
              "scheme": "http",
              "host": "example.sng.link",
              "pathPrefix": "/E"
            },
            {
              "scheme": "https",
              "host": "example.sng.link",
              "pathPrefix": "/E"
            },
            {
              "scheme": "http",
              "host": "example.sng.link",
              "pathPrefix": "/F"
            },
            {
              "scheme": "https",
              "host": "example.sng.link",
              "pathPrefix": "/F"
            }
          ],
          "category": [
            "BROWSABLE",
            "DEFAULT"
          ]
        },
        {
          "action": "VIEW",
          "data": [
            {
              "scheme": "android-app-scheme"
            }
          ],
          "category": [
            "BROWSABLE",
            "DEFAULT"
          ]
        }
      ]
    }
  }
}

Plugins de Expo personalizados

Crear plugins específicos de la plataforma

Estos archivos de plugin automatizan las modificaciones de código nativo necesarias para la integración de Singular SDK en el flujo de trabajo gestionado de Expo, eliminando la necesidad de edición manual después de cada expo prebuild.

  • Plugin iOS: Detecta Swift o Objective-C AppDelegate e inyecta declaraciones de importación, registro de identificador de dispositivo y código de gestión de enlace universal que reenvía los datos de atribución al puente Singular React Native.
  • Plugin de Android: Detecta los archivos MainActivity de Java o Kotlin y añade importaciones, registro de intenciones y el método onNewIntentque captura los enlaces profundos y los pasa al módulo puente de Singular.

Ambos plugins garantizan que cuando los usuarios tocan enlaces profundos o enlaces universales, el código nativo captura y reenvía correctamente estos datos a sus ganchos React Native para el seguimiento de atribución.

Pasos de configuración

  1. Navega hasta el directorio raíz de tu proyecto
  2. Cree una carpeta de plugins
  3. Añada los siguientes archivos de plugin JavaScript

Implementación del plugin iOS

Cree plugins/singular-ios-deeplink-plugin.js para configurar automáticamente la vinculación profunda de iOS.

singular-ios-deeplink-plugin.js
// plugins/singular-ios-deeplink-plugin.js
const { withAppDelegate } = require('@expo/config-plugins');

const withUniversalSingular = (config) => {
  return withAppDelegate(config, (config) => {
    const { modResults } = config;

    // Detect file type
    const isSwift = modResults.contents.includes('import Expo') || 
                    modResults.contents.includes('class AppDelegate');
    const isObjectiveC = modResults.contents.includes('#import') || 
                         modResults.contents.includes('@implementation');

    console.log(`Detected AppDelegate type: ${isSwift ? 'Swift' : isObjectiveC ? 'Objective-C' : 'Unknown'}`);

    if (isSwift) {
      modResults.contents = applySwiftModifications(modResults.contents);
    } else if (isObjectiveC) {
      modResults.contents = applyObjectiveCModifications(modResults.contents);
    } else {
      console.warn('Unable to detect AppDelegate type - skipping Singular modifications');
    }

    return config;
  });
};

function applySwiftModifications(contents) {
  // Add Singular import
  if (!contents.includes('import Singular')) {
    contents = contents.replace(
      /import ReactAppDependencyProvider/,
      `import ReactAppDependencyProvider
import Singular`
    );
  }

  // Add IDFV logging
  const swiftLaunchPattern = /didFinishLaunchingWithOptions.*?\) -> Bool \{/s;
  if (!contents.includes('UIDevice.current.identifierForVendor')) {
    contents = contents.replace(
      swiftLaunchPattern,
      `didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    print("IDFV", UIDevice.current.identifierForVendor!.uuidString)`
    );
  }

  // Add universal link handling
  if (!contents.includes('Universal link received for Singular')) {
    contents = contents.replace(
      /continue userActivity: NSUserActivity,\s*restorationHandler: @escaping \(\[UIUserActivityRestoring\]\?\) -> Void\s*\) -> Bool \{/,
      `continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
  ) -> Bool {
    if let url = userActivity.webpageURL {
      print("Universal link received for Singular:", url.absoluteString)

      if let singularBridge = NSClassFromString("SingularBridge") {
        let selector = NSSelectorFromString("startSessionWithUserActivity:")
        if singularBridge.responds(to: selector) {
          singularBridge.perform(selector, with: userActivity, afterDelay: 1)
        }
      }
    }`
    );
  }

  return contents;
}

function applyObjectiveCModifications(contents) {
  // Add Singular import
  if (!contents.includes('#import <Singular-React-Native/SingularBridge.h>')) {
    contents = contents.replace(
      /#import "AppDelegate.h"/,
      `#import "AppDelegate.h"
#import <Singular-React-Native/SingularBridge.h>`
    );
  }

  // Add IDFV logging to didFinishLaunchingWithOptions
  const objcLaunchPattern = /- \(BOOL\)application:\(UIApplication \*\)application didFinishLaunchingWithOptions:\(NSDictionary \*\)launchOptions\s*{/;
  if (!contents.includes('NSLog(@"IDFV %@"')) {
    contents = contents.replace(
      objcLaunchPattern,
      `- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSLog(@"IDFV %@", [UIDevice currentDevice].identifierForVendor.UUIDString);
  [SingularBridge startSessionWithLaunchOptions:launchOptions];`
    );
  }

  // Add continueUserActivity method if not present
  if (!contents.includes('continueUserActivity')) {
    const methodToAdd = `
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
    restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
    NSLog(@"Universal link received for Singular: %@", userActivity.webpageURL.absoluteString);
    [SingularBridge startSessionWithUserActivity:userActivity];
    return YES;
}`;

    contents = contents.replace(
      /@end$/,
      `${methodToAdd}
@end`
    );
  }

  return contents;
}

module.exports = withUniversalSingular;

Implementación del plugin Android

Cree plugins/singular-android-deeplink-plugin.js para configurar automáticamente la vinculación profunda de Android.

singular-android-deeplink-plugin.js
// plugins/singular-android-deeplink-plugin.js
const { withMainActivity } = require('@expo/config-plugins');

const withSingularAndroid = (config) => {
  // Add MainActivity modifications
  config = withMainActivity(config, (config) => {
    const { modResults } = config;

    // Detect Java vs Kotlin
    const isKotlin = modResults.contents.includes('class MainActivity') && 
                     modResults.contents.includes('override fun');
    const isJava = modResults.contents.includes('public class MainActivity') && 
                   modResults.contents.includes('@Override');

    if (isKotlin) {
      modResults.contents = applyKotlinModifications(modResults.contents);
    } else if (isJava) {
      modResults.contents = applyJavaModifications(modResults.contents);
    }

    return config;
  });

  return config;
};

function applyKotlinModifications(contents) {
  // Add imports
  if (!contents.includes('android.content.Intent')) {
    contents = contents.replace(
      /package com\..*?\n/,
      `$&
import android.content.Intent
import net.singular.react_native.SingularBridgeModule`
    );
  }

  // Add onNewIntent method
  if (!contents.includes('onNewIntent')) {
    const methodToAdd = `
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        if (intent.data != null) {
            setIntent(intent)
            SingularBridgeModule.onNewIntent(intent)
        }
    }`;

    contents = contents.replace(
      /}\s*$/,
      `${methodToAdd}
}`
    );
  }

  return contents;
}

function applyJavaModifications(contents) {
  // Add imports
  if (!contents.includes('android.content.Intent')) {
    contents = contents.replace(
      /package com\..*?;/,
      `$&
import android.content.Intent;
import net.singular.react_native.SingularBridgeModule;`
    );
  }

  // Add onNewIntent method
  if (!contents.includes('onNewIntent')) {
    const methodToAdd = `
    @Override
    public void onNewIntent(Intent intent) {
        if(intent.getData() != null) {
            setIntent(intent);
            super.onNewIntent(intent);
            SingularBridgeModule.onNewIntent(intent);
        }
    }`;

    contents = contents.replace(
      /}\s*$/,
      `${methodToAdd}
}`
    );
  }

  return contents;
}

module.exports = withSingularAndroid;

Registro de plugins

Actualice su archivo app.json para incluir los nuevos plugins personalizados junto con el plugin Singular React Native.

app.json
{
  "expo": {
    "plugins": [
      "singular-react-native",
      "./plugins/singular-ios-deeplink-plugin.js",
      "./plugins/singular-android-deeplink-plugin.js"
    ]
  }
}

Configuración SDK para Expo

Configure la devolución de llamada withSingularLink en su aplicación Expo utilizando el mismo enfoque que en React Native. Consulte la sección Configuración del SDK anterior para obtener todos los detalles de implementación.


Comportamiento del controlador

Comprender la resolución de enlaces

El manejador withSingularLink se comporta de manera diferente dependiendo de si la aplicación está recién instalada o ya instalada.

Nueva instalación (enlace profundo diferido)

En una instalación nueva, no existe ninguna URL abierta cuando se inicia la aplicación. Singular completa la atribución para determinar si el enlace de seguimiento contenía un valor de enlace profundo o de enlace profundo diferido.

Flujode enlace profundo diferido:

  1. El usuario hace clic en un enlace de seguimiento de Singular configurado con un valor de enlace profundo.
  2. El usuario instala y abre la aplicación por primera vez
  3. Singular SDK envía la primera sesión a los servidores de Singular
  4. La atribución se completa e identifica el enlace profundo desde el enlace de seguimiento
  5. El valor del enlace profundo vuelve al manejador withSingularLink en el parámetro deeplink con isDeferred = true

Pruebade enlaces profundos diferidos:

  1. Desinstala la app del dispositivo de prueba (si está instalada actualmente)
  2. iOS: Restablece tu IDFA. Android:Restablezca su ID de publicidad de Google (GAID)
  3. Haz clic en el enlace de seguimiento Singular desde el dispositivo (asegúrate de que está configurado con un valor de enlace profundo)
  4. Instale y abra la aplicación

La atribución debería completarse correctamente, y el valor de enlace profundo diferido se pasará al gestor de withSingularLink.

Consejo profesional: Cuando pruebe enlaces profundos con una compilación de desarrollo que utilice un nombre de paquete diferente (por ejemplo, com.example.deven lugar de com.example.prod), configure el enlace de seguimiento específicamente para el nombre de paquete de la aplicación de desarrollo. Después de hacer clic en el enlace de prueba, instala la compilación de desarrollo directamente en el dispositivo (a través de Android Studio o Xcode) en lugar de descargar la aplicación de producción de la tienda de aplicaciones.


Ya instalada (enlace profundo inmediato)

Cuando la aplicación ya está instalada, al hacer clic en un enlace singular se abre la aplicación inmediatamente mediante la tecnología Universal Links (iOS) o Android App Links.

Flujo de enlace profundo inmediato:

  1. El usuario hace clic en un enlace de seguimiento Singular
  2. El sistema operativo proporciona una URL abierta que contiene el enlace de seguimiento Singular completo
  3. Durante la inicialización del SDK, Singular analiza la URL
  4. Singular extrae los valores deeplink y passthrough
  5. Los valores vuelven a través del manejador withSingularLink con isDeferred = false

Funciones avanzadas

Parámetros Passthrough

Capture datos adicionales del clic en el enlace de seguimiento utilizando parámetros passthrough.

Si se incluye un parámetro passthrough (_p) en el enlace de seguimiento, el parámetro passthroughdel controlador withSingularLink contiene los datos correspondientes. Utilícelo para capturar metadatos de campaña, datos de segmentación de usuarios o cualquier información personalizada que necesite en la aplicación.

Ejemplo de valor Passthrough:

{
  "utm_source": "Facebook",
  "utm_campaign": "Web-to-App US RON",
  "utm_creative": "Blue CTA for Promo",
  "promo_code": "45k435hg"
}

Ejemplo de enlace de seguimiento con valor passthrough codificado en URL en el parámetro (_p):

https://yourapp.sng.link/A1b2c/abc123?_dl=myapp://product/123&_p=%7B%22utm_source%22%3A%20%22Facebook%22%2C%22utm_campaign%22%3A%20%22Web-to-App%20US%20RON%22%2C%22utm_creative%22%3A%20%22Blue%20CTA%20for%20Promo%22%2C%22promo_code%22%3A%20%2245k435hg%22%7D

El manejador withSingularLink recibirá:

urlParameters = {"utm_source": "Facebook","utm_campaign": "Web-to-App US RON","utm_creative": "Blue CTA for Promo","promo_code": "45k435hg"}

Reenviar todos los parámetros de consulta

Capture todos los parámetros de consulta de la URL del enlace de seguimiento añadiendo el parámetro _forward_params=2 a su enlace de seguimiento.

Cuando se añade _forward_params=2 al enlace de seguimiento, todos los parámetros de consulta se incluyen en el parámetro deeplink del manejador withSingularLink, dándole acceso a la URL completa con todos sus parámetros.

Ejemplo de enlace de seguimiento:

https://yourapp.sng.link/A1b2c/abc123?_dl=myapp://product/123&_forward_params=2&utm_source=facebook&promo=SALE2024

El manejador withSingularLink recibirá:

deeplink = "myapp://product/123?utm_source=facebook&promo=SALE2024"