Usar Expo Modules para crear APIs nativas

Lectura
20 min~6 min lectura

Concepto clave

Los Expo Modules son paquetes de código nativo que extienden las capacidades de tu aplicación Expo, permitiendo acceder a APIs del sistema operativo que no están disponibles en JavaScript puro. Piensa en ellos como traductores especializados: tu aplicación en JavaScript necesita comunicarse con el hardware del dispositivo (como la cámara o sensores), y los Expo Modules actúan como intermediarios que convierten esas solicitudes en instrucciones que iOS o Android pueden entender directamente.

En el desarrollo avanzado, crear tus propios módulos es crucial cuando necesitas funcionalidades específicas que no existen en la comunidad, como integrar con hardware personalizado o APIs empresariales privadas. A diferencia de los métodos tradicionales que requieren configurar proyectos nativos separados, Expo Modules usa una arquitectura unificada con Expo CLI y config plugins, lo que simplifica el mantenimiento y despliegue. Imagina construir una aplicación de fitness que use un sensor de frecuencia cardíaca Bluetooth exclusivo: sin un módulo personalizado, sería imposible acceder a esos datos desde JavaScript.

Cómo funciona en la práctica

Para crear un Expo Module, sigues un flujo estructurado que garantiza compatibilidad entre plataformas. Primero, defines la interfaz JavaScript que usarás en tu código React Native, luego implementas la lógica nativa en Swift/Objective-C para iOS y Kotlin/Java para Android, y finalmente configuras el módulo para que Expo lo reconozca. Aquí un ejemplo paso a paso para un módulo simple que muestra un mensaje nativo:

  1. Inicia un nuevo proyecto de módulo con npx create-expo-module, que genera la estructura básica con carpetas para iOS, Android y JavaScript.
  2. En la carpeta src, escribe el archivo JavaScript que exporta las funciones que quieres exponer, como showMessage(text).
  3. En ios/MyModule.swift, implementa la función nativa que usa APIs de iOS para mostrar una alerta con el texto recibido.
  4. En android/src/main/java/.../MyModule.kt, haz lo mismo para Android usando Toast o Snackbar.
  5. Registra el módulo en los archivos de configuración de Expo (expo-module.config.json) para que se incluya automáticamente al construir la aplicación.

Este proceso asegura que tu módulo funcione en ambas plataformas sin que tú gestiones manualmente las diferencias, similar a como un framework como React Native abstrae la UI nativa.

Código en acción

Veamos un ejemplo funcional de un Expo Module que obtiene el nivel de batería del dispositivo. Este es un caso común en aplicaciones que necesitan optimizar el rendimiento en dispositivos móviles.

Antes: Sin un módulo, tendrías que usar soluciones limitadas o bibliotecas de terceros que podrían no ser confiables.

Después: Con tu propio módulo, tienes control total y rendimiento nativo. Aquí está el código JavaScript:

// En tu aplicación Expo, importa y usa el módulo
import * as BatteryModule from 'expo-battery-module';

async function checkBattery() {
  try {
    const batteryLevel = await BatteryModule.getBatteryLevel();
    console.log(`Nivel de batería: ${batteryLevel}%`);
    return batteryLevel;
  } catch (error) {
    console.error('Error al obtener batería:', error);
  }
}

// Llama a la función en tu componente
checkBattery();

Y aquí la implementación nativa para Android (Kotlin):

// android/src/main/java/expo/modules/battery/BatteryModule.kt
package expo.modules.battery

import android.content.Context
import android.os.BatteryManager
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

class BatteryModule : Module() {
  override fun definition() = ModuleDefinition {
    Name("BatteryModule")

    AsyncFunction("getBatteryLevel") {
      val batteryManager = appContext.reactContext?.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
      val batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
      batteryLevel.toDouble() / 100.0 // Devuelve un valor entre 0 y 1
    }
  }
}

Errores comunes

  • No probar en ambas plataformas: Implementar solo para iOS u Android y asumir que funciona en la otra. Solución: Usa el emulador y dispositivos reales para verificar el comportamiento, y escribe pruebas unitarias específicas para cada plataforma.
  • Olvidar registrar el módulo en la configuración de Expo: Si no actualizas expo-module.config.json, Expo no incluirá tu módulo en la build. Solución: Revisa siempre este archivo después de añadir nuevas funciones o plataformas.
  • Manejo incorrecto de hilos en código nativo: Ejecutar operaciones bloqueantes en el hilo principal de UI, causando que la aplicación se congele. Solución: Usa AsyncFunction en Kotlin o colas de despacho en Swift para operaciones asíncronas.
  • No versionar el módulo adecuadamente: Cambiar APIs sin incrementar la versión, rompiendo aplicaciones existentes. Solución: Sigue versionado semántico (ej., 1.0.0 a 1.1.0 para nuevas características) y documenta los cambios.
  • Ignorar el manejo de permisos: Acceder a APIs sensibles como ubicación sin solicitar permisos, lo que causa fallos en iOS/Android. Solución: Añade configuraciones de permisos en app.json y verifica en tiempo de ejecución.

Checklist de dominio

  • Creé un Expo Module desde cero usando npx create-expo-module y lo integré en una aplicación Expo existente.
  • Implementé funciones nativas en Swift/Kotlin que exponen APIs del dispositivo a JavaScript.
  • Probé el módulo en emuladores de iOS y Android, verificando que no haya crashes o warnings.
  • Configuré permisos necesarios en app.json para APIs sensibles y manejé errores de usuario.
  • Documenté el módulo con ejemplos de uso y lo publiqué en un repositorio privado o npm.
  • Optimicé el rendimiento usando operaciones asíncronas y evité fugas de memoria en código nativo.
  • Actualicé el módulo siguiendo versionado semántico y manteniendo retrocompatibilidad.

Crear un módulo para controlar el brillo de pantalla

En este ejercicio, desarrollarás un Expo Module que permita ajustar el brillo de la pantalla del dispositivo desde tu aplicación Expo. Esto es útil para aplicaciones como lectores o reproductores de video que necesitan controlar la experiencia visual del usuario.

  1. Prepara el entorno: Asegúrate de tener Expo CLI instalado y un proyecto Expo existente. Ejecuta npx create-expo-module expo-brightness-control en una carpeta separada para generar la estructura del módulo.
  2. Define la interfaz JavaScript: En src/index.ts, exporta dos funciones: setBrightness(level: number) para establecer el brillo (0 a 1) y getBrightness(): Promise<number> para obtener el nivel actual.
  3. Implementa para iOS: En ios/ExpoBrightnessControl.swift, usa UIScreen.main.brightness para ajustar y obtener el brillo. Asegúrate de manejar los límites (0.0 a 1.0).
  4. Implementa para Android: En android/src/main/java/expo/modules/brightness/ExpoBrightnessControl.kt, usa WindowManager.LayoutParams.screenBrightness con el contexto de la actividad. Nota: En Android, necesitas permisos si cambias la configuración del sistema.
  5. Configura el módulo: Actualiza expo-module.config.json para incluir el nombre y plataformas. Añade cualquier permiso necesario en la sección android o ios.
  6. Integra en tu aplicación: En tu proyecto Expo, añade el módulo localmente usando expo install ./ruta/a/expo-brightness-control y crea un componente simple con controles deslizantes para probarlo.
  7. Prueba y depura: Ejecuta la aplicación en un emulador iOS y Android. Verifica que los cambios de brillo se reflejen y que no haya errores en la consola.
Pistas
  • Usa AsyncFunction en Kotlin para getBrightness ya que accede a hardware.
  • En iOS, recuerda que UIScreen.main.brightness es una propiedad mutable que puedes asignar directamente.
  • Para permisos en Android, añade <uses-permission android:name="android.permission.WRITE_SETTINGS" /> en el manifest si es necesario, pero prueba primero sin él ya que algunos niveles no lo requieren.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.