Plugins de configuración
Un libro de recetas para plugins de app.config que modifican Info.plist, AndroidManifest.xml, y Gradle - la forma segura para CNG de conectar módulos nativos sin desviaciones manuales en ios/ y android/.
Busca en todas las páginas de la documentación
Un libro de recetas para plugins de app.config que modifican Info.plist, AndroidManifest.xml, y Gradle - la forma segura para CNG de conectar módulos nativos sin desviaciones manuales en ios/ y android/.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
// app.config.ts
import type { ExpoConfig } from "expo/config";
export default ({ config }: { config: ExpoConfig }): ExpoConfig => ({
...config,
plugins: [
// Plugins de permisos integrados (Info.plist + AndroidManifest)
[
"expo-camera",
{
cameraPermission: "Permite que $(PRODUCT_NAME) escanee códigos de barras de productos",
microphonePermission: false,
},
],
// Propiedades en tiempo de compilación (Gradle, propiedades de Podfile)
[
"expo-build-properties",
{
ios: { deploymentTarget: "15.1" },
android: { minSdkVersion: 24, compileSdkVersion: 35 },
},
],
// Plugin local para cambios nativos personalizados
"./plugins/with-custom-url-scheme",
],
});# Aplicar plugins e inspeccionar la salida
npx expo prebuild --clean
git diff ios/ android/ # revisa los cambios nativos generados
# Verificación de idempotencia - la segunda ejecución no debe producir nuevos cambios
npx expo prebuild
git diff ios/ android/ # se espera vacíoCuándo usar esto:
NSCameraUsageDescription) requeridas por módulos nativos.queries para visibilidad del paquete.minSdkVersion de Gradle, reglas de ProGuard, o objetivos de implementación de iOS para un SDK nativo.// app.config.ts
import type { ExpoConfig } from "expo/config";
export default ({ config }: { config: ExpoConfig }): ExpoConfig => ({
...config,
name: "ConfigPluginCookbook",
slug: "config-plugin-cookbook",
plugins: [
[
"expo-camera",
{
cameraPermission:
"Permite que $(PRODUCT_NAME) tome fotos para tu perfil",
microphonePermission:
"Permite que $(PRODUCT_NAME) grabe audio con video",
recordAudioAndroid: true,
},
],
],
});Después de npx expo prebuild, iOS recibe:
<!-- ios/<App>/Info.plist (generado) -->
<key>NSCameraUsageDescription</key>
<string>Permite que ConfigPluginCookbook tome fotos para tu perfil</string>
<key>NSMicrophoneUsageDescription</key>
<string>Permite que ConfigPluginCookbook grabe audio con video</string>Android recibe:
<!-- android/app/src/main/AndroidManifest.xml (fragmento generado) -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />// plugins/with-custom-url-scheme.ts
import type { ConfigPlugin } from "expo/config-plugins";
import {
AndroidConfig,
IOSConfig,
createRunOncePlugin,
withAndroidManifest,
withInfoPlist,
} from "expo/config-plugins";
const SCHEME = "myshop";
const withCustomUrlScheme: ConfigPlugin = (config) => {
config = withInfoPlist(config, (mod) => {
const urlTypes = mod.modResults.CFBundleURLTypes ?? [];
const exists = urlTypes.some(
(entry) =>
Array.isArray(entry.CFBundleURLSchemes) &&
entry.CFBundleURLSchemes.includes(SCHEME)
);
if (!exists) {
urlTypes.push({
CFBundleURLName: "com.example.myshop",
CFBundleURLSchemes: [SCHEME],
});
mod.modResults.CFBundleURLTypes = urlTypes;
}
return mod;
});
config = withAndroidManifest(config, (mod) => {
const manifest = mod.modResults;
const app = AndroidConfig.Manifest.getMainApplicationOrThrow(manifest);
// Filtro de intención de enlace profundo
AndroidConfig.Manifest.addIntentFilter(app, {
action: "android.intent.action.VIEW",
category: ["android.intent.category.DEFAULT", "android.intent.category.BROWSABLE"],
data: [{ scheme: SCHEME }],
});
return mod;
});
return config;
};
export default createRunOncePlugin(withCustomUrlScheme, "with-custom-url-scheme", "1.0.0");// app.config.ts - registra el plugin local
import type { ExpoConfig } from "expo/config";
export default ({ config }: { config: ExpoConfig }): ExpoConfig => ({
...config,
plugins: ["./plugins/with-custom-url-scheme"],
});Lo que esto demuestra:
withInfoPlist muta metadatos de iOS idempotentemente - verifica antes de agregar tipos de URL.withAndroidManifest agrega filtros de intención sin desviaciones de regex-append.createRunOncePlugin previene la aplicación duplicada cuando prebuild se ejecuta varias veces.npx expo prebuild --clean - el contrato de CNG según Prebuild Basics.// fragmento de app.config.ts - SDK nativo requiere compileSdk más alto
plugins: [
[
"expo-build-properties",
{
android: {
minSdkVersion: 24,
compileSdkVersion: 35,
targetSdkVersion: 35,
extraProguardRules: `
-keep class com.vendor.sdk.** { *; }
`,
},
ios: {
deploymentTarget: "15.1",
useFrameworks: "static",
},
},
],
],Consulta Patching Native Projects para estrategias avanzadas de supervivencia de Gradle.
app.config.ts / app.json
│
▼
plugins: [ "expo-camera", "./plugins/with-custom-url-scheme" ]
│
▼ (secuencial, en orden de array)
Mod plugins: withInfoPlist, withAndroidManifest, withGradleProperties, …
│
▼
ios/ android/ escrito en disco
| Asistente Mod | Archivo de destino | Uso típico |
|---|---|---|
withInfoPlist | Info.plist | Descripciones de uso, esquemas de URL, modos de fondo |
withAndroidManifest | AndroidManifest.xml | Permisos, filtros de intención, queries |
withGradleProperties | gradle.properties | Argumentos de JVM, banderas de Android |
withAppBuildGradle | app/build.gradle | Dependencias, opciones de empaquetamiento |
withEntitlementsPlist | .entitlements | Dominios asociados, push, grupos de cadena de claves |
Cuando dos plugins modifican la misma clave, los plugins posteriores ganan. Documenta el orden en app.config.ts:
plugins: [
"expo-router", // Debe ejecutarse antes que plugins que agregan activos de splash
"expo-camera", // Permisos primero
"./plugins/with-custom-url-scheme", // Enlaces profundos específicos de la app después del router
[
"expo-build-properties", // Props de compilación al final - pueden depender de la estructura anterior
{ android: { minSdkVersion: 24 } },
],
],// app.config.ts - dinámico (consciente del entorno)
import type { ExpoConfig } from "expo/config";
export default ({ config }: { config: ExpoConfig }): ExpoConfig => ({
...config,
extra: {
apiUrl: process.env.EXPO_PUBLIC_API_URL,
},
plugins: [
[
"react-native-maps",
{
iosGoogleMapsApiKey: process.env.GOOGLE_MAPS_IOS_KEY ?? "",
},
],
],
});Los secretos pertenecen a variables de entorno de EAS, no a archivos .env comprometidos referenciados en tiempo de prebuild en CI.
# Registros de prebuild detallados
EXPO_DEBUG=1 npx expo prebuild --clean
# Diferencia solo Info.plist
npx expo prebuild --platform ios --clean
diff ios/MyApp/Info.plist ios/MyApp/Info.plist.bak 2>/dev/null || true
# Valida con doctor
npx expo-doctorNS*UsageDescription - iOS se bloquea en tiempo de solicitud de permisos. Solución: Usa el plugin de configuración de la biblioteca o agrega entradas de withInfoPlist.createRunOncePlugin.--clean. Solución: Mueve el cambio a un plugin según Config Plugins in CNG.<queries>. Solución: withAndroidManifest agrega entradas de queries.| Alternativa | Úsalo cuando | No lo uses cuando |
|---|---|---|
| Plugin de configuración de Expo integrado | La biblioteca envía expo-camera, expo-location, etc. | La biblioteca de proveedor no tiene plugin |
Mod de expo/config-plugins local | Derechos únicos o entradas de manifiestos | Fork nativo grande - escribe un módulo Expo adecuado |
expo-build-properties | Versiones de SDK, ProGuard, objetivo de implementación | Cirugía Gradle de múltiples archivos complejos |
| Ediciones nativas manuales de Brownfield | App heredada no en CNG | Apps de greenfield estándar del SDK 57 |
Una función registrada en el array plugins de app.config que se ejecuta durante npx expo prebuild y modifica programáticamente archivos de proyectos nativos.
Los plugins integrados se envían dentro de paquetes del Expo SDK (expo-camera, expo-build-properties). Los plugins personalizados viven en tu repositorio - comúnmente plugins/with-*.ts.
Sí. EAS Build ejecuta prebuild (a menos que esté deshabilitado), ejecutando la misma canalización de plugins que npx expo prebuild local.
Ejecuta npx expo prebuild dos veces. git diff ios/ android/ después de la segunda ejecución debe estar vacío.
Un módulo nativo implementa llamadas de JS a nativo en tiempo de ejecución. Un plugin de configuración configura el proyecto nativo para que el módulo se compile y tenga permisos - a menudo se envían juntos.
expo-build-propertiesVersiones de stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0, y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026