DUS is a Over The Air (OTA) update system that allows React Native developers to deploy mobile app updates directly to the devices of the users. It allows you to use your own servers to maintain release patches and provides configurability in the way these patches are downloaded on your Native Apps.
Once a React Native application is released, updating the code involves a recompilation of the new code and releasing a fresh APK/IPA. The adoption of the new APK/IPA and the review time associated with the review process makes it difficult for your latest code to reach your entire users instantly.
DUS allows you to push your latest improvements/bug fixes to all your users instantly. This introduces the agility of web into your Native Applications. It also allows multiple teams to work on the same applications where each team maintains and releases their React Native bundles for their own screens without affecting other bundles.
During the update process, DUS only downloads the missing/changed components in your React Native code reducing the download size by ~90%. While React Native does not allow sharing of code across multiple bundles, DUS avoids redundant download of common code during the bundle creation process at the native clients.
Dus Deployer pulls the repositories specified in a configuration file called DeploymentConfig.json
creates a bundle for each repository, splits it into chunks and generates update patches which contains a config called Update Graph for each version of the Android/iOS application. These patches are then uploaded to the server and the chunks are uploaded to a key-value storage pair/CDN.
The update graph for a app version specifies the chunks to be combined to generate a specific bundle. During the launch of the application, a new update graph is downloaded. When a new bundle is to be generated it fetches the chunks required for the bundle from the cache, downloading the missing chunks and combines them to form a new bundle.
This ReactFoo video explains the working of DUS. A detailed guide on the workings of DUS can be found here(link)
Each version of React Native has a corresponding branch 0.\<react-native-version>-stable.
We are doing our best to support each React Native version and respond to new React Native releases. In most cases, if support for a particular React Native version is unavailable, the branch supporting the previous version should work.
React Native Version | DUS Version | Branch Name |
---|---|---|
0.47.x | 1.47.11 | 0.47-stable |
0.48.x | 1.48.3 | 0.48-stable |
0.49.x | 1.49.0 | 0.49-stable |
0.50.x | WIP | |
0.51.x | WIP | |
0.52.x | WIP |
npm install -g dus-deployer
NOTE: This guide assumes you have used the react-native init command to initialize your React Native project. As of March 2017, the command create-react-native-app can also be used to initialize a React Native project. If using this command, please run npm run eject in your project's home directory to get a project very similar to what react-native init would have created.
Even if you have a different project structure, dus could be easily integrated by setting the appropriate filepath for dus in your android/settings.gradle
Add dus-deployer to your app: npm install --save dus-deployer
In your android/settings.gradle
import the native dependencies of dus in your project using the following code:
include ':dus'
project(':dus').projectDir = new File(
rootProject.projectDir, '../node_modules/dus-deployer/android/dus')
In your android/app/build.gradle
include dus as a dependency by adding compile project(":dus")
in the dependencies:
dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
compile project(":dus")
}
Update the MainActivity.java to use DUS by making the following changes. Specify the bundle name that needs to be fetched from DUS to launch React Native on this activity.
public class MainActivity extends ReactActivity {
/**
* The name of the React Native bundle that needs
* to be fetched from DUS in this activity. This
* name could also be recieved from the intent fired to open
* this activity
*/
private final String bundleName = "example";
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "dusdemo";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new DusReactApplicationDelegate(this, getMainComponentName(), bundleName);
}
}
Update MainApplication.java by making the following changes:
public class MainApplication extends Application implements ReactApplication, DusApplication {
/**
* We create an instance of DusReactNativeHost instead
* of ReactNativeHost to use DUS
*/
private final DusReactNativeHost mReactNativeHost = new DusReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return false;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
/**
* Fetches the latest update graph on app launch
* which can be used to fetch new bundles on demand
*/
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
Cursor cursor = getApplicationContext().getContentResolver().query(DUSContracts.buildFetchUpdateGraphUri(), null, null, null, null);
if (cursor != null) {
cursor.close();
}
}
});
}
/**
* We inject all the dependencies into DUS: ComponentDownloader, FileConfigDownloader
* PackagedDbName, DusLogger and packagedDbVersion
* @return DusDependencyResolver - all dependencies required by DUS
*/
@Override
public DusDependencyResolver getDusDependencyResolver() {
DusDependencyResolver dependencyResolver = new DusDependencyResolver();
dependencyResolver.setComponentRequestInterface(new ComponentDownloader())
.setDusLogger(new DusLoggerResolver())
.setFileConfigRequestInterface(new FileConfigDownloader())
.setPackagedDbName("")
.setPackagedDbVersion(0);
return dependencyResolver;
}
@Override
public DusReactNativeHost getDusReactNativeHost() {
return mReactNativeHost;
}
}
Inject all custom implementations into DUS:
The specifications of these dependencies can be found here
DeploymentConfig.json
. This file contains a list of jobs for a particular application. Each job specifies the react native repository url, the branch name, the app versions, a script to be executed immediately before generating a bundle and the name of the bundle.Sample File
{
"deploymentJob": [
{
"repoUrl": "git@github.com:surya-kanoria/DUS-Sample-App.git",
"appVersions": [
"default"
],
"branchName": "master",
"shouldDeploy": true,
"preCompileScript": "ls",
"bundleName": "example"
}
]
}
Run the following command to generate the update patch for your application:
dus-deployer --config DeploymentConfig.json --platform android --updateGraphVersion <updateGraphVersion> --outputPath output --prodUpdateGraph <Update Patch generated during last deployment>
A Sample command would look like this:
dus-deployer --config DeploymentConfig.json --platform android --updateGraphVersion 0.0.0.0 --outputPath output --prodUpdateGraph output/UpdatePatch.json
In the output folder, an UpdatePatch.json would be generated which is a map of app version vs the update graphs that are generated which needs to be uploaded to your server.
In the same output folder, ComponentMap.json would be generated. It is a map of component keys vs the components that needs to be uploaded to your server.
For advanced usage please refer this doc(*link*).
https://github.com/surya-kanoria/DUS-Sample-App
The easiest way to contribute is by forking the repo, making your changes and creating a pull request.
Please open issues for any bugs that you encounter. You can reach out to me on twitter @suryakanoria or, write to cross-platform@flipkart.com for any questions that you might have.