React Native Academy · Pelajaran

Memodifikasi AndroidManifest dan Info.plist

Gunakan withAndroidManifest untuk menambahkan elemen izin dan metadata, serta withInfoPlist untuk menambahkan kunci seperti NSCameraUsageDescription, lalu uji di kedua platform.

Pelajaran 3 dari 413 langkah

Memodifikasi AndroidManifest dan Info.plist adalah pelajaran React Native Academy gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar React Native Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus React Native Academy mencakup 4 pelajaran total.

Bagian dari pelajaran ini belum diterjemahkan dan ditampilkan dalam bahasa Inggris.

Why These Files Matter

AndroidManifest.xml and Info.plist are the central configuration files for Android and iOS apps respectively. They declare permissions, features, app components, URL schemes, capabilities, and usage descriptions. Third-party native libraries almost always require additions to these files. Without the correct entries, features crash, permissions are denied, and App Store submission fails review.

Anatomy of AndroidManifest.xml

The AndroidManifest.xml has three main sections: uses-permission elements at the top level (declare what the app needs), the application element (app-wide settings like icon, theme, backup), and inside it, activity, service, receiver, and provider elements. Config plugins typically add permissions and meta-data elements to the application or activity.

<!-- Typical AndroidManifest.xml structure -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

  <!-- Permissions at top level -->
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.CAMERA" />

  <application
    android:name=".MainApplication"
    android:label="@string/app_name">

    <!-- meta-data items here -->
    <meta-data android:name="com.google.android.geo.API_KEY"
      android:value="YOUR_KEY" />

    <activity android:name=".MainActivity">
      <!-- intent filters here -->
    </activity>

  </application>
</manifest>

Adding Permissions to AndroidManifest

Use AndroidConfig.Permissions.addUsesPermission to safely add a permission without duplicating it. This helper checks whether the permission already exists before inserting, so running prebuild multiple times is safe. Always add both dangerous runtime permissions (like CAMERA) and the uses-feature declaration when requiring specific hardware to prevent your app from appearing in the Play Store on incompatible devices.

const { withAndroidManifest, AndroidConfig } = require('@expo/config-plugins');

module.exports = function withCameraAndroid(config) {
  return withAndroidManifest(config, (androidConfig) => {
    const manifest = androidConfig.modResults;

    AndroidConfig.Permissions.addUsesPermission(
      manifest, 'android.permission.CAMERA'
    );
    AndroidConfig.Permissions.addUsesPermission(
      manifest, 'android.permission.READ_MEDIA_IMAGES'
    );

    // Declare hardware requirement
    if (!manifest['uses-feature']) manifest['uses-feature'] = [];
    manifest['uses-feature'].push({
      '$': { 'android:name': 'android.hardware.camera', 'android:required': 'false' }
    });

    return androidConfig;
  });
};

Adding meta-data to the Application Element

Many SDKs (Google Maps, Firebase, Facebook) require a meta-data element inside the application tag with an API key or app ID. Use AndroidConfig.Manifest.getMainApplication to safely locate the application element, then push a new meta-data object into its 'meta-data' array. Always check the array exists before pushing to avoid a null reference error.

const { withAndroidManifest, AndroidConfig } = require('@expo/config-plugins');

module.exports = function withGoogleMapsAndroid(config, { apiKey }) {
  return withAndroidManifest(config, (androidConfig) => {
    const manifest = androidConfig.modResults;
    const mainApp = AndroidConfig.Manifest.getMainApplication(manifest);

    if (!mainApp['meta-data']) mainApp['meta-data'] = [];

    // Remove existing key to avoid duplicates
    mainApp['meta-data'] = mainApp['meta-data'].filter(
      (item) => item.$['android:name'] !== 'com.google.android.geo.API_KEY'
    );

    mainApp['meta-data'].push({
      '$': {
        'android:name': 'com.google.android.geo.API_KEY',
        'android:value': apiKey,
      }
    });

    return androidConfig;
  });
};

Intent Filters for Deep Links

To register a custom URL scheme or Android App Link on Android, you add an intent filter to the main activity. The intent filter declares what URL patterns your app handles. Config plugins can add these programmatically by finding the MainActivity element and appending to its intent-filter array.

const { withAndroidManifest, AndroidConfig } = require('@expo/config-plugins');

module.exports = function withDeepLinks(config, { scheme }) {
  return withAndroidManifest(config, (androidConfig) => {
    const manifest = androidConfig.modResults;
    const mainActivity = AndroidConfig.Manifest.getMainActivity(manifest);

    if (!mainActivity['intent-filter']) mainActivity['intent-filter'] = [];

    mainActivity['intent-filter'].push({
      action: [{ '$': { 'android:name': 'android.intent.action.VIEW' } }],
      category: [
        { '$': { 'android:name': 'android.intent.category.DEFAULT' } },
        { '$': { 'android:name': 'android.intent.category.BROWSABLE' } },
      ],
      data: [{ '$': { 'android:scheme': scheme } }],
    });

    return androidConfig;
  });
};

Info.plist Usage Description Keys

iOS requires a usage description string in Info.plist for every sensitive permission your app requests. If the string is missing, the app crashes when it calls requestPermission. Common keys include NSCameraUsageDescription, NSLocationWhenInUseUsageDescription, NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription, and NSContactsUsageDescription. The string appears in the system permission dialog shown to the user.

const { withInfoPlist } = require('@expo/config-plugins');

module.exports = function withAllPermissions(config, opts = {}) {
  return withInfoPlist(config, (iosConfig) => {
    const plist = iosConfig.modResults;

    plist['NSCameraUsageDescription'] =
      opts.camera || 'Scan QR codes and take profile photos.';
    plist['NSMicrophoneUsageDescription'] =
      opts.microphone || 'Record voice messages.';
    plist['NSLocationWhenInUseUsageDescription'] =
      opts.location || 'Show nearby restaurants.';
    plist['NSPhotoLibraryUsageDescription'] =
      opts.photoLibrary || 'Upload photos from your library.';

    return iosConfig;
  });
};

URL Types for Custom Schemes on iOS

To handle custom URL schemes on iOS (e.g., myapp://), add a CFBundleURLTypes entry to Info.plist. Each entry has a role and an array of scheme strings. Expo sets this automatically for the app's own scheme, but if you need an additional scheme (e.g., for OAuth redirect), you must add it via a config plugin.

const { withInfoPlist } = require('@expo/config-plugins');

module.exports = function withOAuthScheme(config, { scheme }) {
  return withInfoPlist(config, (iosConfig) => {
    const plist = iosConfig.modResults;

    if (!plist['CFBundleURLTypes']) plist['CFBundleURLTypes'] = [];

    // Avoid duplicate
    const existing = plist['CFBundleURLTypes'].find(
      (t) => t.CFBundleURLName === scheme
    );
    if (!existing) {
      plist['CFBundleURLTypes'].push({
        CFBundleURLName: scheme,
        CFBundleURLSchemes: [scheme],
      });
    }

    return iosConfig;
  });
};

Info.plist Boolean and Number Values

Info.plist supports several value types beyond strings: booleans, numbers, arrays, and dictionaries. In the JavaScript modResults object, use plain JS booleans and numbers — Expo handles the correct plist XML encoding. Setting ITSAppUsesNonExemptEncryption to false (a boolean) avoids App Store export compliance questions for most apps.

const { withInfoPlist } = require('@expo/config-plugins');

module.exports = function withExportCompliance(config) {
  return withInfoPlist(config, (iosConfig) => {
    const plist = iosConfig.modResults;

    // Boolean value
    plist['ITSAppUsesNonExemptEncryption'] = false;

    // Number value
    plist['UIRequiresFullScreen'] = false;

    // Nested dictionary
    plist['NSAppTransportSecurity'] = {
      NSAllowsArbitraryLoads: false,
      NSExceptionDomains: {
        'localhost': { NSExceptionAllowsInsecureHTTPLoads: true }
      }
    };

    return iosConfig;
  });
};

Background Modes on iOS

iOS apps that need to run code while backgrounded must declare UIBackgroundModes in Info.plist. Supported modes include audio, location, fetch, remote-notification, processing, and voip. Without the correct background mode declared, iOS suspends the app when it leaves the foreground and your background task is silently killed.

const { withInfoPlist } = require('@expo/config-plugins');

module.exports = function withBackgroundModes(config, { modes }) {
  return withInfoPlist(config, (iosConfig) => {
    const plist = iosConfig.modResults;

    if (!plist['UIBackgroundModes']) plist['UIBackgroundModes'] = [];

    for (const mode of modes) {
      if (!plist['UIBackgroundModes'].includes(mode)) {
        plist['UIBackgroundModes'].push(mode);
      }
    }

    return iosConfig;
  });
};

// app.json usage
// ["./plugins/withBackgroundModes", { "modes": ["audio", "fetch"] }]

Verifying Plugin Changes with plutil and grep

After running expo prebuild, always verify your changes landed correctly before building. On macOS, use plutil -p ios/YourApp/Info.plist to pretty-print the plist. Use grep to search AndroidManifest for specific strings. Add this verification as part of your CI pipeline so a broken plugin is caught before wasted build minutes.

# Verify iOS Info.plist
npx expo prebuild --clean --platform ios
plutil -p ios/MyApp/Info.plist | grep NSCamera
# -> "NSCameraUsageDescription" => "Scan QR codes."

# Verify Android manifest permissions
grep 'CAMERA' android/app/src/main/AndroidManifest.xml
# -> <uses-permission android:name="android.permission.CAMERA"/>

# Verify meta-data
grep 'geo.API_KEY' android/app/src/main/AndroidManifest.xml

Idempotency: Safe to Run Multiple Times

A well-written config plugin is idempotent — running it multiple times produces the same result as running it once. Guard every insertion with a check for existing values before adding them. Use filter to remove old values before inserting the new one (replace pattern) rather than blindly pushing, which would accumulate duplicates across repeated prebuild runs.

// Bad - adds duplicate permissions on every prebuild
manifest['uses-permission'].push({
  '$': { 'android:name': 'android.permission.CAMERA' }
});

// Good - idempotent check first
const hasCameraPermission = manifest['uses-permission']?.some(
  (p) => p.$['android:name'] === 'android.permission.CAMERA'
);
if (!hasCameraPermission) {
  AndroidConfig.Permissions.addUsesPermission(
    manifest, 'android.permission.CAMERA'
  );
}

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: how AndroidManifest.xml and Info.plist control permissions, features, and OS integrations, how to use withAndroidManifest to add permissions and meta-data elements, and how to use withInfoPlist to set usage descriptions, URL types, and background modes. You also learned why idempotency matters for plugins that run on every prebuild. Next up we learn how to distribute config plugins as npm packages.

Gratis untuk memulai

Belajar JavaScript dengan tutor AI — gratis

Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.

Kursus
30
Pelajaran
120

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Memodifikasi AndroidManifest dan Info.plist” gratis?

Ya — teks lengkap “Memodifikasi AndroidManifest dan Info.plist” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus React Native Academy, upgrade ke CoddyKit PRO. Kursus React Native Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Memodifikasi AndroidManifest dan Info.plist”?

Gunakan withAndroidManifest untuk menambahkan elemen izin dan metadata, serta withInfoPlist untuk menambahkan kunci seperti NSCameraUsageDescription, lalu uji di kedua platform. Kamu berlatih React Native Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai React Native Academy?

Tidak diperlukan pengalaman sebelumnya. React Native Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Memodifikasi AndroidManifest dan Info.plist” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran React Native Academy ini?

Ya. Setiap pelajaran React Native Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Apa Itu Plugin Konfigurasi dan Kapan Menggunakannya
  2. Menulis Plugin Konfigurasi Pertama Anda
  3. Memodifikasi AndroidManifest dan Info.plist
  4. Mendistribusikan Plugin Konfigurasi sebagai Paket npm
← Kembali ke React Native Academy