React Native y Expo
@unolink/react-native es un puente ligero sobre los SDK nativos de iOS y Android, que vienen dentro del paquete. No necesita
ningún config plugin, ningún permiso aparte de internet ni entradas en Info.plist. Plan Pro y superiores.
Requisitos
- React Native 0.79+ (nueva arquitectura).
- Expo: una build de desarrollo,
npx expo prebuildo EAS Build. No funciona en Expo Go, porque tiene código nativo. - Una clave del SDK: en el panel, abre tu app → Ajustes → Claves del SDK → Nueva clave. Solo se muestra una vez, así que cópiala en ese momento.
Instalación
Expo:
npx expo install @unolink/react-native
npx expo prebuild # or: eas build
React Native sin Expo:
npm install @unolink/react-native
cd ios && pod install
Después vuelve a compilar la app. Recargar el JavaScript no basta después de añadir código nativo.
Si pod install falla con un error de codificación (Ruby 4, o una configuración regional que no es UTF-8), ejecútalo en UTF-8:
cd ios && LANG=en_US.UTF-8 pod install
No instales con npm install --legacy-peer-deps: reescribe package-lock.json y quita las dependencias peer opcionales.
Configuración
Llama a configure una vez, en el nivel superior, antes de que se renderice tu primera pantalla: en app/_layout.tsx con Expo Router, o
en index.js.
import { Unolink } from "@unolink/react-native"
Unolink.configure("ulk_…", { debug: __DEV__ })
Con 0.3.1 o posterior:
- Las llamadas hechas antes de
configureesperan, y se envían justo después, así que no pasa nada si restauras antes una sesión. - Una clave que falta (por ejemplo, una entrada que falta en
.env) se avisa en la consola en lugar de fallar en silencio.Unolink.isConfigured()indica si está funcionando. debuges__DEV__por defecto.
Opciones
| Opción | Por defecto | Qué hace |
|---|---|---|
debug | __DEV__ (0.3.1+; false antes) | Marca esta instalación como dispositivo de prueba. Los paneles ocultan los dispositivos de prueba salvo que Incluir dispositivos de prueba esté activado. |
logLevel | "warn" | Cuánto se escribe en la consola de Xcode o en Logcat (etiqueta Unolink): "none", "error", "warn", "info", "debug". |
baseUrl | https://api.unolink.dev | La API del SDK. Déjala como está. |
La clave es pública: va dentro de tu app y solo puede enviar datos de esa app. Consulta Claves del SDK.
Eventos del embudo
Añade una línea en cada momento importante. Se convierten en los pasos de Embudo y Recorridos.
Unolink.onboardingCompleted() // the last onboarding screen is done
Unolink.paywallViewed("onboarding") // a paywall appeared; placement says which one
Unolink.paywallSkipped("onboarding") // closed without buying
Unolink.signUp("apple") // a new account was created
Unolink.signIn("apple") // signed in to an account they already had
Unolink.signUpSkipped() // went on as a guest (0.3.1+)
Pantallas de onboarding, una llamada por pantalla cuando aparece:
import { useOnboardingStep } from "@unolink/react-native"
useOnboardingStep("goal", { index: 0 }) // in each onboarding screen component
Unolink.onboardingSkipped("goal") // on Skip
Los IDs de pantalla usan letras, dígitos, _ y . (how_heard, no how-heard). En desarrollo, el StrictMode de React monta
las pantallas dos veces, así que puede que ahí veas una pantalla enviada dos veces; las builds de release la envían una vez. Las builds de desarrollo avisan en la
consola de los IDs o propiedades que el servidor rechazaría (0.3.1+).
Sin RevenueCat, informa tú de las pruebas y compras. Pasa el importe como cadena para que nunca se redondee:
Unolink.trialStarted("pro_yearly")
Unolink.purchase("49.99", "USD", "pro_yearly")
Unolink.purchaseCancelled("pro_yearly") // closed the purchase sheet
Unolink.purchaseFailed("pro_yearly", "payment_failed")
Tus propios eventos aceptan un nombre y hasta 20 propiedades (cadenas, números o booleanos):
Unolink.track("level_up", { level: 3 })
La lista completa, con los nombres que llegan al panel, está en Eventos.
ID de usuario y RevenueCat
Si usas RevenueCat, conéctalo en tu app → Ajustes → RevenueCat. Después, no llames a
purchase ni a trialStarted: RevenueCat envía él mismo las pruebas, compras, renovaciones, cancelaciones y reembolsos, y
Unolink ignora las llamadas de compra de la app para que nada cuente dos veces.
Para unir esas compras a la instalación, informa del propio ID de usuario de app de RevenueCat. Con el SDK 0.3.2 o
posterior, una línea lo mantiene al día. Llámala una vez al iniciar, antes o después de configure:
import Purchases from "react-native-purchases"
Unolink.connectRevenueCat(Purchases)
Unolink lee el ID en ese momento, en cada actualización de la información del cliente (compras, logIn, logOut) y cada
vez que la app vuelve al primer plano, y solo lo envía cuando ha cambiado. Si RevenueCat aún no está configurado, la
lectura falla sin hacer ruido y se vuelve a intentar más tarde. Devuelve una función que desconecta. Por eso, Unolink.revenueCatUserChanged() normalmente no hace falta en React Native.
Sin ella (o antes de la 0.3.2), llama a setUserId con el ID de RevenueCat después de Purchases.configure, después de
cada Purchases.logIn y Purchases.logOut, y en cada inicio. Usa uno u otro, no los dos:
// after Purchases.configure, logIn and logOut, and on every launch
Unolink.setUserId(await Purchases.getAppUserID())
Para un invitado, eso es el ID anónimo de RevenueCat, así que una compra hecha antes de registrarse se une a la instalación
de inmediato. Después de logIn, es el ID de la cuenta. Cuenta cada ID que haya informado una instalación, así que la compra
del invitado se mantiene unida. Consulta Invitados y cuentas.
Unolink nunca guarda el ID en sí, solo un hash unidireccional distinto para cada una de tus apps.
Deep links diferidos
En Android, cuando alguien instala desde un enlace de Unolink que tiene un deep link, el enlace llega una vez, en la primera apertura. En iPhone no hay ninguno: Apple no tiene install referrer, y Unolink nunca hace fingerprinting.
link.deepLink es lo que configuras en el enlace: una ruta como /promo/3for2, o una URL completa.
Las primeras aperturas suelen empezar en el onboarding. Si la pantalla del enlace está detrás del onboarding o del inicio de sesión, ábrelo cuando eso termine y luego márcalo como consumido (0.3.1+):
// when onboarding (or sign-in) is done, and on every launch after that until it's consumed:
const link = Unolink.getPendingDeferredDeepLink()
if (link?.deepLink) {
router.push(link.deepLink) // a path; open a full URL with Linking.openURL
Unolink.consumeDeferredDeepLink()
}
Desde 0.3.3, el enlace espera hasta que lo consumas, aunque la app se cierre entretanto (hasta 30 días después de la
instalación). Un usuario nuevo que abandona a mitad del onboarding lo recibe igualmente en el siguiente inicio, así que no
necesitas guardarlo tú (nada de AsyncStorage). Llama a consumeDeferredDeepLink() en cuanto lo hayas abierto: hasta
entonces, getPendingDeferredDeepLink() lo devuelve en cada inicio. Es null antes de configure y hasta que Google Play
responde. Con 0.3.1 y 0.3.2 solo se conserva durante el arranque actual.
Si se puede llegar a la pantalla directamente, basta con un listener. Los listeners se llaman una vez por instalación. Con
0.3.1, un enlace que llegó antes de que te suscribieras va al primer listener que añadas; con 0.3.0, suscríbete justo
después de configure.
import { router } from "expo-router"
const subscription = Unolink.addDeferredDeepLinkListener((link) => {
// link.deepLink: "/promo/3for2", link.params: { offer: "3for2" }
// link.linkSlug: "tiktok" or null, link.clickId: "clk_01JA…"
if (link.deepLink) router.push(link.deepLink)
Unolink.consumeDeferredDeepLink()
})
// when the component unmounts
subscription.remove()
Configura el Enlace profundo y los Parámetros del enlace en el panel (Enlaces → ⋯ → Editar). Cómo funciona está en Deep links diferidos en Android.
Otras llamadas
| Llamada | Qué hace |
|---|---|
Unolink.getInstallId() | El ID aleatorio de esta instalación, o null antes de configure. No identifica a ninguna persona ni dispositivo. |
Unolink.flush() | Envía ya los eventos en cola. Normalmente no hace falta. |
Unolink.setEnabled(false) | Desactiva la medición y elimina los eventos en cola, para tu ajuste de exclusión voluntaria. Se recuerda entre arranques. |
Las llamadas nunca lanzan excepciones ni bloquean: los eventos se ponen en cola en el dispositivo y el SDK nativo los envía en segundo plano.
Jest
Desde 0.3.1 el paquete tiene su propio mock, que registra cada llamada:
jest.mock("@unolink/react-native", () => require("@unolink/react-native/jest"))
// expect(Unolink.signUp).toHaveBeenCalledWith("apple")
Dispositivos de prueba
Las builds de desarrollo son dispositivos de prueba (pasa debug: __DEV__ antes de 0.3.1). Los tracks de prueba de Google Play no se pueden
reconocer en el teléfono: pasa debug: true en esas builds, por ejemplo desde una variable EXPO_PUBLIC_. Sus datos son reales, pero están ocultos hasta que activas Incluir
dispositivos de prueba en Embudo y Recorridos. Más en Pruebas.
Privacidad
- Sin IDFA, sin Google Advertising ID, sin aviso de App Tracking Transparency, sin fingerprinting.
- Un ID de instalación aleatorio, versión de la app, versión del sistema operativo, versión del SDK, teléfono o tablet, idioma. El país viene del borde de la red. Las direcciones IP nunca se guardan.
- En iOS, el manifiesto de privacidad del SDK nativo viene con el paquete: analíticas, no vinculadas a la identidad.
- Nunca pongas nombres, emails ni otros datos personales en las propiedades de los eventos.
Solución de problemas
- ¿"Native module not found" o algo parecido? Estás en Expo Go, o no has vuelto a compilar desde la instalación. Haz una build de desarrollo. Desde 0.3.1 la app no se cierra sin el módulo nativo: las llamadas no hacen nada y un aviso explica por qué.
- ¿No aparece nada en el panel? Activa Incluir dispositivos de prueba: las builds con
__DEV__están ocultas por defecto. - ¿Sigue sin aparecer nada? Configura con
{ debug: __DEV__, logLevel: "debug" }y buscaUnolinken la consola de Xcode o en Logcat. Una clave incorrecta o revocada se registra como error.
Más en Solución de problemas.