UNPKG

react-native-code-sync

Version:

React-native plugin for the code-sync functionality on live via OTA (Over-the-air)

487 lines (386 loc) 17.7 kB
# React Native Code Sync React-native plugin for the code-sync functionality on live via OTA (Over-the-air) A lightweight, developer-friendly plugin for real-time OTA (Over-the-Air) code syncing in React Native. It enables fast iteration with zero native code recompilation — perfect for rapid development and live updates. --- ## 🚀 Features - 🔄 Real-time code syncing for React Native - ⚡ Hot reload without recompiling native code - ☁️ Supports Over-the-Air (OTA) updates - ✅ Compatible with both Android & iOS - 🔧 Easy integration with existing projects --- ## 📦 Installation Bare React Native ### Step 1: Install the package Using npm: ```bash npm install react-native-code-sync ``` Using yarn: ```bash yarn add react-native-code-sync ``` ### Step 2: Install peer dependencies This plugin requires the following packages: For more information, about libraries please visit these sites - [@react-native-async-storage/async-storage](https://react-native-async-storage.github.io/async-storage/docs/install/). - [axios](https://www.npmjs.com/package/axios) - [form-data](https://www.npmjs.com/package/form-data) - [react-native-blob-util](https://www.npmjs.com/package/react-native-blob-util) - [react-native-device-info](https://www.npmjs.com/package/react-native-device-info) - [react-native-fs](https://www.npmjs.com/package/react-native-fs) - [react-native-restart](https://www.npmjs.com/package/react-native-restart) - [react-native-zip-archive](https://www.npmjs.com/package/react-native-zip-archive) Using npm ```bash npm install @react-native-async-storage/async-storage axios form-data react-native-blob-util react-native-device-info react-native-fs react-native-restart react-native-zip-archive ``` Using yarn ```bash yarn add @react-native-async-storage/async-storage axios form-data react-native-blob-util react-native-device-info react-native-fs react-native-restart react-native-zip-archive ``` ## Native Setup #### Android In your MainApplication.kt, override the getJSBundleFile() method: ```java import java.io.File class MainApplication : MultiDexApplication(), ReactApplication { override val reactNativeHost: ReactNativeHost = object : DefaultReactNativeHost(this) { override fun getPackages(): List<ReactPackage> = PackageList(this).packages.apply { // Packages that cannot be autolinked yet can be added manually here, for example: // add(MyReactNativePackage()) } override fun getJSMainModuleName(): String = "index" // Add this function in your code override fun getJSBundleFile(): String? { val otaBundle = File(applicationContext.filesDir, "index.android.bundle") return if (otaBundle.exists()) otaBundle.absolutePath else "assets://index.android.bundle" } } ``` #### iOS Inside Appdelegate.mm file add this function of code ```objc - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { return [self getBundleURL]; } - (NSURL *)getBundleURL { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *bundlePath = [self getUpdatedBundlePath] ; if ([fileManager fileExistsAtPath:bundlePath]) { return [NSURL fileURLWithPath:bundlePath]; } else { return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; } #endif } // Get Path for Updated Bundle - (NSString *)getUpdatedBundlePath { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths firstObject]; return [documentsDirectory stringByAppendingPathComponent:@"main.jsbundle"]; } ``` ## Environment File Configuration ```javascript "CODE_SYNC_BASE_URL": "https://yourBaseUrl.com/", "CHECK_FOR_UPDATE": "your/check_for_update_api/path/endpoint", "UPLOAD_BUNDLE": "your/upload_bundle_api/path/endpoint" ``` > ⚠️ **NOTE:** Your environment file should contain "CODE_SYNC_BASE_URL" and "CHECK_FOR_UPDATE". Here, keyname must be same as mentioned here. ## Usage (Javascript) Value of "baseUrl" and "checkForUpdateEndPoint" in setCodeSyncConfig method is required, without these keys "checkForCodeSync" will not work. Make sure you pass ```javascript // Use this in App.js/index.js or before using and other method of this library import { setCodeSyncConfig } from 'react-native-code-sync' setCodeSyncConfig({ enableInDevMode: false, baseUrl: "https://yourBaseUrl.com/", checkForUpdateEndPoint: "your/api/path/endpoint" }) ``` ### Step 1: Add scripts in package.json Add this in your package.json file inside script block ENVFILE variable will be the name of your environment file you are using in your project If using [react-native-keys](https://www.npmjs.com/package/react-native-keys) for environment management ```javascript "scripts":{ "android:code-sync": "ENVFILE=keys.project.json ENV_KEY=KEYSFILE npx android-code-sync", "ios:code-sync": "ENVFILE=keys.project.json npx ios-code-sync" } ``` If using [react-native-config](https://www.npmjs.com/package/react-native-config) for environment management ```javascript "scripts":{ "android:code-sync": "ENVFILE=.env ENV_KEY=ENVFILE npx android-code-sync", "ios:code-sync": "ENVFILE=.env npx ios-code-sync" } ``` > ⚠️ **NOTE:** Do not remove this statement: `npx android-code-sync` for android and `npx ios-code-sync` for iOS #### Functional Component Example ```javascript import { useCheckCodeSync, CodeSyncModal, restartApp } from 'react-native-code-sync' let timeout const App = () => { const { checkForCodeSync } = useCheckCodeSync() const [OTAVisibleModal, setOTAVisibleModal] = useState(false) const [otaProgress, setOTAProgress] = useState(0) const [otaUpdateError, setOTAUpdateError] = useState(false) useEffect(() => { fetchAvailableUpdate() }, []) const fetchAvailableUpdate = async () => { try { const isAvailable = await checkForCodeSync({ params: { project_key: "Projectkey", version: "1.0.0" }, onDownloadProgress: value => setOTAProgress(value), onStartUpdate: () => { if (timeout) clearTimeout(timeout) timeout = setTimeout(() => { timeout = null setOTAVisibleModal(true) }, 0); }, onUpdateFailed: () => { navigateToScreen() }, onUpdateNotAvailable: navigateToScreen }) if (!isAvailable) navigateToScreen() } catch (err) { Utility.log("catch error=>", JSON.stringify(err)) Utility.showAPIError(err) navigateToScreen() } } const navigateToScreen = () => { // Contimue to mobile app } const onOtaUpdateCancelPress=()=>{ } const onOtaUpdateRestartPress=()=>{ restartApp() } return ( <CodeSyncModal visible={OTAVisibleModal} codeSyncProgress={otaProgress} onCancelPress={onOtaUpdateCancelPress} onRestartPress={onOtaUpdateRestartPress} themeColor={COLORS.THEME} appLogo={SVG_ICONS.appLogo()} messageStyle={{ fontFamily: FONT_FAMILY.POPPINS_REGULAR }} restartButtonLabelStyle={{ fontFamily: FONT_FAMILY.POPPINS_MEDIUM }} percentageTextStyle={{ fontFamily: FONT_FAMILY.POPPINS_REGULAR }} downloadErrorMessage={"Error while parsing package"} downloadCompleteMessage={"Your app has downloaded the latest package"} downloadingInProgressMessage={"Please wait we are downloading updated package"} restartButtonLabel={"Restart Now"} /> ) } ``` #### Class Component Example ```javascript import React, { Component } from 'react' import { View } from 'react-native' import { CheckCodeSyncClass, CodeSyncModal, restartApp } from 'react-native-code-sync' let timeout class App extends Component { constructor(props) { super(props); this.timeout = null this.state = { OTAVisibleModal: false, otaProgress: 0, otaUpdateError: false } } componentDidMount() { this.fetchAvailableUpdate() } fetchAvailableUpdate = async () => { try { const checkCodeSync = new CheckCodeSyncClass() const isAvailable = await checkCodeSync.checkForCodeSync({ params: { project_key: "Projectkey", version: "1.0.0" }, onDownloadProgress: value => this.setState({ otaProgress: value }), onStartUpdate: () => { if (this.timeout) clearTimeout(this.timeout) this.timeout = setTimeout(() => { this.timeout = null this.setState({ OTAVisibleModal: true }) }, 0); }, onUpdateFailed: () => { this.navigateToScreen() }, onUpdateNotAvailable: this.navigateToScreen }) if (!isAvailable) this.navigateToScreen() } catch (err) { console.log("catch error=>", JSON.stringify(err)) this.navigateToScreen() } } navigateToScreen = () => { // Contimue to mobile app } onOtaUpdateCancelPress = () => { } onOtaUpdateRestartPress = () => { restartApp() } render() { return ( <CodeSyncModal visible={this.state.OTAVisibleModal} codeSyncProgress={this.state.otaProgress} onCancelPress={this.onOtaUpdateCancelPress} onRestartPress={this.onOtaUpdateRestartPress} themeColor={COLORS.THEME} appLogo={SVG_ICONS.appLogo()} messageStyle={{ fontFamily: FONT_FAMILY.POPPINS_REGULAR }} restartButtonLabelStyle={{ fontFamily: FONT_FAMILY.POPPINS_MEDIUM }} percentageTextStyle={{ fontFamily: FONT_FAMILY.POPPINS_REGULAR }} downloadErrorMessage={"Error while parsing package"} downloadCompleteMessage={"Your app has downloaded the latest package"} downloadingInProgressMessage={"Please wait we are downloading updated package"} restartButtonLabel={"Restart Now"} /> ) } } ``` ## Component/ Method/ Hooks/ Funcitons used in this react-native-code-sync - CodeSyncModal - CheckCodeSyncClass - useCheckCodeSync - restartApp ### CodeSyncModal Props | Prop | Type | Required | Default | Description | | -------------- | ---------- | -------- | ----------- | ------------------------------------------------------------- | | `codeSyncProgress` | `string` | ✅ | `null` | This is the download progress of bundle file from server | | `visible` | `boolean` | ✅ | `false` | This open the Modal of Code sync update | | `otaUpdateError` | `function` | ❌ | | This callback will get called when an error occured during code-sync update download | | `onCancelPress` | `function` | ❌ | | This callback is called when user pressed the cancel button of code-sync modal. | | `onRestartPress` | `function` | ❌ | | This is function which will be called on press of restart button after downloading bundle file. | | `appLogo` | `UI Element` | ❌ | null | If developer want to show the app icon on code-sync update modal, the pass the Image or SVG file to this prop | | `showCancelButton` | `boolean` | ❌ | `'false'` | This prop will show or hide "Cancel" button on code-sync update modal Options: `'true'`, `'false'`. | | `themeColor` | `string` | ❌ | `#0bbddd` | This is the theme color code for code-sync update modal | | `downloadCompleteMessage` | `string` | ❌ | `Your app has downloaded latest package, please restart your app` | This is the message to show on code-sync update modal, after downloading of bundle file | | `downloadingInProgressMessage` | `string` | ❌ | `Please wait we are downloading updated app package` | This is the message to show on code-sync update modal, during downloading of bundle file | | `downloadErrorMessage` | `string` | ❌ | `'Error while parsing bundle package'` | This is the message to show on code-sync update modal, on error occured of downloading of bundle file | | `cancelButtonLabel` | `string` | ❌ | `Cancel` | This is the label for cancel button on code-sync update modal. | | `restartButtonLabel` | `string` | ❌ | `Restart` | This is the label for restart button on code-sync update modal. | | `percentageTextStyle` | `Style Object` | ❌ | | This is the style for showing downloading percentage text on code-sync update modal. | | `messageStyle` | `Style Object` | ❌ | | This is the style for showing message text on code-sync update modal. | | `mainContaineStyle` | `Style Object` | ❌ | | This is the style for mainContainer on code-sync update modal. | | `cancelButtonLabelStyle` | `Style Object` | ❌ | | This is the style for showing "Cancel" button on code-sync update modal. | | `restartButtonLabelStyle` | `Style Object` | ❌ | | This is the style for showing "Restart" button on code-sync update modal. | | `customUI` | `Style Object` | ❌ | | This is the UI to show after download progress UI on code-sync update modal. | ### CheckCodeSyncClass Class This class has "checkForCodeSync" method to check for available update and this can only be used for class components. #### Usage ```javascript import { CheckCodeSyncClass } from 'react-native-code-sync' class App extends Components { componentDidMount(){ this.fetchUpdate() } fetchUpdate=()=>{ try { const codeSync = new CheckCodeSyncClass() const isAvailable = await codeSync.checkForCodeSync({ params: { project_key: "uniqueProjectKey", version: "1.0.1" }, onDownloadProgress: value => {console.log(value)}, onStartUpdate: () => { // Bundle download started }, onUpdateFailed: () => { // Bundle download get failed }, onUpdateNotAvailable: ()=>{ // Update not available } }) } catch(err){ console.log(err) } } } ``` ### useCheckCodeSync Hook This hook has "checkForCodeSync" method to check for available update and this can only be used for functional components. #### Usage ```javascript import { useCheckCodeSync } from 'react-native-code-sync' const App = ()=> { const { checkForCodeSync } = useCheckCodeSync() useEffect(()=>{ fetchUpdate() },[]) const fetchUpdate=async()=>{ try { const isAvailable = await checkForCodeSync({ params: { project_key: "uniqueProjectKey", version: "1.0.1" }, onDownloadProgress: value => {console.log(value)}, onStartUpdate: () => { // Bundle download started }, onUpdateFailed: () => { // Bundle download get failed }, onUpdateNotAvailable: ()=>{ // Update not available } }) } catch(err){ console.log(err) } } return( // Some UI ) } ``` > ⚠️ **NOTE:** This table shows the parameters for both 'useCheckCodeSync' hook and 'CheckCodeSyncClass' Class, both have the same parameters, the only difference is class and functional component - params - {project_key:"projectuniquekey", version: "Live app version"} | Parameters | Type | Required | Default | Description | | -------------- | ---------- | -------- | ----------- | ------------------------------------------------------------- | | `params` | `Object` | ✅ | `null` | This is the parameter for checking available update. | | `onDownloadProgress` | `callback` | ❌ | | This is the callback for download bundle file, and will return value of downloading progress of bundle file. | | `onStartUpdate` | `callback` | ❌ | | This is the callback called when downloading bundle started. | | `onUpdateFailed` | `callback` | ❌ | | This is the callback called when downloading of bundle file get failed. | | `onUpdateNotAvailable` | `function` | ❌ | | This is function which will be called when there is no update available. | ## restartApp Method This method is used to restart the mobile app to load bundle file from correct path, and load the latest bundle. #### Usage ```javascript import { restartApp } from 'react-native-code-sync' onPress={()=> restartApp()} ``` ## Support For support, email sandeepkumar2121997@gmail.com.