How to: build a controlled codepush in react-native app

Aishani Pachauri
3 min readJun 8, 2024

--

In react-native we have this blessing of sending over the air updates- via codepush, control when user see get to see these codepush changes!

Our team switched from flutter to react-native due to the simple reason of sending updates without a release on any marketplace like the Appstore or Playstore.

Codepush by Microsoft Appcenter, allows developers to send a bundle file over the internet which can be applied to the published react-native app. This enables companies to send updates that get applied automatically without having the user to update the app.

The issue is that as soon as the codepush is sent, it refreshes the app to reflect changes, which will be a pain for the user if they were, for an instance, in the middle of filling a form. All their progress goes to waste as the screen flickers and refreshes the app.

To tackle this, we used the flags/ props provided by codepush package which can help us send codepush updates when :

  1. User sends the app in the background
  2. User is prompted to give permission to install the updates and accepts it

In this article, we assume that the basic setup for a react-native app (native iOS and/or android changes) are already done.

Creating a wrapper for our App

Let’s create a file in the root directory, same level as the App.js file, called AppCodepushWrapper.js. This file will look like this:

import React, { useEffect, useState } from 'react';
import App from './App';
import codePush from 'react-native-code-push';
import { Platform } from 'react-native';
import {
CODEPUSH_ANDROID_PRODUCTION_DEPLOYMENT_KEY,
CODEPUSH_IOS_PRODUCTION_DEPLOYMENT_KEY,
} from './src/utils/constants';

const AppCodePushWrapper = () => {

return <App />;
};

export default __DEV__ ? App : codePush(AppCodePushWrapper);

Here we are checking if we are in development mode or not, to prevent from checking for an available codepush update in development mode. You can return just codePush(AppCodePushWrapper) if that is not required by you.

Now that we know how to add a basic codepush wrapper, let’s edit the same file to have different options of applying a codepush.

Adding codepush options to get updates when app state changes to in background

import React, { useEffect, useState } from 'react';
import App from './App';
import codePush from 'react-native-code-push';
import { Platform } from 'react-native';
import {
CODEPUSH_ANDROID_PRODUCTION_DEPLOYMENT_KEY,
CODEPUSH_IOS_PRODUCTION_DEPLOYMENT_KEY,
} from './src/utils/constants';

const codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL };

const AppWithCodePush = () => {
const [progress, setProgress] = useState(false);
const [codePushType, setCodepushType] = useState('background');

const checkUpdateAvailable = async () => {
const update = await codePush.checkForUpdate();
console.log(update);
if (update) {
return true;
} else return false;
};
const syncCodePush=(type='immediate')=>{
codePush.sync(
{
deploymentKey:
Platform.OS === 'ios'
? CODEPUSH_IOS_PRODUCTION_DEPLOYMENT_KEY
: CODEPUSH_ANDROID_PRODUCTION_DEPLOYMENT_KEY,
updateDialog: false,
installMode: type==='immediate'?
codePush.InstallMode.IMMEDIATE:
codePush.InstallMode.ON_NEXT_RESUME,
},
(syncStatus) => {
switch (syncStatus) {
//Can put log to see these stages and do some action meanwhile
case codePush.SyncStatus.CHECKING_FOR_UPDATE:
break;
case codePush.SyncStatus.DOWNLOADING_PACKAGE:
break;
case codePush.SyncStatus.INSTALLING_UPDATE:
break;
case codePush.SyncStatus.UPDATE_INSTALLED:
break;
}
}
);
}

useEffect(() => {
// This function should check if an update is available
codePush.getUpdateMetadata().then((metadata) => {
/
if (metadata.label == 'PERMIT') setCodepushType('permit');
else setCodepushType('background');
});

if (codePushType == 'permit') {
try {
const update = checkUpdateAvailable();
if (update) {
// This is to show loading state and prompt user if
// they want to install updates
setProgress(true);
if(codePushType==='permit'){
//show dialog box and if presses on a button 'permit'
const dialog= true
if(dialog) {syncCodePush();}
else {
//do something when no codepush downloaded here
}

}
} catch (error) {
console.error('CodePush update error:', error);
} finally {
setProgress(false);
}
else if(codePushType==='background'){
syncCodePush('background');
}
},[]);
return <App />;
};

export default __DEV__ ? App : codePush(codePushOptions)(AppWithCodePush);

Here we use the installMode option (codePush.InstallMode.ON_NEXT_RESUME) provided by codepush to apply the package on next app open.

Reimagining codepush and user journey impact for react-native apps in iOS & Android, design and flow for user experience and product folks (managers and designers)
Reimagining codepush and user journey impact for react-native apps in iOS & Android

From product to user experience, this feature has huge impact:

  1. We are keeping both android and iOS codepushes differently
  2. We can let user select if they want to download this specific update or not
  3. There is no drop-off or user flow exit due to abrupt app refresh on availability of a codepush for current version

--

--