Saltar al contenido

Lección 10 de 20 · Lectura y práctica

Quiz: Conocimientos sobre integración nativa

Tipo de lección: QuizConcepto claveLa integración nativa en Expo permite acceder a funcionalidades del dispositivo que no están disponibles en JavaScript puro, como sensores, hardware específico o APIs del sistema operativo..

Del concepto a la práctica

Taller de esta lección

Este quiz usa una brújula de diagnóstico: primero se identifica si el dato proviene de una API disponible, un sensor o un módulo propio; después se revisan permisos, ciclo de vida y contrato de salida.

Ejemplo resuelto

Caso: una tarjeta muestra brillo ambiental y deja de actualizarse al volver a la pantalla. La solución debe revisar si el sensor está disponible, registrar el listener al enfocar la pantalla, retirar la suscripción al desenfocar y representar illuminance en lux, como devuelve LightSensor. Esa API se documenta para Android: comprueba disponibilidad y ofrece una alternativa en plataformas sin sensor; no conviertas lux en un supuesto porcentaje sin declarar escala. No se crea un módulo personalizado si la API existente cubre la necesidad.

Pruébalo

  1. Clasifica tres necesidades: batería, acelerómetro y una API empresarial HTTP privada.

  2. Indica cuál requiere permiso o disponibilidad comprobable.

  3. Escribe la limpieza necesaria para cada listener.

  4. Define una salida de error para hardware ausente.

Error frecuente: atribuir todo fallo a JavaScript. Corrígelo separando error de permiso, error de disponibilidad y error de contrato.

Comprueba tu resultado

La clasificación esperada es módulo disponible o sensor para las dos primeras necesidades y cliente HTTP autorizado para la API empresarial. Que una API sea privada no exige un módulo nativo propio: lo exigiría una capacidad de plataforma no cubierta por los módulos disponibles. Cada caso debe incluir una comprobación y una salida de error.

Diagrama de decisión con ramas Sí y No y un bucle que procesa cada elemento de una lista.
CursaloMapa de práctica
Cómo avanza un programa

Una condición decide qué camino ejecuta el programa. Un bucle repite una operación mientras queden elementos, y termina cuando la lista se agota. Separar ambas ideas ayuda a seguir el flujo y comprobar casos límite.

Ver la infografía en tamaño completo ↗

Tipo de lección: Quiz

Concepto clave

La integración nativa en Expo permite acceder a funcionalidades del dispositivo que no están disponibles en JavaScript puro, como sensores, hardware específico o APIs del sistema operativo. Imagina que tu aplicación React Native es como un coche con un motor JavaScript: la integración nativa es el sistema de transmisión que conecta ese motor con las ruedas (el hardware real del dispositivo). Sin esta conexión, por más potente que sea tu motor, no podrás mover las ruedas.

Expo maneja esta integración a través de dos enfoques principales: APIs nativas preconstruidas (como Camera, Location o Notifications) y módulos personalizados que tú desarrollas cuando necesitas funcionalidades específicas no cubiertas por Expo. La clave está en entender que JavaScript y el código nativo (Java/Kotlin para Android, Objective-C/Swift para iOS) se comunican a través de un puente (bridge) que serializa y deserializa datos de forma asíncrona.

Cómo funciona en la práctica

Vamos a crear un módulo personalizado paso a paso. Supongamos que necesitas acceder al sensor de proximidad del dispositivo para detectar cuando un objeto se acerca a la pantalla (útil para aplicaciones de llamadas que apagan la pantalla al acercar el teléfono al oído).

  1. Primero, creas la interfaz nativa en cada plataforma. Para Android en Java, defines una clase que extiende ReactContextBaseJavaModule y registra métodos con @ReactMethod.
  2. Luego, creas un paquete ReactPackage que registra tu módulo.
  3. En iOS, haces lo equivalente en Objective-C o Swift con RCT_EXPORT_METHOD.
  4. Finalmente, en JavaScript, importas tu módulo con NativeModules y lo usas como cualquier otra API.

El flujo completo es: llamada JavaScript → puente → código nativo → ejecución en dispositivo → respuesta a través del puente → callback en JavaScript.

Código en acción

Aquí tienes un ejemplo funcional de un módulo personalizado para sensor de proximidad:

// En tu archivo JavaScript (ProximitySensor.js)
import { NativeModules } from 'react-native';
const { ProximityModule } = NativeModules;

export default class ProximitySensor {
  static async isNear() {
    try {
      const isNear = await ProximityModule.isNear();
      return isNear;
    } catch (error) {
      console.error('Error accediendo al sensor:', error);
      return false;
    }
  }

  static startListening(callback) {
    ProximityModule.startListening(callback);
  }

  static stopListening() {
    ProximityModule.stopListening();
  }
}

Y el código nativo para Android (antes de optimizar):

// ProximityModule.java (versión inicial)
package com.yourapp;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

public class ProximityModule extends ReactContextBaseJavaModule {
    private SensorManager sensorManager;
    private Sensor proximitySensor;
    private Callback jsCallback;

    public ProximityModule(ReactApplicationContext reactContext) {
        super(reactContext);
        sensorManager = (SensorManager) reactContext.getSystemService(Context.SENSOR_SERVICE);
        proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
    }

    @Override
    public String getName() {
        return "ProximityModule";
    }

    @ReactMethod
    public void isNear(Callback callback) {
        // Implementación básica
        sensorManager.registerListener(new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent event) {
                float distance = event.values[0];
                callback.invoke(distance < proximitySensor.getMaximumRange());
                sensorManager.unregisterListener(this);
            }
            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {}
        }, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
}

Después de refactorizar para mejor rendimiento:

// ProximityModule.java (versión optimizada)
public class ProximityModule extends ReactContextBaseJavaModule {
    private SensorManager sensorManager;
    private Sensor proximitySensor;
    private SensorEventListener listener;
    private Callback jsCallback;

    public ProximityModule(ReactApplicationContext reactContext) {
        super(reactContext);
        sensorManager = (SensorManager) reactContext.getSystemService(Context.SENSOR_SERVICE);
        proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
    }

    @ReactMethod
    public void isNear(Promise promise) {
        // Usando Promise en lugar de Callback para mejor manejo de errores
        if (proximitySensor == null) {
            promise.reject("NO_SENSOR", "Dispositivo no tiene sensor de proximidad");
            return;
        }
        
        listener = new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent event) {
                float distance = event.values[0];
                boolean isNear = distance < proximitySensor.getMaximumRange();
                sensorManager.unregisterListener(this);
                promise.resolve(isNear);
            }
            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {}
        };
        
        sensorManager.registerListener(listener, proximitySensor, SensorManager.SENSOR_DELAY_FASTEST);
    }

    @ReactMethod
    public void startListening(Callback callback) {
        this.jsCallback = callback;
        if (listener == null) {
            listener = new SensorEventListener() {
                @Override
                public void onSensorChanged(SensorEvent event) {
                    float distance = event.values[0];
                    boolean isNear = distance < proximitySensor.getMaximumRange();
                    WritableMap eventData = Arguments.createMap();
                    eventData.putBoolean("isNear", isNear);
                    eventData.putDouble("distance", distance);
                    sendEvent("proximityChange", eventData);
                }
                @Override
                public void onAccuracyChanged(Sensor sensor, int accuracy) {}
            };
        }
        sensorManager.registerListener(listener, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
    }

    private void sendEvent(String eventName, WritableMap params) {
        getReactApplicationContext()
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(eventName, params);
    }
}

Errores comunes

  • No manejar la ausencia del hardware: Asumir que todos los dispositivos tienen el sensor o API que necesitas. Siempre verifica la disponibilidad y proporciona fallbacks.
  • Bloquear el hilo principal: Ejecutar operaciones pesadas en el hilo de UI nativo. Usa hilos secundarios o trabajadores para operaciones largas.
  • Fugas de memoria en listeners: Registrar event listeners sin desregistrarlos cuando el componente se desmonta. Implementa cleanup en onCatalystInstanceDestroy.
  • Serialización incorrecta de datos: Intentar pasar objetos complejos a través del puente. Solo se permiten tipos básicos: boolean, number, string, array, map.
  • Olvidar configurar permisos: APIs como cámara o ubicación requieren permisos en AndroidManifest.xml e Info.plist, y solicitudes en tiempo de ejecución.

Checklist de dominio

  1. Puedo crear un módulo nativo personalizado que exponga al menos 3 métodos a JavaScript
  2. Sé configurar correctamente los permisos necesarios para APIs nativas en ambas plataformas
  3. He implementado manejo de errores robusto tanto en código nativo como en JavaScript
  4. Puedo optimizar la comunicación entre JavaScript y nativo para minimizar latencia
  5. Entiendo cómo depurar problemas en el puente usando herramientas como Flipper
  6. Sé cuándo usar APIs preconstruidas de Expo vs. desarrollar módulos personalizados
  7. He probado mi integración nativa en dispositivos físicos con diferentes versiones de OS

Crear un módulo nativo para control de brillo de pantalla

Desarrolla un módulo nativo personalizado que permita ajustar el brillo de la pantalla desde tu aplicación Expo. Este ejercicio simula un caso real donde necesitas funcionalidad específica no cubierta por las APIs estándar.

  1. Configuración inicial: Crea un proyecto Expo con desarrollo desnudo (bare workflow) o modifica uno existente. Asegúrate de tener las herramientas de desarrollo nativo instaladas (Android Studio, Xcode).
  2. Implementación Android:
    • Crea una clase Java BrightnessModule que extienda ReactContextBaseJavaModule
    • Implementa dos métodos: setBrightness(float brightness) y getBrightness()
    • Usa WindowManager.LayoutParams para ajustar el brillo (valores de 0.0 a 1.0)
    • Registra tu módulo en un ReactPackage personalizado
  3. Implementación iOS:
    • Crea un archivo BrightnessModule.m (Objective-C) o .swift
    • Implementa los mismos métodos usando UIScreen.main.brightness
    • Exporta los métodos con RCT_EXPORT_METHOD
  4. Capa JavaScript:
    • Crea un archivo BrightnessController.js que importe NativeModules
    • Expón una API limpia con métodos setBrightness() y getBrightness()
    • Añade validación de rangos (0-1) y manejo de errores
  5. Pruebas:
    • Crea una pantalla de prueba con un Slider para ajustar brillo
    • Muestra el valor actual del brillo en tiempo real
    • Prueba en dispositivo físico Android e iOS
  6. Optimización:
    • Añade un método resetToSystemBrightness() que restaure la configuración original
    • Implementa eventos nativos para detectar cambios de brillo del sistema
    • Documenta tu módulo con TypeScript definitions
Pistas
  • Recuerda que en Android necesitas el permiso WRITE_SETTINGS para modificar brillo del sistema
  • En iOS, UIScreen.main.brightness solo funciona dentro del rango 0.0 a 1.0, valores fuera causarán excepciones
  • Considera usar eventos nativos (DeviceEventEmitter) para notificar cambios de brillo iniciados por el sistema

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.

Sigue con Desarrollo Avanzado de Aplicaciones con Expo: De Concepto a Producción

Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.

Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.

01 / ELEGIR UNA RUTA

El próximo paso

Aprende IA mientras construyes algo que sí necesitas.

Empieza por el curso inicial o explora el catálogo para encontrar una práctica alineada con tu trabajo.