Dez exemplos para quando soluções apenas em JavaScript falham e você precisa cruzar a fronteira nativa - a base que toda equipe do Expo SDK 57 precisa antes de adicionar bibliotecas de terceiros, plugins de configuração ou módulos personalizados.
Comece com um aplicativo Expo em branco com um caminho de build de desenvolvimento pronto - módulos nativos não são executados no Jest sem mocks e não podem carregar código arbitrário no Expo Go:
Ferramentas: Estes exemplos visam o Expo SDK 57 (expo ~57.0.4), React Native 0.86.0 e React 19.2.3. Combine esta página com Regras de Módulos Nativos antes de adicionar qualquer pacote com código nativo.
Frames de câmera, correções de GPS, fluxos de acelerômetro e sensores biométricos leem hardware do dispositivo através de APIs do sistema operacional. JavaScript não pode acessá-los sem um módulo nativo:
import { CameraView, useCameraPermissions } from "expo-camera";import { Button } from "react-native";export function ScanScreen() { const [permission, requestPermission] = useCameraPermissions(); if (!permission?.granted) { return <Button title="Permitir câmera" onPress={requestPermission} />; } return <CameraView style={{ flex: 1 }} facing="back" />;}
expo-camera envolve AVFoundation (iOS) e CameraX (Android) - nenhum substituto puramente em JS
Solicitações de permissão exigem entradas Info.plist e AndroidManifest - plugins de configuração lidam com isso
Expo Go inclui expo-camera; SDKs de câmera arbitrários não incluem
Timers e fetch pausam quando o aplicativo está em segundo plano ou é encerrado. Entrega de push, atualizações de localização e uploads em segundo plano exigem agendadores nativos:
import * as TaskManager from "expo-task-manager";import * as Location from "expo-location";const TASK_NAME = "background-location";TaskManager.defineTask(TASK_NAME, ({ data, error }) => { if (error) return; const { locations } = data as { locations: Location.LocationObject[] }; // Envie as localizações para sua API - a tarefa nativa continua disparando});export async function startBackgroundLocation(): Promise<void> { await Location.startLocationUpdatesAsync(TASK_NAME, { accuracy: Location.Accuracy.Balanced, distanceInterval: 100, showsBackgroundLocationIndicator: true, });}
Modos de segundo plano exigem permissões e flags de manifesto - nunca edite manualmente após pré-build do CNG
O iOS encerra aplicativos que reivindicam capacidade de segundo plano sem uso real - a revisão da App Store rejeita modos fantasmas
Algumas visualizações não podem ser reproduzidas fielmente no layout do React Native - mapas, players de vídeo, renderizadores de PDF e WebViews incorporam visualizações nativas da plataforma dentro da árvore RN:
import MapView, { Marker } from "react-native-maps";export function StoreMap({ lat, lng }: { lat: number; lng: number }) { return ( <MapView style={{ flex: 1 }} initialRegion={{ latitude: lat, longitude: lng, latitudeDelta: 0.02, longitudeDelta: 0.02, }} > <Marker coordinate={{ latitude: lat, longitude: lng }} title="Loja" /> </MapView> );}
Visualizações nativas participam da árvore de sombras do Fabric na Nova Arquitetura - props de layout ainda se aplicam ao contêiner
SDKs de mapas de terceiros enviam seus próprios binários nativos - avalie a compatibilidade antes de npm install
HealthKit, NFC, CarPlay, atalhos da Siri e intents do Android existem apenas em uma plataforma. A detecção de recursos impede o JS; módulos nativos implementam a chamada:
import { Platform } from "react-native";import * as LocalAuthentication from "expo-local-authentication";export async function biometricGate(): Promise<boolean> { if (Platform.OS === "web") return false; const compatible = await LocalAuthentication.hasHardwareAsync(); if (!compatible) return false; const enrolled = await LocalAuthentication.isEnrolledAsync(); if (!enrolled) return false; const result = await LocalAuthentication.authenticateAsync({ promptMessage: "Desbloqueie para continuar", }); return result.success;}
Verificações Platform.OS evitam travamentos; elas não substituem a implementação nativa
Módulos específicos da plataforma devem expor uma API unificada de TypeScript na camada do seu aplicativo - oculte Platform.select dentro de serviços
Transformações pesadas de imagem, codificação de vídeo, criptografia e inferência de ML bloqueiam a thread JS se feitas em JavaScript puro. Módulos nativos executam trabalho em filas de segundo plano ou caminhos de GPU:
import * as ImageManipulator from "expo-image-manipulator";export async function resizeAvatar(uri: string): Promise<string> { const result = await ImageManipulator.manipulateAsync( uri, [{ resize: { width: 256 } }], { compress: 0.8, format: ImageManipulator.SaveFormat.JPEG } ); return result.uri;}
Hermes é rápido para lógica de aplicativo - não é um substituto para Core Image, MediaCodec ou C++ SIMD
Perfure no dispositivo: se as quedas de quadros do JS coincidirem com sua função, mova o trabalho para nativo ou para um worker
Folhas de compartilhamento, widgets na tela inicial, App Clips e links universais são registrados na camada de aplicativo nativo. JavaScript recebe eventos de deep-link; o código nativo é o proprietário do registro:
import * as Linking from "expo-linking";export function subscribeToDeepLinks(onUrl: (url: string) => void): () => void { const sub = Linking.addEventListener("url", ({ url }) => onUrl(url)); Linking.getInitialURL().then((url) => { if (url) onUrl(url); }); return () => sub.remove();}
// trecho do app.json - domínios associados são permissões nativas{ "expo": { "scheme": "myapp", "ios": { "associatedDomains": ["applinks:shop.example.com"] } }}
Domínios associados e filtros de intent do Android são aplicados durante o pré-build - expresse-os na configuração, não em edições manuais de Info.plist
Web Bluetooth não está disponível no React Native. Escaneamento BLE, emparelhamento e leitura/escrita GATT exigem CoreBluetooth (iOS) e BluetoothGatt (Android):
// Conceitual - bibliotecas BLE reais expõem módulos nativosimport { BleManager } from "react-native-ble-plx";const manager = new BleManager();export async function scanForPeripherals(): Promise<void> { manager.startDeviceScan(null, null, (error, device) => { if (error) return; if (device?.name) console.log("Encontrado", device.name); });}
Bibliotecas BLE nunca estão no Expo Go - planeje expo-dev-client desde o início
BLE em segundo plano no iOS requer permissões adicionais e consome bateria - documente em seu ADR
Às vezes, a lacuna não é capacidade, mas conformidade: fixação de certificado, detecção de jailbreak, SDKs de pagamento proprietários ou APIs exigidas pelo MDM. Estes são enviados como binários nativos sem equivalente no Expo SDK:
// Fachada da camada de aplicativo - a implementação é um módulo nativo ou SDK de terceirosexport type AttestationResult = { passed: boolean; reason?: string };export async function attestDevice(): Promise<AttestationResult> { // Módulo nativo: DeviceCheck (iOS) / Play Integrity (Android) const NativeAttestation = require("../native/attestation").default; return NativeAttestation.runAsync();}
Revisão da loja pode exigir versões específicas do SDK nativo - wrappers JS devem corresponder a builds nativos fixados
Qualquer solução alternativa apenas com polyfill que toque na segurança deve ser rejeitada na revisão de código - veja Regras de Módulos Nativos
JavaScript (Hermes)
│
▼
expo-modules-core / objeto host JSI
│
├── API Expo Modules (expo-camera, expo-file-system, …)
├── React Native TurboModules (bibliotecas da comunidade)
└── Fallback de bridge legado (apenas bibliotecas mais antigas)
│
▼
Implementação nativa Swift / Kotlin / C++
Camada
O que você importa
Reconstrução necessária?
Módulo Expo SDK
import * as Camera from "expo-camera"
Sim, após a instalação
Biblioteca nativa da comunidade
import MapView from "react-native-maps"
Sim, após a instalação
Módulo Expo personalizado
import MyModule from "my-local-module"
Sim, sempre
Pacote apenas JS
import { z } from "zod"
Não - OTA pode enviar
import { requireNativeModule, requireOptionalNativeModule } from "expo-modules-core";// Lança erro se a classe nativa estiver faltando no binárioconst FileSystem = requireNativeModule("ExpoFileSystem");// Retorna null em testes ou quando o módulo não está vinculadoconst Optional = requireOptionalNativeModule("ExpoCustomFeature");
Esperar que o Expo Go carregue qualquer pacote npm nativo - apenas módulos pré-instalados funcionam. Correção:npx expo install expo-dev-client e crie um cliente de desenvolvimento.
Instalar com npm install em vez de npx expo install - versões de binários JS e nativos divergem. Correção: Sempre npx expo install <pacote> para dependências nativas.
Pular reconstrução nativa após alteração no package.json - a recarga a quente do Metro não compila Swift/Kotlin. Correção:npx expo run:ios / eas build após cada alteração nativa.
Chamar módulos nativos no nível superior do módulo em testes - Jest não tem bridge. Correção: Simular na fronteira do serviço ou usar jest-expo.
Editar manualmente ios/ e android/ em aplicativos CNG - o próximo prebuild --clean apaga as edições. Correção: Codificar alterações em plugins de configuração conforme Pré-build CNG.
Uma ponte entre JavaScript e código da plataforma (Swift, Kotlin, Objective-C, Java, C++). No React Native 0.86 com a Nova Arquitetura, a maioria dos módulos usa JSI e TurboModules para chamadas síncronas e tipadas.
Preciso de código nativo para todos os pacotes do Expo SDK?
Não - você os consome do JavaScript. A equipe do Expo mantém a implementação nativa. Você só escreve código nativo quando nenhum pacote SDK ou da comunidade se encaixa.
Posso enviar alterações de código nativo via OTA?
Não. OTA (EAS Update) envia apenas JavaScript e assets. Alterações de dependência nativa exigem um novo binário da loja ou build de desenvolvimento.
Expo Go vs build de desenvolvimento - regra rápida?
Expo Go: apenas módulos SDK, o spike mais rápido. Build de desenvolvimento: suas dependências nativas do package.json, necessário para módulos de terceiros e personalizados.
Como sei se um pacote npm tem código nativo?
Procure por ios/, android/, .podspec ou expo-module.config.json no pacote. Execute npx expo-doctor após a instalação - ele sinaliza dependências nativas incompatíveis.