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.
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
)
}
// Top of the AppDelegate.mm
#import <React/RCTLinkingManager.h>
#import <Singular-React-Native/SingularBridge.h>
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add inside didFinishLaunchingWithOptions
[SingularBridge startSessionWithLaunchOptions:launchOptions];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
[SingularBridge startSessionWithUserActivity:userActivity];
return [RCTLinkingManager application:application continueUserActivity: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.
// 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)
}
}
// 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
public void onNewIntent(Intent intent) {
if(intent.getData() != null) {
setIntent(intent);
super.onNewIntent(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.
// 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
);
}
import React, { useEffect } from 'react';
import { Singular, SingularConfig } from 'singular-react-native';
function App() {
useEffect(() => {
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withSingularLink((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
});
// Initialize SDK
Singular.init(config);
}, []);
return (
// Your app components
);
}
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
{
"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
- Navega hasta el directorio raíz de tu proyecto
- Cree una carpeta de plugins
- 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.
// 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.
// 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.
{
"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:
- El usuario hace clic en un enlace de seguimiento de Singular configurado con un valor de enlace profundo.
- El usuario instala y abre la aplicación por primera vez
- Singular SDK envía la primera sesión a los servidores de Singular
- La atribución se completa e identifica el enlace profundo desde el enlace de seguimiento
-
El valor del enlace profundo vuelve al manejador
withSingularLinken el parámetrodeeplinkconisDeferred = true
Pruebade enlaces profundos diferidos:
- Desinstala la app del dispositivo de prueba (si está instalada actualmente)
- iOS: Restablece tu IDFA. Android:Restablezca su ID de publicidad de Google (GAID)
- Haz clic en el enlace de seguimiento Singular desde el dispositivo (asegúrate de que está configurado con un valor de enlace profundo)
- 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:
- El usuario hace clic en un enlace de seguimiento Singular
- El sistema operativo proporciona una URL abierta que contiene el enlace de seguimiento Singular completo
- Durante la inicialización del SDK, Singular analiza la URL
-
Singular extrae los valores
deeplinkypassthrough -
Los valores vuelven a través del manejador
withSingularLinkconisDeferred = 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"