Improving docs and examples

This commit is contained in:
Jonathan Carter
2015-11-25 00:37:24 -08:00
parent f99a6cea92
commit aa395d5403

180
README.md
View File

@@ -1,31 +1,21 @@
# React Native plugin for CodePush
This plugin provides client-side integration for the [CodePush service](https://microsoft.github.io/code-push), allowing you to easily add a dynamic code update experience to your React Native apps.
This plugin provides client-side integration for the [CodePush service](https://microsoft.github.io/code-push), allowing you to easily add a dynamic update experience to your React Native app(s).
The CodePush React Native API provides two primary mechanisms for discovering updates and dynamically installing them within your apps:
## How does it work?
1. [**Sync mode**](#codepushsync), which allows you to call a single method--presumably as part of mounting your app's root component or in response to a button click--that will automatically check for an update, download and install it, while respecting the policies and metadata associated with each release (e.g. if the release is mandatory then it doesn't give the end-user the option to ignore it)
2. [**Advanced mode**](#codepushcheckforupdate), which provides a handful of "low-level" methods which give you complete control over the update experience, at the cost of added complexity.
A React Native app is composed of a `.jsbundle` file which is generated by the packager and then distributed as part of your app's binary (the `.ipa` or `.apk` file). Traditionally, in order to update any of the JavaScript code, you'd need to recompile and re-distribute the binary, which includes any review time for the respective store(s) you are publishing to.
When getting started using CodePush, we recommended using the sync method until you discover that it doesn't suit your needs. That said, if you have a user scenario
that isn't currently covered well by sync, please [let us know](mailto:codepushfeed@microsoft.com) since that would be valuable feedback.
*NOTE: We don't currently have support for an "automatic mode" which provides a "code-free" experience to adding in dynamic update discovery and acquisition. If that would be valuable to you, please [let us know](mailto:codepushfeed@microsoft.com).*
The CodePush plugin for React Native helps simplify and speed up the process of getting JavaScript changes to your end-users by synchronizing the `.jsbundle` file with updates you release to the CodePush server. This way, you get the benefit of an offline, mobile experience, as well as "web-like" agility of side-loading updates as soon as they are made available.
## Supported React Native platforms
- iOS
- Coming soon: Android (Try it out on [this branch](https://github.com/Microsoft/react-native-code-push/tree/first-check-in-for-android))
## How does it work?
A React Native application's assets (JavaScript code and other resources) are traditionally bundled up as a ```.jsbundle``` file which is loaded from the application installation location on the target device during runtime. After you submit an update to the store, the user downloads the update, and those assets will be replaced with the new assets.
CodePush is here to simplify this process by allowing you to instantly update your application's assets without having to submit a new update to the store. We do this by allowing you to upload and manage your React Native app bundles on the CodePush server. In the application, we check for the presence of updated bundles on the server. If they are available, we will install and persist them to the internal storage of the device. If a new bundle is installed, the application will reload from the updated package location.
## Plugin Acquisition
Acquire the React Native CodePush plugin by running the following command within your app's root directory:
To get started, you first need to acquire the React Native CodePush plugin by running the following command within your app's root directory:
```
npm install --save react-native-code-push
@@ -78,10 +68,9 @@ This change configures your app to always load the most recent version of your a
To let the CodePush runtime know which deployment it should query for updates against, perform the following steps:
1. Open your app's `Info.plist` and add a new `CodePushDeploymentKey` entry, whose value is the key of the deployment you want to configure this app against (e.g. the Staging deployment for FooBar app)
1. Open your app's `Info.plist` and add a new `CodePushDeploymentKey` entry, whose value is the key of the deployment you want to configure this app against (e.g. the Staging deployment for FooBar app). You can retreive this value by running `code-push deployment ls <appName>` in the CodePush CLI, and copying the value of the `Deployment Key` column which corresponds to the deployment you want to use.
2. In your app's `Info.plist` make sure your `CFBundleShortVersionString` value is a valid [semver](http://semver.org/) version (e.g. 1.0.0 not 1.0)
*NOTE: If you'd prefer, you can also set the deployment key in code by assigning the key to the `[CodePushConfig current].deploymentKey` property.*
## Plugin consumption
With the CodePush plugin downloaded and linked, and your app asking CodePush where to get the right JS bundle from, the only thing left is to add the neccessary code to your app to control the following:
@@ -103,14 +92,14 @@ The simplest way to do this is to perform the following in your app's root compo
CodePush.sync();
```
If an update is available, it will be silently download, and will be installed the next time the app is restarted. This experience ensures the least invasive experience for your end-users. If you would like to display a confirmation dialog, or customize the update experience in any way, refer to the `sync` method's [API reference](#codepushsync) for information on how to tweak this default behavior.
If an update is available, it will be silently downloaded, and installed the next time the app is restarted (either explicitly by the end-user or by the OS). This experience ensures the least invasive experience for your end-users. If you would like to display a confirmation dialog (an "active install"), or customize the update experience in any way, refer to the `sync` method's [API reference](#codepushsync) for information on how to tweak this default behavior.
## Releasing code updates
Once your app has been configured and distributed to your users, and you've made some JS changes, it's time to release it to them instantly! To do this, run the following steps:
1. Execute `react-native bundle` in order to generate the JS bundle for your app.
2. Execute `code-push release <appName> <deploymentName> ./ios/main.jsbundle <appVersion>` in order to publish the generated JS bundle to the server (assuming your CWD is the root directory of your React Native app).
2. Execute `code-push release <appName> ./ios/main.jsbundle <appVersion>` in order to publish the generated JS bundle to the server (assuming your CWD is the root directory of your React Native app).
And that's it! For more information regarding the CodePush API, including the various options you can pass to the `sync` method, refer to the reference section below.
@@ -193,70 +182,111 @@ If you call this method, and there isn't a pending update, it will result in a n
codePush.sync(options: Object, syncStatusChangeCallback: function(syncStatus: Number), downloadProgressCallback: function(progress: DownloadProgress)): Promise<Number>;
```
Provides a simple option for checking for an update, displaying a notification to the user, downloading it and then installing it, all while also respecting the policy that your release was published with. This method effectively composes together the "advanced mode" APIs for you, so that you don't need to handle any of the following scenarios yourself:
Provides a simple method for checking for an update and subsequently downloading and installing it. This method provides support for two different (but customizable) "modes" to easily enables apps with different requirements:
1. Checking for an update and displaying a standard confirmation dialog asking if they would like to install it
2. Automatically ignoring updates which have previously failed to install (due to automatic rollback), and therefore, likely don't make sense trying to install again (let's blacklist them!)
3. Looking to see whether an available update is mandatory, and if so, don't give the end-user the choice to ignore it
4. Displaying the description of an update to the end-user as part of the install confirmation experience
1. **Silent mode** *(the default behavior)*, which automatically downloads available updates, and applies them the next time the app restarts. This way, the entire update experience is "silent" to the end-user, since they don't see any update prompt and/or "synthentic" app restarts.
If you want to pivot whether you check and/or download an available update based on the end-user's device battery level, network conditions, etc. then simply wrap the call to `sync` in a condition that ensures you only call it when desired.
2. **Active mode**, which when an update is available, prompts the end-user for permission before downloading it, and then immediately applies the update. If an update was released using the `mandatory` flag, the end-user would still be notified about the update, but they wouldn't have the choice to ignore it.
The method accepts an options object that allows you to customize numerous aspects of the default behavior, all of which provide sensible values by default:
* __deploymentKey__ (String) - Specifies the deployment key you want to query for an update against. By default, this value is derived from the `Info.plist` file, but this option allows you to override it from the script-side if you need to dynamically change your app's current deployment.
* __ignoreFailedUpdates__ (Boolean) - Indicates whether you would like to automatically ignored updates which are available, but have been previously attemped to install, but failed. Defaults to `true`.
* __installMode__ (CodePush.InstallMode) - Indicates whether you would like to restart the app immediately after the update has been installed, or wait until the next app resume or restart. Defaults to `CodePush.InstallMode.ON_NEXT_RESTART`
* __rollbackTimeout__ (Number) - The number of seconds that you want the runtime to wait after an update has been installed before considering it failed and rolling it back. Defaults to `0`, which disables rollback protection.
* __updateDialog__ (UpdateDialogOptions) - The options object used to customize the dialog displayed to the user. Unspecified fields in the object provided will default to the values shown below, hence the boolean flag `true`, or any other truthy value will cause the default values below to be used. A falsey value will disable the display of a dialog, in which case updates will be downloaded automatically. Defaults to `null` (do not show a dialog). The list of `UpdateDialogOptions` are as follows:
* __appendReleaseDescription__ (Boolean) - Indicates whether you would like to append the description of an available release to the notification message which is displayed to the end-user. Defaults to `false`.
* __descriptionPrefix__ (String) - Indicates the string you would like to prefix the release description with, if any, when displaying the update notification to the end-user. Defaults to `" Description: "`
* __mandatoryContinueButtonLabel__ (String) - The text to use for the button the end-user must press in order to install a mandatory update. Defaults to `"Continue"`.
* __mandatoryUpdateMessage__ (String) - The text used as the body of an update notification, when the update is specified as mandatory. Defaults to `"An update is available that must be installed."`.
* __optionalIgnoreButtonLabel__ (String) - The text to use for the button the end-user can press in order to ignore an optional update that is available. Defaults to `"Ignore"`.
* __optionalInstallButtonLabel__ (String) - The text to use for the button the end-user can press in order to install an optional update. Defaults to `"Install"`.
* __optionalUpdateMessage__ (String) - The text used as the body of an update notification, when the update is optional. Defaults to `"An update is available. Would you like to install it?"`.
* __title__ (String) - The text used as the header of an update notification that is displayed to the end-user. Defaults to `"Update available"`.
In addition, the method also recieves two function arguments which serve as event handlers which are called at various points in the sync process:
* __syncStatusChangeCallback__ (function(syncStatus: Number)) - Called when the sync process moves to a different step. Below is the list of possible SyncStatus values:
* __CodePush.SyncStatus.CHECKING_FOR_UPDATE__ *(0)* - Querying the CodePush server for an update.
* __CodePush.SyncStatus.AWAITING_USER_ACTION__ *(1)* - Waiting for a response from the user (e.g. a confirmation dialog).
* __CodePush.SyncStatus.DOWNLOADING_PACKAGE__ *(2)* - Downloading the updated package from the CodePush server.
* __CodePush.SyncStatus.INSTALLING_UPDATE__ *(3)* - The app had an optional or mandatory update that was successfully downloaded and is about to be installed.
* __CodePush.SyncStatus.UP_TO_DATE__ *(4)* - The app does not have an available update.
* __CodePush.SyncStatus.UPDATE_IGNORED__ *(5)* - The app has an optional update, that the user chose to ignore.
* __CodePush.SyncStatus.UPDATE_INSTALLED__ *(6)* - The update has been installed and will be run either immediately after the syncStatusCallback function returns or the next time the app resumes/restarts, depending on the `InstallMode` specified in `SyncOptions`.
* __CodePush.SyncStatus.UNKNOWN_ERROR__ *(-1)* - The sync operation encountered an unknown error.
* __downloadProgressCallback__ (function(progress: DownloadProgress)) - Called periodically when the update package is being downloaded from the CodePush server to report the progress of the update. `DownloadProgress` contains two fields:
* __totalBytes__ (Number) - The total number of bytes expected to be received for this update package
* __receivedBytes__ (Number) - The number of bytes downloaded thus far.
The method returns a `Promise` that is resolved to a `SyncStatus` integer code, which indicates why the `sync` call succeeded. This code can be one of the following values:
* __CodePush.SyncStatus.UP_TO_DATE__ *(4)* - The app does not have an available update.
* __CodePush.SyncStatus.UPDATE_IGNORED__ *(5)* - The app has an optional update, that the user chose to ignore.
* __CodePush.SyncStatus.UPDATE_INSTALLED__ *(6)* - The update has been installed and will be run either immediately after the syncStatusCallback function returns or the next time the app resumes/restarts, depending on the `InstallMode` specified in `SyncOptions`.
If the update check and/or the subseqeuent download fails for any reason, the `Promise` object returned by `sync` will be rejected with the reason.
Example Usage:
```javascript
codePush.sync()
.then((status) => {
if (status == codePush.SyncResult.UPDATE_INSTALLED) {
// Do any neccessary work here before the app
// is restarted in order to install the update
}
})
.catch((reason) => {
// Do something with the failure
});
// Fully silent update
codePush.sync();
// Active update
codePush.sync({ updateDialog: true, installMode: codePush.InstallMode.IMMEDIATE });
```
*Note: If you want to determine whether you check and/or download an available update based on the end-user's device battery level, network conditions, etc. then simply wrap the call to `sync` in a condition that ensures you only call it when desired.*
While the `sync` method tries to make it easy to perform silent and active updates, with little configuration, it accepts an "options" object that allows you to customize numerous aspects of the default behavior:
* __deploymentKey__ *(String)* - Specifies the deployment key you want to query for an update against. By default, this value is derived from the `Info.plist` file, but this option allows you to override it from the script-side if you need to dynamically use a different deployment for a specific call to `sync`.
* __ignoreFailedUpdates__ *(Boolean)* - Indicates whether you would like to automatically ignored updates which are available on the server, but have already failed previous installation attempts. Defaults to `true`.
* __installMode__ *(CodePush.InstallMode)* - Indicates when you would like to "install" the update after downloading it, which includes reloading the JS bundle in order for any changes to take affect. Defaults to `CodePush.InstallMode.ON_NEXT_RESTART`.
* __rollbackTimeout__ *(Number)* - The number of milliseconds that you want the runtime to wait after an update has been installed before considering it failed and rolling it back. Defaults to `0`, which disables rollback protection.
* __updateDialog__ *(UpdateDialogOptions)* - The options object used to customize the dialog displayed to the end-user when employing an "active" update workflow. Unspecified fields in the object provided will default to the values shown below, hence the boolean flag `true`, or any other truthy value will cause the default values below to be used. A falsey value will disable the display of a dialog, in which case updates will be downloaded automatically. Defaults to `null` (do not show a dialog). The list of `UpdateDialogOptions` are as follows:
* __appendReleaseDescription__ *(Boolean)* - Indicates whether you would like to append the description of an available release to the notification message which is displayed to the end-user. Defaults to `false`.
* __descriptionPrefix__ *(String)* - Indicates the string you would like to prefix the release description with, if any, when displaying the update notification to the end-user. Defaults to `" Description: "`
* __mandatoryContinueButtonLabel__ *(String)* - The text to use for the button the end-user must press in order to install a mandatory update. Defaults to `"Continue"`.
* __mandatoryUpdateMessage__ *(String)* - The text used as the body of an update notification, when the update is specified as mandatory. Defaults to `"An update is available that must be installed."`.
* __optionalIgnoreButtonLabel__ *(String)* - The text to use for the button the end-user can press in order to ignore an optional update that is available. Defaults to `"Ignore"`.
* __optionalInstallButtonLabel__ *(String)* - The text to use for the button the end-user can press in order to install an optional update. Defaults to `"Install"`.
* __optionalUpdateMessage__ *(String)* - The text used as the body of an update notification, when the update is optional. Defaults to `"An update is available. Would you like to install it?"`.
* __title__ *(String)* - The text used as the header of an update notification that is displayed to the end-user. Defaults to `"Update available"`.
Example Usage:
```javascript
// Use a different deployment key for this
// specific call, instead of the one configured
// in the Info.plist file
codePush.sync({ deploymentKey: "KEY" });
// Download the update silently
// but install is on the next resume
// instead of waiting until the app is restarted
codePush.sync({ installMode: codePush.InstallMode.ON_NEXT_RESUME });
// Changing the title displayed in the
// confirmation dialog of an "active" update
codePush.sync({ updateDialog: { title: "An update is available!" } });
```
In addition to the options, the `sync` method also accepts two optional function parameters which allow you to subscribe to the lifecycle of the `sync` "pipeline" in order to display additional UI as needed (e.g. a "checking for update modal or a download progress modal):
* __syncStatusChangeCallback__ *((syncStatus: Number) => void)* - Called when the sync process moves from one step to another in the overall update process. The method is called with a status code which represents the current state, and can be any of the following values:
* __CodePush.SyncStatus.CHECKING_FOR_UPDATE__ *(0)* - The CodePush server is being queried for an update.
* __CodePush.SyncStatus.AWAITING_USER_ACTION__ *(1)* - An update is available, and a confirmation dialog was shown to the end-user. (This is only applicable when the `updateDialog` is used)
* __CodePush.SyncStatus.DOWNLOADING_PACKAGE__ *(2)* - An available update is being downloaded from the CodePush server.
* __CodePush.SyncStatus.INSTALLING_UPDATE__ *(3)* - An available udpate was downloaded and is about to be installed.
* __CodePush.SyncStatus.UP_TO_DATE__ *(4)* - The app is fully up-to-date with the configured deployment.
* __CodePush.SyncStatus.UPDATE_IGNORED__ *(5)* - The app has an optional update, which the end-user chose to ignore. (This is only applicable when the `updateDialog` is used)
* __CodePush.SyncStatus.UPDATE_INSTALLED__ *(6)* - An available update has been installed and will be run either immediately after the syncStatusCallback function returns or the next time the app resumes/restarts, depending on the `InstallMode` specified in `SyncOptions`.
* __CodePush.SyncStatus.UNKNOWN_ERROR__ *(-1)* - The sync operation encountered an unknown error.
* __downloadProgressCallback__ *((progress: DownloadProgress) => void)* - Called periodically when an available update is being downloaded from the CodePush server. The method is called with a `DownloadProgress` object, which contains the following two properties:
* __totalBytes__ *(Number)* - The total number of bytes expected to be received for this update package
* __receivedBytes__ *(Number)* - The number of bytes downloaded thus far.
Example Usage:
```javascript
// Prompt the user when an update is available
// and then display a "downloading" modal
codePush.sync({ updateDialog: true }, (status) => {
switch (status) {
case CodePush.SyncStatus.DOWNLOADING_PACKAGE:
// Show "downloading" modal
break;
case CodePush.SyncStatus.INSTALLING_UPDATE:
// Hide "downloading" modal
break;
}
});
```
The method returns a `Promise` that is resolved with a `SyncStatus` code that indicates why the `sync` call succeeded. This code can be one of the following values:
* __CodePush.SyncStatus.UP_TO_DATE__ *(4)* - The app is up-to-date with the CodePush server.
* __CodePush.SyncStatus.UPDATE_IGNORED__ *(5)* - The app has an optional update, that the user chose to ignore.
* __CodePush.SyncStatus.UPDATE_INSTALLED__ *(6)* - The update has been installed and will be run either immediately after the syncStatusCallback function returns or the next time the app resumes/restarts, depending on the `InstallMode` specified in `SyncOptions`.
If the update check and/or the subseqeuent download fails for any reason, the `Promise` object returned by `sync` will be rejected with the reason.
The `sync` method can be called anywhere you'd like to check for an update. That could be in the `componentWillMount` lifecycle event of your root component, the onPress handler of a `<TouchableHighlight>` component, in the callback of a periodic timer, or whatever else makes sense for your needs. Just like the `checkForUpdate` method, it will perform the network request to check for an update in the background, so it won't impact your UI thread and/or JavaScript thread's responsiveness.
### Package objects
@@ -305,4 +335,4 @@ The `RemotePackage` inherits all of the same properties as the `LocalPackage`, b
## Debugging
When debugging your JavaScript using Chrome, make sure that your JS bundle location is configured in your `AppDelegate.m` file to point at the packager URL, since that will provide you with the most effecient debugging experience. Since your CodePush deployment key is specified in either the `Info.plist` file, by setting the `[CodePushConfig current].deploymentKey` property, or by calling the `codePush.setDeploymentKey()` method from JavaScript, any calls to `sync` or `checkForUpdate` will work just fine regardless if your `AppDelegate.m` file hasn't be configured to use the `[CodePush bundleURL]` method for its JS bundle location.
When debugging your JavaScript using Chrome, make sure that your JS bundle location is configured in your `AppDelegate.m` file to point at the packager URL, since that will provide you with the most effecient debugging experience. Since your CodePush deployment key is specified in either the `Info.plist` file or your call(s) to the `sync` and/or `checkForUpdate` methods, any calls to `sync` or `checkForUpdate` will work just fine regardless if your `AppDelegate.m` file hasn't be configured to use the `[CodePush bundleURL]` method for its JS bundle location.