Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registering React Native Code Push with React Native Navigation by Wix

I use react-native-code-push. which is:

This plugin provides client-side integration for the CodePush service, allowing you to easily add a dynamic update experience to your React Native app(s).

but In some of native implementations of navigation like react-native-navigation there isn't any root component. the app will start calling a function like this:

// index.js

import { Navigation } from 'react-native-navigation';

Navigation.startTabBasedApp({
  tabs: [
    {
      label: 'One',
      screen: 'example.FirstTabScreen', // this is a registered name for a screen
      icon: require('../img/one.png'),
      selectedIcon: require('../img/one_selected.png'), // iOS only
      title: 'Screen One'
    },
    {
      label: 'Two',
      screen: 'example.SecondTabScreen',
      icon: require('../img/two.png'),
      selectedIcon: require('../img/two_selected.png'), // iOS only
      title: 'Screen Two'
    }
  ]
});
// or a single screen app like:
Navigation.registerComponent('example.MainApplication', () => MainComponent);

Navigation.startSingleScreenApp({
  screen: {
    screen: 'example.MainApplication', 
    navigatorButtons: {}, 
    navigatorStyle: {
      navBarHidden: true
    }
  },

})

since there is no root component, It's not clear where should I call CodePush, since normally I should wrap my whole root component with CodePush like a higher order component. what I used to do was:

// index.js
class MyRootComponent extends Component {
  render () {
    return <MainNavigator/> // a navigator using react-navigation
  }
}

let codePushOptions = {
  checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
  installMode: CodePush.InstallMode.ON_NEXT_RESUME
}


export default CodePush(codePushOptions)(MyRootComponent)

Is there a proper way to solve this problem!?


I know I could do this:

Navigation.registerComponent('example.MainApplication', () => CodePush(codePushOptions)(RootComponent));

Navigation.startSingleScreenApp({
  screen: {
    screen: 'example.MainApplication', 
    navigatorButtons: {},
    navigatorStyle: {
      navBarHidden: true
    }
  },

})

but then I should use a Navigator only for projecting my root component, and It doesn't look like a good idea. I think this problem probably has a best-practice that I'm looking for.


UPDATE

I think there are some complications registering a tab navigator inside a stacknavigator in react-native-navigation at least I couldn't overcome this problem. example tabBasedApp in react-native-navigation with react-native-code-push, will be all that I need.

like image 937
Soorena Avatar asked May 19 '18 11:05

Soorena


3 Answers

Thanks for the previous code snippets. I was able to get code push check on app resume and update immediately with react-native-navigation V2 with the below code without requiring wrapper component for codePush. This is the relevant part of the app startup logic.

    Navigation.events().registerAppLaunchedListener(() => {
      console.log('Navigation: registerAppLaunchedListener ')
      start()
    })

    function checkCodePushUpdate () {
      return  codePush.sync({
        checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
        installMode: codePush.InstallMode.IMMEDIATE,
        deploymentKey: CODEPUSH_KEY,
      })
    }

    function start () {
      checkCodePushUpdate ()
        .then(syncStatus => {
          console.log('Start: codePush.sync completed with status: ', syncStatus)
          // wait for the initial code sync to complete else we get flicker
          // in the app when it updates after it has started up and is
          // on the Home screen
          startApp()
          return null
        })
        .catch(() => {
          // this could happen if the app doesn't have connectivity
          // just go ahead and start up as normal
          startApp()
        })
    }

    function startApp() {
      AppState.addEventListener('change', onAppStateChange)
      startNavigation()
    }

    function onAppStateChange (currentAppState) {
      console.log('Start: onAppStateChange: currentAppState: ' + currentAppState)
      if (currentAppState === 'active') {
        checkCodePushUpdate()
      }
    }

    function startNavigation (registered) {
      console.log('Start: startNavigation')

      registerScreens()

      Navigation.setRoot({
        root: {
          stack: {
            children: [{
              component: {
                name: 'FirstScreen,
              },
            }],
          },
        },
      })
    }
like image 181
denali Avatar answered Nov 03 '22 18:11

denali


I got it working this way, although this is for RNN v2

// index.js
import App from './App';
const app = new App();
// App.js
import CodePush from 'react-native-code-push';
import { Component } from 'react';
import { AppState } from 'react-native';
import { Navigation } from 'react-native-navigation';
import configureStore from './app/store/configureStore';
import { registerScreens } from './app/screens';

const appStore = configureStore();
registerScreens(appStore, Provider);

const codePushOptions = {
  checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
  updateDialog: true,
  installMode: CodePush.InstallMode.IMMEDIATE
};

export default class App extends Component {
    constructor(props) {
        super(props);
        // Set app state and listen for state changes
        this.appState = AppState.currentState;
        AppState.addEventListener('change', this.handleAppStateChange);

        this.codePushSync();
        Navigation.events().registerAppLaunchedListener(() => {
            this.startApp();
        });
    }

    handleAppStateChange = nextAppState => {
        if (this.appState.match(/inactive|background/) && nextAppState === 'active') {
            this.handleOnResume();
        }
        this.appState = AppState.currentState;
    };

    codePushSync() {
        CodePush.sync(codePushOptions);
    }


    handleOnResume() {
        this.codePushSync();
        ...
    }

    startApp() {
        Navigation.setRoot({
            root: {
                stack: {
                    children: [
                        {
                            component: {
                                name: 'MyApp.Login'
                            }
                        }
                    ]
                }
            }
        });
    }
}
// app/screens/index.js
import CodePush from 'react-native-code-push';
import { Navigation } from 'react-native-navigation';
import Login from './Login';

function Wrap(WrappedComponent) {
    return CodePush(WrappedComponent);
}

export function registerScreens(store, Provider) {
    Navigation.registerComponentWithRedux(
        'MyApp.Login',
        () => Wrap(Login, store),
        Provider,
        store.store
    );
    ...
}
like image 27
malerie.kaplan Avatar answered Nov 03 '22 19:11

malerie.kaplan


I found the answer myself. Look at this example project structure:

.
├── index.js
├── src
|   └── app.js
└── screens
    ├── tab1.html
    └── tab2.html

you can register you code-push in index.js.

//index.js
import { AppRegistry } from 'react-native';
import App from './src/app';
import CodePush from 'react-native-code-push'

let codePushOptions = {
  checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
  installMode: CodePush.InstallMode.ON_NEXT_RESUME
}

AppRegistry.registerComponent('YourAppName', () => CodePush(codePushOptions)(App));

now you can start react-native-navigation in app.js like this:

import {Navigation} from 'react-native-navigation';
import {registerScreens, registerScreenVisibilityListener} from './screens';

registerScreens();
registerScreenVisibilityListener();

const tabs = [{
  label: 'Navigation',
  screen: 'example.Types',
  icon: require('../img/list.png'),
  title: 'Navigation Types',
}, {
  label: 'Actions',
  screen: 'example.Actions',
  icon: require('../img/swap.png'),
  title: 'Navigation Actions',
}];

Navigation.startTabBasedApp({
  tabs,
  tabsStyle: {
    tabBarBackgroundColor: '#003a66',
    tabBarButtonColor: '#ffffff',
    tabBarSelectedButtonColor: '#ff505c',
    tabFontFamily: 'BioRhyme-Bold',
  },
  appStyle: {
    tabBarBackgroundColor: '#003a66',
    navBarButtonColor: '#ffffff',
    tabBarButtonColor: '#ffffff',
    navBarTextColor: '#ffffff',
    tabBarSelectedButtonColor: '#ff505c',
    navigationBarColor: '#003a66',
    navBarBackgroundColor: '#003a66',
    statusBarColor: '#002b4c',
    tabFontFamily: 'BioRhyme-Bold',
  }
});
like image 39
Soorena Avatar answered Nov 03 '22 17:11

Soorena