Our enemy, app reviews
The wonderfull world of creating mobile apps. Where before we had to write objective-c (or swift) or java (or kotlin) we now have the ability to create apps by writing javascript. The way the written code renders your app differs from the two main platforms: web-hybrid and native-hybrid. Where web-hybrid (like cordova) uses an HTML5 capable browser screen to render an actual webpage to load your javascript bundle and then generate and show generated html to render your app, native-hybrid (like react-native) uses the javascript to directly talk to the rendering engine of your mobile device to render a native UI.
I can write a complete blog to explain the differences between the two approaches, but today I want to talk a bit about how you can keep your app updated, hotfixed and be able to do this in a way that is expected from today’s app users. Where a normal hosted web-app can be updated and pushed from your CI/CD solution to directly put the new content/feature in your users hands, mobile apps require a build artifact that has to be approved by either Google or Apple.
Apple (and to an extend Google) installs your app, checks your app configuration and wants to make sure you are following their requirements. Unfortunately this process can take quite a bit of time, especially in a time where doing quick and small releases is expected by your users.
Our Savior, CodePush
Fortunately, this is exactly where codepush comes into play. Codepush is a service offered for free by Microsoft that allows you to push an update to an already deployed app without having to publish the app to the AppStore or Google Play. What kind of sorcery is this you say? Let me explain a bit before go into a basic example implementation.
As I said before, hybrid apps use a javascript bundle that is used to actually render your app. On a bit more technical level the app uses native code to decide which javascript bundle to load. Normally this is the included, precompiled bundle your app was build with when it was uploaded. CodePush is a solution that calls back home to ask if there is a new bundle available and then loads the new bundle instead of the old one.
The first question that comes to mind when I explain this feature to colleagues, is if this is allowed by Apple and Google: which it is. The short answer:
CodePush allows you to follow these rules in full compliance so long as the update you push does not significantly deviate your product from its original App Store approved intent
(The longer answer can be found here)
So as long as you don’t change big parts of the app, or add or remove features that are beyond the intent of the initial review you are safe.
tl;dr Let start Implementing!
In order to properly demonstrate the workings of CodePush we will use a pretty simple and tiny React Native application. Using CodePush we will push an update that changes the text and adds an image to the installed app. If you want to follow along you can clone the following repository from GitHub:
blog-hybrit-codepush-base @ GitHub
The final result of the project can be found here:
blog-hybrit-codepush-completed @ GitHub
This was generated using react-native cli tooling and changed to show a simple screen. We can divide the steps to implement CodePush in the following 3 simple steps:
- Create an account on appcenter.ms
The creation part is not a difficult step, so sign up and get ready to create an app.
- Create an app and CodePush API keys on appcenter.ms
After you have access to AppCenter head over to the Apps section and add a new app. For this demo I will enter the following information:
After the app has been created we need to add the default CodePush deployments (a staging and a production flow). Navigate to your app and select the Distribute -> CodePush from the menu. It will show you the following screen:
Click on the default button to add the deployments. After pressing this button you will have two deployment types for the app to check for updates: Staging and Production. Select the Pipewrench icon in the top left to retrieve your two Android API keys. Save these in a easy to get to place.
- Install the local CLI tooling
In order to actually use the tooling to push an update we need the AppCenter CLI tooling. This allows us to send an update to the CodePush servers from our local developer station. Install it with the simple NPM command:
After the installation is complete, finish the setup of the plugin with
This will open up a new window to login into the Appcenter.ms to retrieve a new token. Make sure you have your ghostery/ublock setup turned of, otherwise it might cause issues with the redirect. After you setup the proper key for AppCenter you are done with the CLI installation.
React CodePush
Microsoft has provided a library to simplify the implementation of CodePush into your React-Native projects. Get the demo repository from up top or follow along from your own codebase. First include the actual code-push library using NPM:
With the tool installed, we need to make a couple of changes to the Native part of the iOS and Android apps. The CodePush code has to run at app start in order to install the updates.
Android
Add the following part to your android/app/build.gradle file:
This will make sure the codepush project is included in the build process. The final step for Android is to include it in the MainActivity. Add the following import at the top of your MainApplication.java file:
The finally, include the following part in your ReactNativeHost:
The final file should look something like this:
Now that we have CodePush implemented in the Native part of our app, we still need to let it know what deployment (and what project) it can connect to at the Appcenter.ms side of things. Find strings.xml in your Android project and add the following line using the deployment key retrieved from the above Appcenter.ms step:
This should give you something along the following lines:
That’s it for the native Android part of CodePush.
iOS
iOS needs a similar implementation of CodePush in order to retrieve and install the latest bundle your app uses. The iOS section will be completed in an upcoming blog post!
React Native
Now that our native part of the app knows to check for a bundle from appcenter.ms and install it, we need a small addition to our javascript / react side of the app. We need to call CodePush, let it know we are looking for an update and then we can act on it. CodePush gives us several options in order to install an update and they are (roughly) as followed:
- Silent sync on app start
Check if there is an update on app start, is so, download and install the update silenty. The app user will not know an update is available and the update will be install the next time the app is restarted. - Silent sync every time the app resumes
The same as the previous one except it will check everytime the app is resumed. - Interactive
Install an update and let the user know an update is available. This even gives the user the option to directly download and install the update. Pretty important to note is that Apple does not like this feature of CodePush. Bundle updates have to be silent in order for them to be allowed on the iOS platform.
For the purpose of this blog we will be using the interactive version. This allows us to properly check and see if everything is working properly, for production apps I recommend you use the silent sync on app start. More information regarding all available options can be found on the CodePush documentation website here: CodePush Install options
Implementing CodePush start in App.js
For React Native the CodePush implementation uses a Higher-Order Component. We need to wrap our main app entry component in the CodePush HCO optionally giving it options for configuration. Add the import and the options variable to your App.js file:
The latest react-native started projects have the app as a functional component, simply wrap your component with the HCO:
That’s it for the implementation! The moment you app runs, it will check for updates and if one has been found it will show the user an update dialog. How about we do a small test and see if everything is working as it should?
Doing a release
Doing an actual CodePush release is pretty simple. The user has an app running on his phone with version 1.0.0. When we do a local change and we start a CodePush release the bundle it creates is only compatible with the version of app it was created for. (ie a updated bundle for version 1.1.0 of the app will not be downloaded and installed on a version 1.0.0 of the app).
Make sure you have a non-development version of the app running. You can start and install it with:
Now that the app is running, change the text in your application. I will be doing something along the lines of:
With the change done, we can do a release to the staging environment using CodePush:
Now restart your React-Native app, it should give you a popup and let you know an update is available:
When you click install and restart the app, the changes will be applied!
As you can see, pushing changes to a running app can be as easy and a single cli command. You can manage and monitor all releases done using the Appcenter.ms website. It will show you which updates are available, if they are installed or not and you can do a partial or mandatory rollout of an update.