Hello! I'm using react-native-navigation and maybe it has something to do with it, so I'll inform this first.
This is my package.json:
...
...
"babel-preset-react-native-stage-0": "^1.0.1",
"react-native": "^0.49.3",
"appcenter": "^1.0.1",
"appcenter-analytics": "^1.0.1",
"appcenter-crashes": "^1.0.1",
"react-native-code-push": "^5.2.0-beta",
"react-native-navigation": "^1.1.295",
...
...
My .babelrc is configured correctly so I can use the decorator:
...
"presets": ["react-native-stage-0/decorator-support"],
...
My index.js file:
import App from './App/Containers/App';
import './App/Config/ReactotronConfig';
new App();
My App/Containers/App file:
import 'Config';
import createStore from 'App/Redux';
export default class App {
constructor() {
createStore();
}
}
I've already searched in all issues and found a related issue: #875. I've followed the steps and created a custom component that returns null just to make codepush work:
import React from 'react';
import codePush from 'react-native-code-push';
@codePush({
installMode: codePush.InstallMode.ON_NEXT_RESTART,
mandatoryInstallMode: codePush.InstallMode.ON_NEXT_RESTART,
})
export default class CodePushComponent extends React.Component {
render() {
return null;
}
}
So, I render in my DashboardScreen (the first screen that appears when the user opens the app):
import CodePushComponent from 'App/Components/CodePush';
...
...
render() {
...
...
<CodePushComponent />
...
...
}
The DashboardScreen is being registered this way:
Navigation.registerComponent(screens.DASHBOARD_SCREEN, () => screenWrapper(DashboardScreen), store, Provider);
The screenWrapper is just a HOC to apply some default navigation styles that is used in the react-native-navigation package:
import React from 'react';
import defaultNavigationStyles from './DefaultNavigationStyle';
export default function screenWrapper(WrappedComponent) {
const wrapper = class extends React.Component {
static navigatorStyle = {
...defaultNavigationStyles,
}
render() {
return <WrappedComponent {...this.props} />;
}
};
if (WrappedComponent.navigatorStyle) {
wrapper.navigatorStyle = Object.assign(wrapper.navigatorStyle, WrappedComponent.navigatorStyle);
}
if (WrappedComponent.navigatorButtons) {
wrapper.navigatorButtons = WrappedComponent.navigatorButtons;
}
return wrapper;
}
This is my MainApplication.java:
package com.myapp;
import com.RNFetchBlob.RNFetchBlobPackage;
import com.airbnb.android.react.maps.MapsPackage;
import com.facebook.react.ReactPackage;
import com.lugg.ReactNativeConfig.ReactNativeConfigPackage;
import com.lwansbrough.RCTCamera.RCTCameraPackage;
import com.microsoft.appcenter.reactnative.analytics.AppCenterReactNativeAnalyticsPackage;
import com.microsoft.appcenter.reactnative.appcenter.AppCenterReactNativePackage;
import com.microsoft.appcenter.reactnative.crashes.AppCenterReactNativeCrashesPackage;
import com.microsoft.codepush.react.CodePush;
import com.oblador.vectoricons.VectorIconsPackage;
import com.reactnative.ivpusic.imagepicker.PickerPackage;
import com.reactnativenavigation.NavigationApplication;
import com.myapp.modules.BackupData.RNMyAppPackage;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends NavigationApplication {
@Override
public boolean isDebug() {
return BuildConfig.DEBUG;
}
protected List<ReactPackage> getPackages() {
return Arrays.asList(
new AppCenterReactNativeCrashesPackage(MainApplication.this, getResources().getString(R.string.appcenterCrashes_whenToSendCrashes)),
new AppCenterReactNativeAnalyticsPackage(MainApplication.this, getResources().getString(R.string.appcenterAnalytics_whenToEnableAnalytics)),
new AppCenterReactNativePackage(MainApplication.this),
new CodePush(BuildConfig.CODEPUSH_DEPLOYMENT_KEY, MainApplication.this, BuildConfig.DEBUG),
new RNMyAppPackage(),
new VectorIconsPackage(),
new ReactNativeConfigPackage(),
new RNFetchBlobPackage(),
new RCTCameraPackage(),
new PickerPackage(),
new MapsPackage()
);
}
@Override
public List<ReactPackage> createAdditionalReactPackages() {
return getPackages();
}
@Override
public String getJSMainModuleName() {
return "index";
}
@Override
public String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
@Override
public String getBundleAssetName() {
return "index.android.bundle";
}
}
My deployment key is specified in a .env.prod file because I'm using react-native-conf. This file is setup correctly when building from appcenter using a appcenter-pre-build.hs build script. This is my .env.prod file:

My application is being built using AppCenter:

(the build is fine)
While building, the pre build script create the .env.prod file and apply the codepush api key:

I download the APK and install on my simulator and physical device.
When I open the app, the bundle is loded correcly from CodePush:
12-04 07:56:43.372 6351-6351/com.myapp D/ReactNative: [CodePush] Loading JS bundle from "assets://index.android.bundle"
Right before the DashboardScreen is open, I got the following log:
12-04 08:10:19.519 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Checking for update.
12-04 08:10:19.520 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Reporting binary update (0.0.1)
12-04 08:10:20.715 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Downloading package.
12-04 08:10:22.982 7013-7036/com.myapp D/ReactNative: [CodePush] Applying full update.
12-04 08:10:22.983 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Installing update.
12-04 08:10:23.034 7013-7038/com.myapp D/ReactNative: [CodePush] Loading JS bundle from "/data/user/0/com.myapp/files/CodePush/166f00235674bfc21c3234bc826338caa2982f204c676255071b1ff4d97246b5/code-push117113-8356-94oipj.96vm/index.android.bundle"
12-04 08:10:23.035 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Restarting app
So the bundle is downloaded, applied, but when the app is restarted by codepush, it crashes:

After the crash, I try to open the app again and the following log is received:
12-04 08:11:45.880 7173-7173/com.myapp D/ReactNative: [CodePush] Update did not finish loading the last time, rolling back to a previous version.
12-04 08:11:45.892 7173-7173/com.myapp D/ReactNative: [CodePush] Loading JS bundle from "assets://index.android.bundle"
12-04 08:11:46.748 7173-7198/com.myapp I/ReactNativeJS: [CodePush] Sync already in progress.
12-04 08:11:46.773 7173-7198/com.myapp I/ReactNativeJS: [CodePush] Checking for update.
12-04 08:11:46.776 7173-7198/com.myapp I/ReactNativeJS: [CodePush] Reporting CodePush update rollback (v5)
12-04 08:11:48.089 7173-7198/com.myapp I/ReactNativeJS: [CodePush] An update is available, but it is being ignored due to having been previously rolled back.
12-04 08:11:48.090 7173-7198/com.myapp I/ReactNativeJS: [CodePush] App is up to date.
Then in AppCenter I got the rollback results:

So the updated did not complete. I guess the notifyAppReady maybe is not being called?
I'm using inside my application, so I dont have any steps to reproduce to inform here.
I expect that the update is installed on the user's phone.
What actually happens?
The application try to update, download the bundle, restarts and rollback.
If I can provide any other information tell me. Thanks!
Okay, after a few tries I found the problem and a workaround but not a solution. Inside my application I have a backup service that runs in background on Android. I declare a new React Native package:
package com.myapp.modules.BackupData;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RNMyAppPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new BackupDataModule(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
And in my BackupModule I start the service:
package com.myapp.modules.BackupData;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class BackupDataModule extends ReactContextBaseJavaModule {
public BackupDataModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "BackupDataModule";
}
@ReactMethod
public void startService() {
BackupDataService.scheduleAlarm(getReactApplicationContext());
}
}
In my BackupDataService I start a new Intent and set to run inexactRepeating using the AlarmManager in Android:
Intent intent = new Intent(context, BackupDataReceiver.class);
intent.setAction("com.myapp.backupdata");
...
...
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(
AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
AlarmManager.INTERVAL_HALF_DAY,
myIntent
Inside my application javascript code I import my module and start the service:
import BackupDataService from 'App/Services/BackupDataService';
...
...
...
BackupDataService.startService();
...
...
After I removed the call to my background service, rebuild the apk, install on the emulator, open the application, restart the aplication, the update was applied correctly, as you can see in the logs:
2-04 08:47:59.025 8857-8969/com.myapp I/ReactNativeJS: [CodePush] Checking for update.
12-04 08:47:59.031 8857-8969/com.myapp I/ReactNativeJS: [CodePush] Reporting CodePush update success (v6)
12-04 08:48:00.114 8857-8969/com.myapp I/ReactNativeJS: [CodePush] App is up to date.
Could be this error related to the fact that I don't declare my the ReactInstanceHolder as the docs instruct to do so? The docs says:
his section is only necessary if you're explicitly launching a React Native instance without an Activity (for example, from within a native push notification receiver). For these situations, CodePush must be told how to find your React Native instance.
But using the react-native-navigation package, my MainApplication.java is extending NavigationApplication.java and I don't know how to declare the ReactInstanceHolder.
Hi @Fuhrmann and sorry for delayed response. Yes, you problem might be related with fact that you didn't declared ReactInstanceHolder. To declare ReactInstanceHolder you just need to edit MainApplication.java as following:
import com.microsoft.codepush.react.ReactInstanceHolder;
//implement ReactInstanceHolder interface
public class MainApplication extends NavigationApplication implements ReactInstanceHolder {
///...
@Override
public ReactInstanceManager getReactInstanceManager() {
return getReactNativeHost().getReactInstanceManager();
}
}
Please let us know if it solves your problem or any updates.
Hi! You are right. That was the problem. Thanks so much for the help, I'll close since we solved it.
Most helpful comment
Hi @Fuhrmann and sorry for delayed response. Yes, you problem might be related with fact that you didn't declared ReactInstanceHolder. To declare ReactInstanceHolder you just need to edit
MainApplication.javaas following:Please let us know if it solves your problem or any updates.