包详细信息

react-native-nitro-screen-recorder

A library to capture screen recordings with react-native powered by NitroModules.

react-native, ios, android, react-native-nitro-screen-recorder

自述文件

React Native Nitro Screen Recorder

react-native-nitro-screen-recorder

⚠️ This package is currently in alpha and under active development. Please report any issues that you run across on either platform.

A React Native library powered by NitroModules that provides comprehensive screen recording capabilities for both iOS and Android. Capture in-app content or global screen recordings with camera overlay support, audio recording, and extensive customization options.

Features

  • In-App Recording (iOS only) - Record your app's content with camera overlay
  • Global Screen Recording - System-wide screen capture (iOS & Android)
  • Camera Integration - Front/back camera overlay with customizable positioning
  • Audio Recording - Microphone support with permission management
  • Event Listeners - Real-time recording status updates
  • File Management - Automatic file handling and cache management
  • Permission Management - Built-in camera and microphone permission handling
  • React Hooks - Convenient hooks for permissions and global recording management

Demo

iOS In-App Recording iOS Global Recording Android Global Recording
IOS_In-App (With Camera) IOS_Global Android_Global

Installation

Using npm:

npm install react-native-nitro-screen-recorder react-native-nitro-modules

Using yarn:

yarn add react-native-nitro-screen-recorder react-native-nitro-modules

react-native-nitro-modules is required as this library relies on Nitro Modules.

Configuration

This library includes an Expo config plugin for automatic native configuration.

<summary>

Using Expo

</summary> Add the plugin to your app.config.js or app.json: js export default { expo: { plugins: [ [ 'react-native-nitro-screen-recorder', { enableCameraPermission: true, cameraPermissionText: 'Allow $(PRODUCT_NAME) to access your camera for screen recording with camera overlay', enableMicrophonePermission: true, microphonePermissionText: 'Allow $(PRODUCT_NAME) to access your microphone for screen recording with audio', iosBroadcastExtensionTargetName: "ScreenRecorder", iosAppGroupIdentifier: 'group.com.yourcompany.yourapp.screenrecording', iosExtensionBundleIdentifier: 'com.yourcompany.yourapp.BroadcastExtension', showPluginLogs: false, }, ], ], }, }; #### Plugin Configuration Options | Option | Type | Platform | Default | Description | | :----------------------------- | :-------- | :----------- | :--------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | | enableCameraPermission | boolean | iOS | true | Whether to enable camera permission for screen recording with camera overlay | | cameraPermissionText | string | iOS | "Allow $(PRODUCT_NAME) to access your camera for screen recording with camera overlay" | Camera permission description text displayed in iOS permission dialog | | enableMicrophonePermission | boolean | iOS, Android | true | Whether to enable microphone permission for screen recording with audio capture | | microphonePermissionText | string | iOS | "Allow $(PRODUCT_NAME) to access your microphone for screen recording with audio" | Microphone permission description text displayed in iOS permission dialog | | disableExperimental | boolean | iOS | false | Whether to disable the experimental Expo appExtensions configuration. When true, skips applying the broadcast extension configuration | | iosBroadcastExtensionTargetName | string | iOS | BroadcastExtension | The ability to customize the Target Name of the ios Broadcast Extension. | iosExtensionBundleIdentifier | string | iOS | "${PRODUCT_BUNDLE_IDENTIFIER}.BroadcastExtension" | The ability to customize the Broadcast Extension Bundle Identifier. | | iosAppGroupIdentifier | string | iOS | "group.${PRODUCT_BUNDLE_IDENTIFIER}.screen-recording" | App Group identifier used to share data between the main app and its extensions | | showPluginLogs | boolean | iOS, Android | false | Whether to display detailed plugin logs during the build process |
<summary>

Using Bare Workflow (Non-Expo)

</summary> If you're using a bare React Native project (not using Expo), you'll need to manually configure the native iOS and Android projects. ## iOS Setup ### 1. Add Permissions to Info.plist Add the following permissions to your ios/YourApp/Info.plist: xml <key>NSCameraUsageDescription</key> <string>Allow $(PRODUCT_NAME) to access your camera for screen recording with camera overlay</string> <key>NSMicrophoneUsageDescription</key> <string>Allow $(PRODUCT_NAME) to access your microphone for screen recording with audio</string> ### 2. Create App Group 1. Open your project in Xcode 2. Select your main app target 3. Go to Signing & Capabilities 4. Click + Capability and add App Groups 5. Create a new app group with identifier: group.com.yourcompany.yourapp.screenrecording 6. Add the App Group identifier to your Info.plist: xml <key>AppGroupIdentifier</key> <string>group.com.yourcompany.yourapp.screenrecording</string> ### 3. Create Broadcast Upload Extension 1. In Xcode, go to File → New → Target 2. Choose Broadcast Upload Extension 3. Name it BroadcastExtension 4. Set the bundle identifier to com.yourcompany.yourapp.BroadcastExtension ### 4. Configure Extension Files 1. Copy SampleHandler.swift from node_modules/react-native-nitro-screen-recorder/plugin/src/ios/SampleHandler.swift to your BroadcastExtension/ folder 2. Copy BroadcastWriter.swift from node_modules/react-native-nitro-screen-recorder/plugin/src/ios/BroadcastWriter.swift to your BroadcastExtension/ folder 3. Update the following values in SampleHandler.swift: - Replace <GROUPIDENTIFIER> with your app group identifier (e.g., group.com.yourcompany.yourapp.screenrecording) - Replace <SCHEME> with your app's custom URL scheme ### 5. Configure Extension Settings 1. Select the BroadcastExtension target in Xcode 2. Go to Signing & Capabilities 3. Add App Groups capability 4. Select the same app group you created earlier 5. Set the Deployment Target to match your main app 6. Ensure ReplayKit.framework is linked in Build Phases → Link Binary With Libraries ### 6. Update Extension Info.plist Update BroadcastExtension/Info.plist: xml <key>NSExtension</key> <dict> <key>NSExtensionPointIdentifier</key> <string>com.apple.broadcast-services-upload</string> <key>NSExtensionPrincipalClass</key> <string>$(PRODUCT_MODULE_NAME).SampleHandler</string> <key>RPBroadcastProcessMode</key> <string>RPBroadcastProcessModeSampleBuffer</string> </dict> <key>AppGroupIdentifier</key> <string>group.com.yourcompany.yourapp.screenrecording</string> ### 7. Create Extension Entitlements Create BroadcastExtension/BroadcastExtension.entitlements: xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.application-groups</key> <array> <string>group.com.yourcompany.yourapp.screenrecording</string> </array> </dict> </plist> Then in your extension target's Build Settings, set Code Signing Entitlements to BroadcastExtension/BroadcastExtension.entitlements. ## Android Setup ### 1. Add Permissions to AndroidManifest.xml Add the following permissions to android/app/src/main/AndroidManifest.xml: xml <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> ### 2. Add Service Declaration Add the screen recording service to your AndroidManifest.xml inside the <application> tag: xml <service android:name="com.margelo.nitro.nitroscreenrecorder.ScreenRecordingService" android:enabled="true" android:exported="false" android:foregroundServiceType="mediaProjection" /> ### 3. Update MainActivity Add activity result handling to your MainActivity.java or MainActivity.kt: #### For Java (MainActivity.java): java import android.content.Intent; import com.margelo.nitro.nitroscreenrecorder.NitroScreenRecorder; import android.util.Log; @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.d("MainActivity", "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode); try { NitroScreenRecorder.handleActivityResult(requestCode, resultCode, data); } catch (Exception e) { Log.e("MainActivity", "Error handling activity result: " + e.getMessage()); e.printStackTrace(); } } #### For Kotlin (MainActivity.kt): kotlin import com.margelo.nitro.nitroscreenrecorder.NitroScreenRecorder import android.content.Intent import android.util.Log override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) Log.d("MainActivity", "onActivityResult: requestCode=$requestCode, resultCode=$resultCode") try { NitroScreenRecorder.handleActivityResult(requestCode, resultCode, data); } catch (e: Exception) { Log.e("MainActivity", "Error handling activity result: ${e.message}") e.printStackTrace() } } ## Important Notes - Replace group.com.yourcompany.yourapp.screenrecording with your actual app group identifier - Replace com.yourcompany.yourapp with your actual bundle identifier - Ensure both your main app and broadcast extension have the same App Group configured - Test thoroughly on physical devices as screen recording doesn't work in simulators - Make sure your app has a custom URL scheme configured for deep linking

Verification

After completing these steps:

  1. Build and run your app on a physical device
  2. Test global screen recording functionality
  3. Verify that recorded files are properly saved and accessible
  4. Check that permissions are properly requested when needed

Quick Start Example

Here's a complete example using the new useGlobalRecording hook and updated stopGlobalRecording:

import React from 'react';
import { View, Text, Button, Alert } from 'react-native';
import {
  useGlobalRecording,
  useMicrophonePermission,
  startGlobalRecording,
  stopGlobalRecording,
} from 'react-native-nitro-screen-recorder';

export default function ScreenRecorderExample() {
  const { hasPermission, requestPermission } = useMicrophonePermission();

  const { isRecording } = useGlobalRecording({
    onRecordingStarted: () => {
      Alert.alert('Recording started');
    },
    onRecordingFinished: async (file) => {
      if (file) {
        Alert.alert(
          'Recording Complete!',
          `Saved: ${file.name}\nDuration: ${file.duration}s\nSize: ${file.size} bytes`
        );
        // e.g., uploadRecording(file.path)
      } else {
        Alert.alert('Recording Complete', 'Failed to retrieve the file.');
      }
    },
    settledTimeMs: 700, // optional delay before retrieving the file
  });

  const handleStartRecording = async () => {
    if (!hasPermission) {
      const granted = await requestPermission();
      if (!granted) {
        Alert.alert(
          'Permission Required',
          'Microphone permission is needed for audio recording'
        );
        return;
      }
    }

    startGlobalRecording({
      enableMic: true,
      onRecordingError: (error) => {
        Alert.alert('Global recording error', error.message);
      },
    });
  };

  const handleStopRecording = async () => {
    const file = await stopGlobalRecording({ settledTimeMs: 1000 });
    if (file) {
      console.log('Stopped and retrieved file:', file);
    }
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
      <Text style={{ fontSize: 18, marginBottom: 20, textAlign: 'center' }}>
        Screen Recorder Demo
      </Text>

      <Button title="Start Global Recording" onPress={handleStartRecording} />
      <Button title="Stop Recording" onPress={handleStopRecording} />

      {isRecording && (
        <Text style={{ marginTop: 10 }}>Recording is active…</Text>
      )}
    </View>
  );
}

Documentation

Table of Contents

React Hooks

useCameraPermission(): PermissionState

Returns whether the user has granted permission to use the Camera, or not. If the user doesn't grant Camera Permission, you cannot use camera overlay features.

Platform: iOS, Android

Returns: Object with hasPermission boolean and requestPermission function

Example:

import { useCameraPermission } from 'react-native-nitro-screen-recorder';

const { hasPermission, requestPermission } = useCameraPermission();

if (!hasPermission) {
  return <PermissionScreen onPress={requestPermission} />;
} else {
  return <CameraRecordingScreen />;
}

useMicrophonePermission(): PermissionState

Returns whether the user has granted permission to use the Microphone, or not. If the user doesn't grant Audio Permission, you can still record but without audio.

Platform: iOS, Android

Returns: Object with hasPermission boolean and requestPermission function

Example:

import { useMicrophonePermission } from 'react-native-nitro-screen-recorder';

const { hasPermission: canRecordAudio, requestPermission } =
  useMicrophonePermission();

// Use in recording configuration
const recordingOptions = {
  enableMic: canRecordAudio,
  enableCamera: true,
  // ... other options
};

useGlobalRecording(input): GlobalRecordingHookOutput

React hook for monitoring and responding to global screen recording events.

Platform: iOS, Android

Parameters:

  • onRecordingStarted?: () => void — Called when a global recording begins.
  • onRecordingFinished?: (file?: ScreenRecordingFile) => void — Called after recording ends (with a delay to allow the file to settle).
  • onBroadcastPickerShown?: () => void — Called when the broadcast picker on ios is shown.
  • onBroadcastPickerDismissed?: () => void — Called with the broadcast picker on ios is dismissed.
  • ignoreRecordingsInitiatedElsewhere?: boolean -iOS-onlyallows the listener to only callback when thestartGlobalRecording` is called.
  • settledTimeMs?: number — Milliseconds to wait after recording end before attempting to retrieve the file. Defaults to 500.

Returns: { isRecording: boolean } — whether a global recording is currently active.

Example:

import { useGlobalRecording } from 'react-native-nitro-screen-recorder';

const { isRecording } = useGlobalRecording({
  onRecordingStarted: () => console.log('started'),
  onRecordingFinished: (file) => {
    if (file) {
      console.log('finished:', file.path);
    }
  },
  onBroadcastPickerShown: () => {
    console.log('Perform some action');
  },
  onBroadcastPickerDismissed: () => {
    console.log('Perform some other action');
  },
  ignoreRecordingsInitiatedElsewhere: false,
  settledTimeMs: 600,
});

Permissions

getCameraPermissionStatus(): PermissionStatus

Gets the current camera permission status without requesting permission.

Platform: iOS, Android

Returns: The current permission status for camera access

Example:

import { getCameraPermissionStatus } from 'react-native-nitro-screen-recorder';

const status = getCameraPermissionStatus();
if (status === 'granted') {
  // Camera is available
}

getMicrophonePermissionStatus(): PermissionStatus

Gets the current microphone permission status without requesting permission.

Platform: iOS, Android

Returns: The current permission status for microphone access

Example:

import { getMicrophonePermissionStatus } from 'react-native-nitro-screen-recorder';

const status = getMicrophonePermissionStatus();
if (status === 'granted') {
  // Microphone is available
}

requestCameraPermission(): Promise<PermissionResponse>

Requests camera permission from the user if not already granted. Shows the system permission dialog if permission hasn't been determined.

Platform: iOS, Android

Returns: Promise that resolves with the permission response

Example:

import { requestCameraPermission } from 'react-native-nitro-screen-recorder';

const response = await requestCameraPermission();
if (response.status === 'granted') {
  // Permission granted, can use camera
}

requestMicrophonePermission(): Promise<PermissionResponse>

Requests microphone permission from the user if not already granted. Shows the system permission dialog if permission hasn't been determined.

Platform: iOS, Android

Returns: Promise that resolves with the permission response

Example:

import { requestMicrophonePermission } from 'react-native-nitro-screen-recorder';

const response = await requestMicrophonePermission();
if (response.status === 'granted') {
  // Permission granted, can record audio
}

In-App Recording

startInAppRecording(input): Promise<void>

Starts in-app screen recording with the specified configuration. Records only the current app's content, not system-wide screen content.

Platform: iOS only

Parameters:

  • enableMic: boolean - Whether to enable microphone audio
  • enableCamera: boolean - Whether to enable camera overlay
  • cameraPreviewStyle: RecorderCameraStyle - Camera positioning and styling
  • cameraDevice: CameraDevice - Front or back camera
  • onRecordingFinished: (file: ScreenRecordingFile) => void - Callback when recording completes

Example:

import { startInAppRecording } from 'react-native-nitro-screen-recorder';

await startInAppRecording({
  enableMic: true,
  enableCamera: true,
  cameraPreviewStyle: { width: 100, height: 150, top: 30, left: 10 },
  cameraDevice: 'front',
  onRecordingFinished: (file) => {
    console.log('Recording saved:', file.path);
  },
});

stopInAppRecording(): Promise<ScreenRecordingFile | undefined>

Stops the current in-app recording and returns the recorded video file. The recording file is also provided through the onRecordingFinished callback.

Platform: iOS only

Returns: Promise that resolves with the recording file or undefined if no recording was active

Example:

import { stopInAppRecording } from 'react-native-nitro-screen-recorder';

const file = await stopInAppRecording();
if (file) {
  console.log('Recording stopped and saved:', file.path);
}

cancelInAppRecording(): Promise<void>

Cancels the current in-app recording without saving the video. No file will be generated and onRecordingFinished will not be called.

Platform: iOS only

Example:

import { cancelInAppRecording } from 'react-native-nitro-screen-recorder';

await cancelInAppRecording(); // Recording discarded, no file saved

Global Recording

startGlobalRecording(input): void

Starts global screen recording that captures the entire device screen. Records system-wide content, including other apps and system UI.

Platform: iOS, Android

Parameters:

  • enableMic: boolean - Whether to enable microphone audio
  • onRecordingError: (error: RecordingError) => void - Error callback

Throws:

  • Error: If microphone permission is not granted on Android when enableMic is true.

Example:

import { startGlobalRecording } from 'react-native-nitro-screen-recorder';

startGlobalRecording({
  enableMic: true, // enableMic
  onRecordingError: (error) => {
    console.error('Global recording error:', error.message);
  },
});

stopGlobalRecording(options?): Promise<ScreenRecordingFile | undefined>

Stops the current global screen recording and returns the saved video file. Because the system may take a short moment to finalize the asset writer output, you can pass an optional delay before retrieval.

Platform: iOS, Android

Parameters:

  • options.settledTimeMs?: number — Milliseconds to wait after the broadcast ends before attempting to retrieve the file. Defaults to 500.

Example:

import { stopGlobalRecording } from 'react-native-nitro-screen-recorder';

const file = await stopGlobalRecording({ settledTimeMs: 1000 });
if (file) {
  console.log('Global recording saved:', file.path);
}

retrieveLastGlobalRecording(): ScreenRecordingFile | undefined

Retrieves the most recently completed global recording file. Returns undefined if no global recording has been completed.

Platform: iOS, Android

Returns: The last global recording file or undefined if none exists

Example:

import { retrieveLastGlobalRecording } from 'react-native-nitro-screen-recorder';

const lastRecording = retrieveLastGlobalRecording();
if (lastRecording) {
  console.log('Duration:', lastRecording.duration);
  console.log('File size:', lastRecording.size);
}

Event Listeners

addScreenRecordingListener(listener): () => void

Adds a listener for screen recording events (began, ended, etc.). Returns a cleanup function to remove the listener when no longer needed.

Platform: iOS, Android

Parameters:

  • listener: Callback function that receives screen recording events
  • ignoreRecordingsInitiatedElsewhere?: boolean -iOS-onlyallows the listener to only callback when thestartGlobalRecording` is called.

Returns: Cleanup function to remove the listener

Example:

import { useEffect } from 'react';
import { addScreenRecordingListener } from 'react-native-nitro-screen-recorder';

useEffect(() => {
  const removeListener = addScreenRecordingListener({
    ignoreRecordingsInitiatedElsewhere: false,
    listener: (event) => {
      console.log('Event type:', event.type, 'Event reason:', event.reason);
    },
  });

  // Clean up listener when component unmounts
  return removeListener;
}, []);

addBroadcastPickerListener(listener): () => void

Adds a listener for iOS broadcast picker status changes (showing & dismissed). Returns a cleanup function to remove the listener when no longer needed. This helps when trying to perform some action on iOS as soon as the broadcast picker is dismissed.

Platform: iOS only (returns no-op cleanup function on Android)

Parameters:

  • listener: Callback function that receives broadcast picker presentation events

Returns: Cleanup function to remove the listener

Example:

import { useEffect } from 'react';
import { addBroadcastPickerListener } from 'react-native-nitro-screen-recorder';

useEffect(() => {
  const removeListener = addBroadcastPickerListener((event) => {
    console.log('Picker status:', event);

    switch (event) {
      case 'showing':
        console.log('Broadcast picker is showing');
        break;
      case 'dismissed':
        console.log(
          'Broadcast picker was dismissed without starting recording'
        );
        break;
    }
  });

  // Clean up listener when component unmounts
  return removeListener;
}, []);

Event Types:

  • showing: The broadcast picker modal is displayed to the user
  • dismissed: The broadcast modal was dismissed without starting recording

Utilities

clearRecordingCache(): void

Clears all cached recording files to free up storage space. This will delete temporary files but not files that have been explicitly saved.

Platform: iOS, Android

Example:

import { clearRecordingCache } from 'react-native-nitro-screen-recorder';

clearRecordingCache(); // Frees up storage by removing temporary recording files

Types

The library exports comprehensive TypeScript types for all functionality:

// Permission types
export type PermissionStatus = 'denied' | 'granted' | 'undetermined';

export type PermissionResponse = {
  canAskAgain: boolean;
  granted: boolean;
  status: PermissionStatus;
  expiresAt: never | number;
};

// Hook types
export interface PermissionState {
  hasPermission: boolean;
  requestPermission: () => Promise<boolean>;
}

export type GlobalRecordingHookInput = {
  onRecordingStarted?: () => void;
  onRecordingFinished?: (file?: ScreenRecordingFile) => void;
  settledTimeMs?: number;
};

export type GlobalRecordingHookOutput = {
  isRecording: boolean;
};

// Recording configuration
export type RecorderCameraStyle = {
  top?: number;
  left?: number;
  width?: number;
  height?: number;
  borderRadius?: number;
  borderWidth?: number;
};

export type CameraDevice = 'front' | 'back';

// Recording file information
export interface ScreenRecordingFile {
  path: string;
  name: string;
  size: number;
  duration: number;
  enabledMicrophone: boolean;
}

// Event types
export interface ScreenRecordingEvent {
  type: 'global' | 'withinApp';
  reason: 'began' | 'ended';
}

export interface RecordingError {
  name: string;
  message: string;
}

Platform Differences

iOS

  • In-App Recording: Full support with camera overlay
  • Global Recording: Full programmatic control including start and stop functionality
  • Permissions: Camera and microphone permissions handled automatically
  • App Extensions: Uses broadcast extensions for global recording

Android

  • In-App Recording: Not supported (use global recording instead)
  • Global Recording: Full programmatic control including stop functionality
  • Permissions: Microphone permission required for audio recording
  • Media Projection: Uses Android's MediaProjection API

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT


Made with create-react-native-library

更新日志

Changelog

[0.3.8] - 2025-09-8

Changes

  • Fix #9, privacyinfo naming conflict with other expo config plugins

[0.3.7] - 2025-08-25

Changes

  • Bump nitro modules to 0.29.3 (latest)
  • Fix Gradle Build error

[0.3.6] - 2025-08-25

Changes

  • Bump nitro modules to 0.29.2 (latest)

[0.3.5] - 2025-08-25

Changes

  • Bump nitro modules to 0.28.1 (latest)

[0.3.4] - 2025-08-12

Changes

  • Fix expo plugin typescript
  • Allow customizing the screen recorder target name on iOS
  • Bump nitro modules to 0.28.0 (latest)

[0.3.2] - 2025-08-12

Changes

  • Fix expo plugin typescript
  • Close #7 - ignore external screen recording property to the useGlobalRecording hook
  • Bump nitro modules to 0.27.6 (latest)

[0.2.8] - 2025-08-02

Feat

  • Allow custom iosBundleIdentifier for BroadcastExtension

[0.2.7] - 2025-08-02

Feat

  • Rewrite useGlobalRecording hook
  • Add adjustable settledTimeMs to stopGlobalRecording function

Chore

  • Update README.md
  • Bump react-native-nitro-modules to 0.27.3
  • Update keywords on package.json

[0.1.9] - [0.2.5] - 2025-08-02

Fix

  • Rewrite of app group entitlements file, wasn't applying correctly to the main app

[0.1.8] - 2025-08-02

Fix

  • Complete rewrite of config plugin to make files link properly on ios
  • Successful hack to get stopGlobalRecording work on iOS
  • Fix multiple bugs on iOS

[0.1.6] - 2025-07-31

Fix

  • Attempt #6: Fix build error with expo config plugin

[0.1.5] - 2025-07-31

Fix

  • Attempt #5: Fix build error with expo config plugin

[0.1.4] - 2025-07-31

Fix

  • Attempt #4: Fix build error with expo config plugin

[0.1.3] - 2025-07-31

Fix

  • Attempt #3: Typescript error in expo config plugin

[0.1.2] - 2025-07-31

Fix

  • Attempt #2: Typescript error in expo config plugin

[0.1.1] - 2025-07-30

Fix

  • Typescript error in expo config plugin

[0.1.0] - 2025-07-30

Added

  • Initial release