# SDK React Native

## Para qué sirve

Mide sesiones, pantallas, errores de JavaScript y nativos, crashes, pedidos de red y eventos de negocio de tu app React Native. No reimplementa nada: es una capa fina sobre los SDK nativos de Lymen ([Android](https://lymen.app/docs/instalacion/sdk-android/) e [iOS](https://lymen.app/docs/instalacion/sdk-ios/)). El SDK nativo maneja la sesión, la cola en disco, el consentimiento, la privacidad, los crashes, el inicio de la app y la red; JavaScript suma lo que el nativo no ve: errores de JS, promesas rechazadas y pantallas de `react-navigation`.

Con **una sola alta** y **una sola clave** recibís de Android y de iOS. En el detalle de la aplicación filtrás por sistema (Todas, Android, iOS).

> **Qué se probó.** La versión 0.2.1 compila en Android y se probó en un emulador contra una ingesta de prueba. En iOS se compila en integración continua, pero no se probó en un teléfono.

## Requisitos

| Requisito | Detalle |
|---|---|
| React Native | 0.76 o posterior (arquitectura nueva o vieja) |
| JavaScript | Hermes (por defecto), necesario para capturar promesas rechazadas |
| Android | `minSdk` 24, `compileSdk` 37 y **Kotlin 2.3 o posterior** en la app |
| iOS | iOS 13.4 |
| Expo | Solo con *development build* (`npx expo prebuild` o EAS Build). **No funciona en Expo Go**. |

## Instalación

Primero dá de alta la app en [Aplicaciones](https://lymen.app/docs/instalacion/aplicaciones/) con tipo **React Native** (podés cargar el paquete Android y el *bundle id* de iOS).

```sh
npm install lymen-sdk-react-native@0.2.1
cd ios && pod install
```

### Android

En `android/build.gradle` de la app, la versión de compilación y la de Kotlin que exige el SDK:

```groovy
buildscript {
  ext {
    compileSdkVersion = 37
    minSdkVersion = 24
    kotlinVersion = "2.4.21"
  }
  dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") }
}
```

El paquete le agrega el seguimiento de pedidos al cliente de red de React Native. Si `minSdkVersion` es menor que 26, la app necesita *desugaring* (ver [SDK Android](https://lymen.app/docs/instalacion/sdk-android/)).

### iOS

El `pod install` suma el SDK nativo de iOS como paquete de Swift (React Native 0.75 o más nuevo).

### Expo

Con un *development build* no hace falta un plugin propio. Para Android, configurá `compileSdkVersion: 37` y `minSdkVersion: 24` con `expo-build-properties`.

## Iniciar

Lo antes posible, en `index.js`, antes de `AppRegistry.registerComponent`:

```js
import lymen from 'lymen-sdk-react-native';

lymen.iniciar({
  sitio: 'ly_pub_TU_CLAVE',                 // la misma para Android e iOS
  endpoint: 'https://<tu-cuenta>-ingesta.lymen.app',
  version: '1.4.0',
  propagarA: ['api.tu-empresa.com'],
});
```

### Iniciar también desde el código nativo (opcional)

Iniciar desde JavaScript es lo más simple, pero JavaScript arranca cuando la app ya abrió: el **inicio de la app en frío** y un crash en los primeros segundos no se miden. Para medirlos, iniciá también el SDK nativo al abrir la app (`MainApplication.onCreate()` en Android, `AppDelegate` en iOS). Después, el `iniciar` de JS se ignora en el nativo (gana el primero) y solo engancha lo de JS. En Android, usá `vistasAutomaticas = false`: las pantallas las marca JS.

## Opciones

| Opción | Para qué sirve | Por defecto | Valores válidos | Impacto de cambiarlo |
|---|---|---|---|---|
| `sitio` | Clave pública de la app. | — (obligatoria) | `ly_pub_…` | Es la misma para Android y iOS. |
| `endpoint` | Punto de entrada de Lymen. | El del SDK nativo (no es el de tu cuenta) | `https://<tu-cuenta>-ingesta.lymen.app` | **Indicalo siempre.** |
| `servicio` | Nombre del servicio. | El paquete de la app | Texto | El servidor lo reemplaza por el del alta. |
| `version` | Versión de la app. | La del paquete | Texto | Para comparar versiones. |
| `entorno` | Entorno. | `produccion` | Texto | Separa producción de pruebas. |
| `propagarA` | Destinos que reciben `traceparent` (une el pedido del celular con la traza del servidor). | Vacía | `api.x.com`, `api.x.com:8443`, `*.x.com` o `https://api.x.com` | Se suma a lo que baje del portal. |
| `muestreo` | Porción de sesiones que se mandan. | `1` | De 0 a 1 | **Costo.** Si el portal define uno, manda el del portal. |
| `consentimiento` | Valor inicial. | `concedido` (o lo último que eligió el usuario) | `pendiente`, `concedido`, `denegado` | **Legal y privacidad.** `pendiente` junta sin mandar; `denegado` no manda nada. |
| `capturarClics` | Registra los toques. | `false` | `true` / `false` | **Privacidad.** Nunca el texto. |
| `capturarErrores` | Errores de JavaScript no atrapados. | `true` | `true` / `false` | Apagado, no verás los errores de JS. |
| `capturarPromesas` | Promesas rechazadas sin `catch` (Hermes). | `true` | `true` / `false` | Apagado, perdés esos errores. |
| `depuracion` | Mensajes del SDK en consola y en el log nativo. | `false` | `true` / `false` | Solo desarrollo. |

## API

```js
lymen.usuario('paciente-123');   // se manda el hash SHA-256; null lo borra
lymen.evento('turno.reservado', { especialidad: 'clinica', monto: 15000 });
lymen.error(err, { 'pantalla.accion': 'pagar' });   // error manejado
lymen.vista('Pago');             // pantalla manual
lymen.consentimiento('denegado');
lymen.log('warn', 'reintento del pago');   // apagado salvo que el portal lo prenda
lymen.enviarAhora();
await lymen.sesionActual();      // id de sesión o null
```

Los eventos de negocio se llaman `entidad.accion` (minúsculas, números y `_`). Mandalos **desde la app o desde el backend, nunca desde los dos**. Ninguna función lanza excepciones. `probarCierreNativo()` existe solo para comprobar la instalación: **cierra la app** con un error nativo, no la uses en producción.

### Pantallas con react-navigation

```jsx
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import { useVistasLymen } from 'lymen-sdk-react-native';

export default function App() {
  const navigationRef = useNavigationContainerRef();
  useVistasLymen(navigationRef);   // cada cambio de pantalla es una vista
  return <NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>;
}
```

El nombre de la vista es el nombre de la ruta, **nunca sus parámetros** (pueden tener datos personales). Para cambiarlo: `useVistasLymen(navigationRef, { nombrar: (ruta) => ruta.name })`.

### Errores de JavaScript

`iniciar` engancha el manejador global de errores **encadenándolo** al que había: la pantalla roja en desarrollo y el cierre de la app ante un error fatal siguen igual. Cada error trae el tipo, el mensaje, el stack (hasta 8 KB) y una huella para agruparlos. Tope: **100 errores por ejecución y 10 por huella**. Un error fatal en la versión de producción cierra la app: llega el error de JS y, en el próximo arranque, el crash nativo.

### Red

El `fetch` y el `XMLHttpRequest` de React Native viajan por la red nativa, así que **la red la mide el SDK nativo** y cada pedido tiene un solo span. Solo si el nativo no mide la red (por ejemplo, en Android si la app arma su propio cliente HTTP) se usa `instrumentarFetch()`, que es seguro de llamar siempre: si el nativo ya mide, no hace nada.

## Privacidad

Las reglas de Privacidad bajan del portal y las aplica el SDK nativo antes de mandar; el servidor las vuelve a aplicar. JavaScript no captura textos ingresados ni parámetros de navegación, y `user.id` sale siempre hasheado.

## Problemas comunes

| Síntoma | Qué hacer |
|---|---|
| Error de compilación de Kotlin al armar Android | Subí Kotlin a 2.3 o posterior en `android/build.gradle`. |
| No funciona en Expo Go | Usá un *development build*. |
| No se mide el inicio en frío | Iniciá también desde el código nativo. |
| Los errores de promesas no llegan | Necesitás Hermes y `capturarPromesas` encendido. |
| Veo todo mezclado | En el detalle de la aplicación filtrá por sistema: Todas, Android o iOS. |
