diff --git a/README.md b/README.md index 5f18dbb..7e0b8e4 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,26 @@ 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). -*Note: This README covers the current state of the master branch, and therefore, may include slight API differences as compared to what is published to NPM. For the latest "stable" documentation, please refer to the [website](http://microsoft.github.io/code-push/docs/react-native.html).* +* [How does it work?](#how-does-it-work) +* [Supported React Native Platforms](#supported-react-native-platforms) +* [Getting Started](#getting-started) +* [iOS Setup](#ios-setup) + * [Plugin Installation](#plugin-installation-ios) + * [Plugin Configuration](#plugin-configuration-ios) +* [Android Setup](#android-setup) + * [Plugin Installation](#plugin-installation-android) + * [Plugin Configuration](#plugin-configuration-android) +* [Plugin Usage](#plugin-usage) +* [Releasing Updates (JavaScript-only)](#releasing-updates-javascript-only) +* [Releasing Updates (JavaScript + images)](#releasing-updates-javascript--images) +* [Debugging](#debugging) +* [API Reference](#api-reference) ## How does it work? -A React Native app is composed of a JavaScript bundle file, which is generated by the [packager](https://github.com/facebook/react-native/tree/master/packager) and distributed as part of your app's binary (i.e. the `.ipa` or `.apk` file). Once the app is released, updating the JavaScript code (e.g. making bug fixes, adding new features) requires you to recompile and redistribute the entire binary, which of course, includes any review time associated with the store(s) you are publishing to. +A React Native app is composed of JavaScript files and any accompanying [images](https://facebook.github.io/react-native/docs/images.html#content), which are bundled together by the [packager](https://github.com/facebook/react-native/tree/master/packager) and distributed as part of a platform-specific binary (i.e. an `.ipa` or `.apk` file). Once the app is released, updating either the JavaScript code (e.g. making bug fixes, adding new features) or image assets, requires you to recompile and redistribute the entire binary, which of course, includes any review time associated with the store(s) you are publishing to. -The CodePush plugin helps get product improvements in front of your end-users instantly, by keeping the JavaScript bundle synchronized with updates that are released to the CodePush server. This way, your app gets the benefits of an offline mobile experience, as well as the "web-like" agility of side-loading updates as soon as they are available. It's a win-win! +The CodePush plugin helps get product improvements in front of your end-users instantly, by keeping your JavaScript and images synchronized with updates you release to the CodePush server. This way, your app gets the benefits of an offline mobile experience, as well as the "web-like" agility of side-loading updates as soon as they are available. It's a win-win! *Note: Any product changes which touch native code (e.g. modifying your `AppDelegate.m`/`MainActivity.java` file, adding a new plugin) cannot be distributed via CodePush, and therefore, must be updated via the appropriate store(s).* @@ -21,16 +34,20 @@ The CodePush plugin helps get product improvements in front of your end-users in ## Getting Started -Once you've followed the overall ["getting started"](http://microsoft.github.io/code-push/docs/getting-started.html) instructions for setting up your CodePush account, you can acquire the React Native CodePush plugin by running the following command from within your app's root directory: +Once you've followed the general-purpose ["getting started"](http://microsoft.github.io/code-push/docs/getting-started.html) instructions for setting up your CodePush account, you can start CodePush-ifying your React Native app by running the following command from within your app's root directory: ``` npm install --save react-native-code-push ``` -## Plugin Installation - iOS +As with all other React Native plugins, the integration experience is different for iOS and Android, so perform the following setup steps depending on which platform(s) you are targetting. + +## iOS Setup Once you've acquired the CodePush plugin, you need to integrate it into the Xcode project of your React Native app. To do this, take the following steps: +### Plugin Installation (iOS) + 1. Open your app's Xcode project 2. Find the `CodePush.xcodeproj` file within the `node_modules/react-native-code-push` directory, and drag it into the `Libraries` node in Xcode @@ -52,27 +69,7 @@ Add a new value, `$(SRCROOT)/../node_modules/react-native-code-push` and select ![Add CodePush library reference](https://cloud.githubusercontent.com/assets/516559/10322038/b8157962-6c30-11e5-9264-494d65fd2626.png) -## Plugin Installation - Android - -To integrate CodePush into your Android project, do the following steps: - -1. In your `android/settings.gradle` file, make the following additions: - - ```gradle - include ':app', ':react-native-code-push' - project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app') - ``` -2. In your `android/app/build.gradle` file, add the `:react-native-code-push` project as a compile-time dependency: - - ```gradle - ... - dependencies { - ... - compile project(':react-native-code-push') - } - ``` - -## Plugin Configuration - iOS +### Plugin Configuration (iOS) Once your Xcode project has been setup to build/link the CodePush plugin, you need to configure your app to consult CodePush for the location of your JS bundle, since it is responsible for synchronizing it with updates that are released to the CodePush server. To do this, perform the following steps: @@ -106,7 +103,29 @@ To let the CodePush runtime know which deployment it should query for updates ag 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) -## Plugin Configuration - Android +## Android Setup + +In order to integrate CodePush into your Android project, perform the following steps: + +### Plugin Installation (Android) + +1. In your `android/settings.gradle` file, make the following additions: + + ```gradle + include ':app', ':react-native-code-push' + project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app') + ``` +2. In your `android/app/build.gradle` file, add the `:react-native-code-push` project as a compile-time dependency: + + ```gradle + ... + dependencies { + ... + compile project(':react-native-code-push') + } + ``` + +### Plugin Configuration (Android) After installing the plugin and syncing your Android Studio project with Gradle, you need to configure your app to consult CodePush for the location of your JS bundle, since it will "take control" of managing the current and all future versions. To do this, perform the following steps: @@ -158,7 +177,7 @@ After installing the plugin and syncing your Android Studio project with Gradle, } ``` -## Plugin consumption +## Plugin Usage 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 necessary code to your app to control the following policies: @@ -181,16 +200,26 @@ The simplest way to do this is to perform the following in your app's root compo 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), which 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 +## Releasing Updates (JavaScript-only) -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: +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! If you're not using React Native to bundle images (via `require('./foo.png')`), then perform the following steps. Otherwise, skip to the next section instead: -1. Execute `react-native bundle` in order to generate the updated JS bundle for your app. -2. Execute `code-push release --deploymentName ` in order to publish the generated JS bundle to the server. Assuming your CWD is the root directory of your React Native app, `` could be `iOS/main.jsbundle` for iOS, or `android/app/src/main/assets/index.android.jsbundle` for Android. +1. Execute `react-native bundle` (passing the appropriate parameters) in order to generate the updated JS bundle for your app. You can place this file wherever you want via the `--bundle-output` flag, since the exact location isn't relevant for CodePush purposes. -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. Additionally, for more information regarding the CLI and how the release (or promote and rollback) commands work, refer to the [documentation](http://microsoft.github.io/code-push/docs/cli.html). +2. Execute `code-push release --deploymentName ` in order to publish the generated JS bundle to the server. The `` parameter should equal the value you provided to the `--bundle-output` flag in step #1. Additionally, the `` parameter should equal the exact app store version (i.e. the semver version end-users would see when installing it) you want this CodePush update to target. -## Releasing asset updates - iOS +Example Usage: + +``` +react-native bundle --platform ios --entry-file index.ios.js --bundle-output codepush.js +code-push release MyApp codepush.js 1.0.2 +``` + +And that's it! for more information regarding the CLI and how the release (or promote and rollback) commands work, refer to the [documentation](http://microsoft.github.io/code-push/docs/cli.html). + +*Note: Instead of running `react-native bundle`, you could rely on running `xcodebuild` and/or `gradlew assemble` instead to generate the JavaScript bundle file, but that would be unneccessarily doing a native build, when all you need for distributing updates via CodePush is your JavaScript bundle.* + +## Releasing Updates (JavaScript + images) If you are using the new React Native [assets system](https://facebook.github.io/react-native/docs/images.html#content), as opposed to loading your images from the network and/or platform-specific mechanisms (e.g. iOS asset catalogs), then you can't simply pass your jsbundle to CodePush as demonstrated above. You need to provide your images as well. To do this, simply use the following workflow: @@ -218,9 +247,9 @@ When you require `react-native-code-push`, the module object provides the follow * [checkForUpdate](#codepushcheckforupdate): Asks the CodePush service whether the configured app deployment has an update available. -* [getCurrentPackage](#codepushgetcurrentpackage): Gets information about the currently installed "package" (e.g. description, installation time, size). +* [getCurrentPackage](#codepushgetcurrentpackage): Retrieves the metadata about the currently installed update (e.g. description, installation time, size). -* [notifyApplicationReady](#codepushnotifyapplicationready): Notifies the CodePush runtime that an installed update is considered successful. This is an optional API, but is useful when you want to expicitly enable "rollback protection" in the event that an exception occurs in code that you've deployed to production. +* [notifyApplicationReady](#codepushnotifyapplicationready): Notifies the CodePush runtime that an installed update is considered successful. This is an optional API, but is useful when you want to expicitly enable "rollback protection" in the event that an exception occurs in code that you've deployed to production. * [restartApp](#codepushrestartapp): Immediately restarts the app. If there is an update pending, it will be immediately displayed to the end-user, and the rollback timer (if specified when installing the update) will begin. Otherwise, calling this method simply has the same behavior as the end-user killing and restarting the process. @@ -232,9 +261,9 @@ When you require `react-native-code-push`, the module object provides the follow codePush.checkForUpdate(deploymentKey: String = null): Promise; ``` -Queries the CodePush service to see whether the configured app deployment has an update available. By default, it will use the deployment key that is configured in your `Info.plist` file, but you can override that by specifying a value in the optional `deploymentKey` parameter. +Queries the CodePush service to see whether the configured app deployment has an update available. By default, it will use the deployment key that is configured in your `Info.plist` file (iOS), or `MainActivity.java` file (Android), but you can override that by specifying a value via the optional `deploymentKey` parameter. This can be useful when you want to dynamically "redirect" a user to a specific deployment, such as allowing "Early access" via an easter egg or a user setting switch. -This method returns a `Promise` which resolves to one of two values: +This method returns a `Promise` which resolves to one of two possible values: * `null` if there is no update available. * A `RemotePackage` instance which represents an available update that can be inspected and/or subsequently downloaded. @@ -282,10 +311,12 @@ codePush.getCurrentPackage() codePush.notifyApplicationReady(): Promise; ``` -Notifies the CodePush runtime that an update should be considered successful, and therefore, an automatic rollback isn't necessary. Calling this function is required whenever the `rollbackTimeout` parameter is specified when calling either ```LocalPackage.install``` or `sync`. If you specify a `rollbackTimeout`, and don't call `notifyApplicationReady`, the CodePush runtime will assume that the installed update has failed and roll back to the previous version. +Notifies the CodePush runtime that a freshly installed update should be considered successful, and therefore, an automatic client-side rollback isn't necessary. Calling this function is required whenever the `rollbackTimeout` parameter is specified when calling either ```LocalPackage.install``` or `sync`. If you specify a `rollbackTimeout`, and don't call `notifyApplicationReady`, the CodePush runtime will assume that the installed update has failed and roll back to the previous version. This behavior exists to help ensure that your end-users aren't blocked by a broken update. If the `rollbackTimeout` parameter was not specified, the CodePush runtime will not enforce any automatic rollback behavior, and therefore, calling this function is not required and will result in a no-op. +If you are using the `sync` function, and doing your update check on app start, then you don't need to manually call `notifyApplicationReady` since `sync` will call it for you. This behavior exists due to the assumption that the point at which `sync` is called in your app represents a good approximation of a successful startup. + ### codePush.restartApp ```javascript @@ -303,9 +334,9 @@ Immediately restarts the app. If there is an update pending, it will be presente codePush.sync(options: Object, syncStatusChangeCallback: function(syncStatus: Number), downloadProgressCallback: function(progress: DownloadProgress)): Promise; ``` -Synchronizes your app's JavaScript bundle with the latest release to the configured deployment. Unlike the `checkForUpdate` method, which simplies checks for the presence of an update, and let's you control what to do next, `sync` handles the update check, download and installation experience for you. +Synchronizes your app's JavaScript bundle and image assets with the latest release to the configured deployment. Unlike the `checkForUpdate` method, which simply checks for the presence of an update, and let's you control what to do next, `sync` handles the update check, download and installation experience for you. -This method provides support for two different (but customizable) "modes" to easily enables apps with different requirements: +This method provides support for two different (but customizable) "modes" to easily enable apps with different requirements: 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 "synthetic" app restarts. @@ -330,11 +361,11 @@ codePush.sync({ updateDialog: true, installMode: codePush.InstallMode.IMMEDIATE 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 mentioned above: -* __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`. +* __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 (iOS) and `MainActivity.java` file (Android), 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`. * __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`. Refer to the [`InstallMode`](#installmode) enum reference for a description of the available options and what they do. -* __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. Note that if you set this parameter to a non-zero number, then your app needs to call `notifyApplicationReady` in order to prevent the automatic rollback from happening. +* __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. Note that if you set this parameter to a non-zero number, and are calling `sync` during app start, you don't need to call `notifyApplicationReady`. However, if you're calling `sync` from a non-app start scenario (e.g. a button click), then you need to manually call `notifyApplicationReady` when specifying a rollback timeout. * __updateDialog__ *(UpdateDialogOptions)* - An "options" object used to determine whether a confirmation dialog should be displayed to the end-user when an update is available, and if so, what strings to use. Defaults to `null`, which has the effect of disabling the dialog completely. Setting this to any truthy value will enable the dialog with the default strings, and passing an object to this parameter allows enabling the dialog as well as overriding one or more of the default strings. The following list represents the available options and their defaults: * __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`.