From 902009f721a5eb2dd19aa0f397c20e464f2d1b89 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Fri, 15 Jul 2016 20:17:30 -0700 Subject: [PATCH 01/22] add codePushify decorator --- CodePush.js | 45 ++++++++++++++++++++++++++++- typings/react-native-code-push.d.ts | 30 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/CodePush.js b/CodePush.js index 288c254..10ddd71 100644 --- a/CodePush.js +++ b/CodePush.js @@ -3,7 +3,7 @@ import { Alert } from "./AlertAdapter"; import requestFetchAdapter from "./request-fetch-adapter"; import { AppState, Platform } from "react-native"; import RestartManager from "./RestartManager"; -import log from './logging'; +import log from "./logging"; let NativeCodePush = require("react-native").NativeModules.CodePush; const PackageMixins = require("./package-mixins")(NativeCodePush); @@ -401,6 +401,44 @@ async function syncInternal(options = {}, syncStatusChangeCallback, downloadProg let CodePush; +function codePushify(options = {}) { + return (RootComponent) => { + let React; + let ReactNative = require("react-native"); + + try { React = require("react"); } catch (e) { } + if (!React) { + try { React = ReactNative.React; } catch (e) { } + if (!React) { + throw new Error( +`Unable to find the 'Component' class, please either: +1. Upgrade to a newer version of React Native that supports it, or +2. Call the codePush.sync API in your component instead of using the @CodePushify decorator` + ); + } + } + + return class CodePushComponent extends React.Component { + componentDidMount() { + let rootComponentInstance = this.refs.rootComponent; + let syncStatusCallback = rootComponentInstance && rootComponentInstance.codePushStatusDidChange; + let downloadProgressCallback = rootComponentInstance && rootComponentInstance.codePushDownloadDidProgress; + + CodePush.sync(options, syncStatusCallback, downloadProgressCallback); + if (options.syncMode === CodePush.SyncMode.ON_APP_RESUME) { + ReactNative.AppState.addEventListener("change", (newState) => { + newState === "active" && CodePush.sync(options, syncStatusCallback, downloadProgressCallback); + }); + } + } + + render() { + return + } + } + } +} + // If the "NativeCodePush" variable isn't defined, then // the app didn't properly install the native module, // and therefore, it doesn't make sense initializing @@ -409,6 +447,7 @@ if (NativeCodePush) { CodePush = { AcquisitionSdk: Sdk, checkForUpdate, + codePushify, getConfiguration, getCurrentPackage, getUpdateMetadata, @@ -436,6 +475,10 @@ if (NativeCodePush) { DOWNLOADING_PACKAGE: 7, INSTALLING_UPDATE: 8 }, + SyncMode: { + ON_APP_START: 0, + ON_APP_RESUME: 1, + }, UpdateState: { RUNNING: NativeCodePush.codePushUpdateStateRunning, PENDING: NativeCodePush.codePushUpdateStatePending, diff --git a/typings/react-native-code-push.d.ts b/typings/react-native-code-push.d.ts index cf3bacc..5c57753 100644 --- a/typings/react-native-code-push.d.ts +++ b/typings/react-native-code-push.d.ts @@ -20,6 +20,14 @@ interface Promise { export type DowloadProgressCallback = (progress: DownloadProgress) => void; export type SyncStatusChangedCallback = (status: CodePush.SyncStatus) => void; +export interface CodePushOptions extends SyncOptions { + /** + * Specifies when you would like to synchronize updates with the CodePush server. + * Defaults to codePush.SyncMode.ON_APP_START. + */ + syncMode: CodePush.SyncMode; +} + export interface DownloadProgress { /** * The total number of bytes expected to be received for this update. @@ -233,6 +241,13 @@ declare namespace CodePush { */ function checkForUpdate(deploymentKey?: string): Promise; + /** + * Decorates a React Component configuring it to sync for updates with the CodePush server. + * + * @param options Options used to configure the end-user sync and update experience (e.g. when to check for updates?, show an prompt?, install the update immediately?). + */ + function CodePushify(options?: CodePushOptions): Function; + /** * Retrieves the metadata for an installed update (e.g. description, mandatory). * @@ -383,6 +398,21 @@ declare namespace CodePush { */ SUCCEEDED } + + /** + * Indicates when you would like to synchronize updates with the CodePush server. + */ + enum SyncMode { + /** + * When the app is fully initialized (or more specifically, when the root component is mounted). + */ + ON_APP_START, + + /** + * When the app re-enters the foreground. + */ + ON_APP_RESUME + } } export default CodePush; From d779741ccf96d3f441e746de7c148c0d210031c7 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Fri, 15 Jul 2016 20:24:18 -0700 Subject: [PATCH 02/22] lower case codePushify --- typings/react-native-code-push.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/react-native-code-push.d.ts b/typings/react-native-code-push.d.ts index 5c57753..c8730f9 100644 --- a/typings/react-native-code-push.d.ts +++ b/typings/react-native-code-push.d.ts @@ -246,7 +246,7 @@ declare namespace CodePush { * * @param options Options used to configure the end-user sync and update experience (e.g. when to check for updates?, show an prompt?, install the update immediately?). */ - function CodePushify(options?: CodePushOptions): Function; + function codePushify(options?: CodePushOptions): Function; /** * Retrieves the metadata for an installed update (e.g. description, mandatory). From 53a6071605ee04b940a56ff5f48895e48c6c3c81 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Sat, 16 Jul 2016 01:02:11 -0700 Subject: [PATCH 03/22] Update README.md --- README.md | 193 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 178 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7245ca5..d86290a 100644 --- a/README.md +++ b/README.md @@ -404,33 +404,87 @@ With the CodePush plugin downloaded and linked, and your app asking CodePush whe 2. When an update is available, how to present it to the end user? -The simplest way to do this is to perform the following in your app's root component: +The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following three options: -1. Import the JavaScript module for CodePush: +1. Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax: + + ```javascript + import codePush, { codePushify } from "react-native-code-push"; + + let codePushOptions; + + @codePushify(CodePushOptions) + class MyApp extends Component { + } + ``` + + *NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by doing the following: + - Install the [`babel-preset-react-native-stage-0` package](https://github.com/skevy/babel-preset-react-native-stage-0) + + ``` + npm install babel-preset-react-native-stage-0 --save-dev + ``` + + - In your `.babelrc` file, include the following: + + ``` + { + "presets": ["react-native-stage-0/decorator-support"] + } + ``` + +2. Call `codePushify` on your root component and assign the result to itself after the component declaration: + + ```javascript + import codePush, { codePushify } from "react-native-code-push"; + + let codePushOptions; + + class MyApp extends Component { + } + + MyApp = codePushify(codePushOptions)(MyApp); + ``` + +3. Manually call the `sync` method from within the `componentDidMount` lifecycle event, to initiate a background update on each app start: ```javascript import codePush from "react-native-code-push"; - ``` -2. Call the `sync` method from within the `componentDidMount` lifecycle event, to initiate a background update on each app start: - - ```javascript - codePush.sync(); + class MyApp extends Component { + componentDidMount() { + let syncOptions; + + codePush.sync(CodePushOptions); + } + } ``` 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. -If you would like your app to discover updates more quickly, you can also choose to call `sync` every time the app resumes from the background, by adding the following code (or something equivalent) as part of your app's startup behavior (e.g. your root component's `componentDidMount` method). You can call `sync` as frequently as you would like, so when and where you call it just depends on your personal preference. +If you would like your app to discover updates more quickly, you can also choose to sync up with the CodePush server every time the app resumes from the background. -```javascript -AppState.addEventListener("change", (newState) => { - newState === "active" && codePush.sync(); -}); -``` +If you are using the `codePushify` decorator function: -*NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in simpler/more idiomatic way.* + ```javascript + let codePushOptions = { syncMode: codePush.SyncMode.ON_APP_START }; + + @codePushify(CodePushOptions) + class MyApp extends Component { + } + ``` -Additionally, if you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) 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 you are calling `sync` directly: + + ```javascript + AppState.addEventListener("change", (newState) => { + newState === "active" && codePush.sync(); + }); + ``` + +*NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* + +Additionally, if you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush.codePushify()`](#codepushcodepushify) or [`codePush.sync()`](#codepushsync) API reference for information on how to tweak this default behavior. *NOTE: While [Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf) fully allows performing over-the-air updates of JavaScript and assets (which is what enables CodePush!), it is against their policy for an app to display an update prompt. Because of this, we recommend that App Store-distributed apps don't enable the `updateDialog` option when calling `sync`, whereas Google Play and internally distributed apps (e.g. Enterprise, Fabric, HockeyApp) can choose to enable/customize it.* @@ -648,6 +702,8 @@ 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. +* [codePushify](#codepushcodepushify): Returns a function that wraps a React component inside a "higher order" React component configured to call [`codePush.sync()`](#codepushsync) when it is mounted. This allows you to declaratively specify the behaviour of how and when your app checks for an update, downloads it and installs it. + * [disallowRestart](#codepushdisallowrestart): Temporarily disallows any programmatic restarts to occur as a result of a CodePush update being installed. This is an advanced API, and is useful when a component within your app (e.g. an onboarding process) needs to ensure that no end-user interruptions can occur during its lifetime. * [getCurrentPackage](#codepushgetcurrentpackage): Retrieves the metadata about the currently installed update (e.g. description, installation time, size). *NOTE: As of `v1.10.3-beta` of the CodePush module, this method is deprecated in favor of [`getUpdateMetadata`](#codepushgetupdatemetadata)*. @@ -717,6 +773,103 @@ codePush.checkForUpdate() }); ``` +#### codePush.codePushify + +```javascript +codePush.codePushify(options: CodePushOptions)(rootComponent: React.Component): React.Component; +``` + +Used as a decorator to wrap a React component inside a "higher order" React component that knows how to synchronize your app's JavaScript bundle and image assets with the latest release to the configured deployment when it is mounted. Internally, the wrapper component calls [`sync`](#codepushsync) inside its `componentDidMount` lifecycle handle, which in turns performs an update check, downloads the update if it exists and installs the update for you. + +This decorator provides support for letting you customize its behaviour to easily enable apps with different requirements. Below are some examples of ways you can use it (you can pick one or even use a combination): + +1. **Silent sync on app start** *(the simplest, default behavior)*. Your app will automatically download available updates, and apply them the next time the app restarts (e.g. the OS or end user killed it, or the device was restarted). 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. + + ```javascript + // Fully silent update which keeps the app in + // sync with the server, without ever + // interrupting the end user + @codePushify() + class MyApp extends Component {} + ``` + +2. **Silent sync everytime the app resumes**. Same as 1, except we check for updates, or apply an update if one exists every time the app returns to the foreground after being "backgrounded". + + ```javascript + // Sync for updates everytime the app resumes. + @codePushify({ syncMode: codePush.SyncMode.ON_APP_RESUME, installMode: codePush.InstallMode.ON_NEXT_RESUME }) + class MyApp extends Component {} + ``` + +3. **Interactive**. When an update is available, prompt the end user for permission before downloading it, and then immediately apply 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. + + ```javascript + // Active update, which lets the end user know + // about each update, and displays it to them + // immediately after downloading it + @codePushify({ updateDialog: true, installMode: codePush.InstallMode.IMMEDIATE }) + class MyApp extends Component {} + ``` + +4. **Log/display progress**. While the app is syncing with the server for updates, make use of the `codePushStatusDidChange` and/or `codePushDownloadDidProgress` event hooks to log down the different stages of this process, or even display a progress bar to the user. + + ```javascript + @codePushify() + class MyApp extends Component { + codePushStatusDidChange(status) { + switch(syncStatus) { + case codePush.SyncStatus.CHECKING_FOR_UPDATE: + console.log("Checking for updates."); + break; + case CodePush.SyncStatus.DOWNLOADING_PACKAGE: + console.log("Downloading package."); + break; + case CodePush.SyncStatus.INSTALLING_UPDATE: + console.log("Installing update."); + break; + case CodePush.SyncStatus.UP_TO_DATE: + console.log("Installing update."); + break; + case CodePush.SyncStatus.UPDATE_INSTALLED: + console.log("Update installed."); + break; + } + } + + codePushDownloadDidProgress(progress) { + console.log(progess.receivedBytes + " of " + progress.totalBytes + " received."); + } + } + ``` + +##### CodePushOptions + +The `codePushify` decorator accepts an "options" object that allows you to customize numerous aspects of the default behavior mentioned above: + +* __syncMode__ *(codePush.SyncMode)* - Specifies when you would like to check for updates. Defaults to `codePush.SyncMode.ON_APP_START`. Refer to the [`SyncMode`](#syncmode) enum reference for a description of the available options and what they do. + +* __deploymentKey__ *(String)* - Refer to [`SyncOptions`](#syncoptions). + +* __installMode__ *(codePush.InstallMode)* - Refer to [`SyncOptions`](#syncoptions). + +* __mandatoryInstallMode__ *(codePush.InstallMode)* - Refer to [`SyncOptions`](#syncoptions). + +* __minimumBackgroundDuration__ *(Number)* - Refer to [`SyncOptions`](#syncoptions). + +* __updateDialog__ *(UpdateDialogOptions)* - Refer to [`SyncOptions`](#syncoptions). + +##### codePushStatusDidChange (event hook) + +Called when the sync process moves from one stage to another in the overall update process. The event hook is called with a status code which represents the current state, and can be any of the [`SyncStatus`](#syncstatus) values. + +##### codePushDownloadDidProgress (event hook) + +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 (i.e. the size of the set of files which changed from the previous release). + +* __receivedBytes__ *(Number)* - The number of bytes downloaded thus far, which can be used to track download progress. + #### codePush.disallowRestart ```javascript @@ -891,6 +1044,8 @@ codePush.sync({ updateDialog: true, installMode: codePush.InstallMode.IMMEDIATE *Note: If you want to decide 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.* +##### SyncOptions + 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 (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`. @@ -1053,6 +1208,14 @@ This enum specifies when you would like an installed update to actually be appli * __codePush.InstallMode.ON_NEXT_RESUME__ *(2)* - Indicates that you want to install the update, but don't want to restart the app until the next time the end user resumes it from the background. This way, you don't disrupt their current session, but you can get the update in front of them sooner then having to wait for the next natural restart. This value is appropriate for silent installs that can be applied on resume in a non-invasive way. +##### SyncMode + +This enum specifies when you would like your app to sync with the server for updates, and can be passed to the `codePushify` decorator. It includes the following values: + +* __codePush.SyncMode.ON_APP_START__ *(0)* - Indicates that you want to check for updates whenever the app's process is started. + +* __codePush.InstallMode.ON_APP_RESUME__ *(1)* - Indicates that you want to check for updates whenever the app is brought back to the foreground after being "backgrounded" (user pressed the home button, app launches a seperate payment process, etc). + ##### SyncStatus This enum is provided to the `syncStatusChangedCallback` function that can be passed to the `sync` method, in order to hook into the overall update process. It includes the following values: From 36cc292dd6d05f5b6a03af9f31ab43b0a67b8980 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Sat, 16 Jul 2016 01:06:07 -0700 Subject: [PATCH 04/22] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d86290a..146b46f 100644 --- a/README.md +++ b/README.md @@ -467,7 +467,7 @@ If you would like your app to discover updates more quickly, you can also choose If you are using the `codePushify` decorator function: ```javascript - let codePushOptions = { syncMode: codePush.SyncMode.ON_APP_START }; + let codePushOptions = { syncMode: codePush.SyncMode.ON_APP_RESUME }; @codePushify(CodePushOptions) class MyApp extends Component { @@ -814,6 +814,8 @@ This decorator provides support for letting you customize its behaviour to easil 4. **Log/display progress**. While the app is syncing with the server for updates, make use of the `codePushStatusDidChange` and/or `codePushDownloadDidProgress` event hooks to log down the different stages of this process, or even display a progress bar to the user. ```javascript + // Make use of the event hooks to keep track of + // the different stages of the sync process. @codePushify() class MyApp extends Component { codePushStatusDidChange(status) { From f92acc6f55f4a241292c9016f568c0838c4bb75f Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Sat, 16 Jul 2016 12:16:19 -0700 Subject: [PATCH 05/22] update error handling --- CodePush.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CodePush.js b/CodePush.js index 10ddd71..de678a6 100644 --- a/CodePush.js +++ b/CodePush.js @@ -410,6 +410,9 @@ function codePushify(options = {}) { if (!React) { try { React = ReactNative.React; } catch (e) { } if (!React) { + throw new Error("Unable to find the 'React' module."); + } + if (!React.Component) { throw new Error( `Unable to find the 'Component' class, please either: 1. Upgrade to a newer version of React Native that supports it, or @@ -423,8 +426,7 @@ function codePushify(options = {}) { let rootComponentInstance = this.refs.rootComponent; let syncStatusCallback = rootComponentInstance && rootComponentInstance.codePushStatusDidChange; let downloadProgressCallback = rootComponentInstance && rootComponentInstance.codePushDownloadDidProgress; - - CodePush.sync(options, syncStatusCallback, downloadProgressCallback); + CodePush.sync(options, syncStatusCallback, downloadProgressCallback) if (options.syncMode === CodePush.SyncMode.ON_APP_RESUME) { ReactNative.AppState.addEventListener("change", (newState) => { newState === "active" && CodePush.sync(options, syncStatusCallback, downloadProgressCallback); From 67f211a06ad9d703738c653ff9aee80b8100a7c9 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Sat, 16 Jul 2016 12:23:35 -0700 Subject: [PATCH 06/22] Update README.md --- README.md | 90 +++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 146b46f..c746f63 100644 --- a/README.md +++ b/README.md @@ -406,59 +406,59 @@ With the CodePush plugin downloaded and linked, and your app asking CodePush whe The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following three options: -1. Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax: +**Option 1: Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax:** - ```javascript - import codePush, { codePushify } from "react-native-code-push"; +```javascript +import codePush, { codePushify } from "react-native-code-push"; + +let codePushOptions; + +@codePushify(codePushOptions) +class MyApp extends Component { +} +``` + +*NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by doing the following: +- Install the [`babel-preset-react-native-stage-0` package](https://github.com/skevy/babel-preset-react-native-stage-0) - let codePushOptions; - - @codePushify(CodePushOptions) - class MyApp extends Component { + ``` + npm install babel-preset-react-native-stage-0 --save-dev + ``` + +- In your `.babelrc` file, include the following: + + ``` + { + "presets": ["react-native-stage-0/decorator-support"] } ``` - *NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by doing the following: - - Install the [`babel-preset-react-native-stage-0` package](https://github.com/skevy/babel-preset-react-native-stage-0) - - ``` - npm install babel-preset-react-native-stage-0 --save-dev - ``` - - - In your `.babelrc` file, include the following: - - ``` - { - "presets": ["react-native-stage-0/decorator-support"] - } - ``` - -2. Call `codePushify` on your root component and assign the result to itself after the component declaration: +**Option 2: Call `codePushify` on your root component and assign the result to itself after the component declaration:** - ```javascript - import codePush, { codePushify } from "react-native-code-push"; +```javascript +import codePush, { codePushify } from "react-native-code-push"; - let codePushOptions; - - class MyApp extends Component { +let codePushOptions; + +class MyApp extends Component { +} + +MyApp = codePushify(codePushOptions)(MyApp); +``` + +**Option 3: Manually call the `sync` method from within the `componentDidMount` lifecycle event, to initiate a background update on each app start:** + +```javascript +import codePush from "react-native-code-push"; + +class MyApp extends Component { + componentDidMount() { + let syncOptions; + + codePush.sync(syncOptions); } - - MyApp = codePushify(codePushOptions)(MyApp); - ``` - -3. Manually call the `sync` method from within the `componentDidMount` lifecycle event, to initiate a background update on each app start: - - ```javascript - import codePush from "react-native-code-push"; - - class MyApp extends Component { - componentDidMount() { - let syncOptions; - - codePush.sync(CodePushOptions); - } - } - ``` +} +``` 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. From 4326515f283874701d3864eb205956a8a6674761 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 16:17:46 -0700 Subject: [PATCH 07/22] CR feedback --- CodePush.js | 28 ++- README.md | 355 ++++++++++++++-------------- typings/react-native-code-push.d.ts | 15 +- 3 files changed, 205 insertions(+), 193 deletions(-) diff --git a/CodePush.js b/CodePush.js index de678a6..e626fa6 100644 --- a/CodePush.js +++ b/CodePush.js @@ -423,14 +423,18 @@ function codePushify(options = {}) { return class CodePushComponent extends React.Component { componentDidMount() { - let rootComponentInstance = this.refs.rootComponent; - let syncStatusCallback = rootComponentInstance && rootComponentInstance.codePushStatusDidChange; - let downloadProgressCallback = rootComponentInstance && rootComponentInstance.codePushDownloadDidProgress; - CodePush.sync(options, syncStatusCallback, downloadProgressCallback) - if (options.syncMode === CodePush.SyncMode.ON_APP_RESUME) { - ReactNative.AppState.addEventListener("change", (newState) => { - newState === "active" && CodePush.sync(options, syncStatusCallback, downloadProgressCallback); - }); + if (options.checkFrequency === CodePush.CheckFrequency.MANUAL) { + CodePush.notifyAppReady(); + } else { + let rootComponentInstance = this.refs.rootComponent; + let syncStatusCallback = rootComponentInstance && rootComponentInstance.codePushStatusDidChange; + let downloadProgressCallback = rootComponentInstance && rootComponentInstance.codePushDownloadDidProgress; + CodePush.sync(options, syncStatusCallback, downloadProgressCallback) + if (options.checkFrequency === CodePush.CheckFrequency.ON_APP_RESUME) { + ReactNative.AppState.addEventListener("change", (newState) => { + newState === "active" && CodePush.sync(options, syncStatusCallback, downloadProgressCallback); + }); + } } } @@ -446,7 +450,8 @@ function codePushify(options = {}) { // and therefore, it doesn't make sense initializing // the JS interface when it wouldn't work anyways. if (NativeCodePush) { - CodePush = { + CodePush = codePushify; + Object.assign(CodePush, { AcquisitionSdk: Sdk, checkForUpdate, codePushify, @@ -477,9 +482,10 @@ if (NativeCodePush) { DOWNLOADING_PACKAGE: 7, INSTALLING_UPDATE: 8 }, - SyncMode: { + CheckFrequency: { ON_APP_START: 0, ON_APP_RESUME: 1, + MANUAL: 2 }, UpdateState: { RUNNING: NativeCodePush.codePushUpdateStateRunning, @@ -500,7 +506,7 @@ if (NativeCodePush) { optionalUpdateMessage: "An update is available. Would you like to install it?", title: "Update available" } - }; + }); } else { log("The CodePush module doesn't appear to be properly installed. Please double-check that everything is setup correctly."); } diff --git a/README.md b/README.md index c746f63..e8c1bc3 100644 --- a/README.md +++ b/README.md @@ -406,85 +406,84 @@ With the CodePush plugin downloaded and linked, and your app asking CodePush whe The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following three options: -**Option 1: Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax:** +**Option 1: Call `codePush` on your root component and assign the result to itself after the component declaration:** ```javascript -import codePush, { codePushify } from "react-native-code-push"; +import codePush from "react-native-code-push"; let codePushOptions; -@codePushify(codePushOptions) +class MyApp extends Component { +} + +MyApp = codePush(codePushOptions)(MyApp); +``` + +**Option 2: Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax:** + +```javascript +import codePush from "react-native-code-push"; + +let codePushOptions; + +@codePush(codePushOptions) class MyApp extends Component { } ``` *NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by doing the following: - Install the [`babel-preset-react-native-stage-0` package](https://github.com/skevy/babel-preset-react-native-stage-0) - + ``` npm install babel-preset-react-native-stage-0 --save-dev ``` - + - In your `.babelrc` file, include the following: - + ``` { "presets": ["react-native-stage-0/decorator-support"] } ``` -**Option 2: Call `codePushify` on your root component and assign the result to itself after the component declaration:** - -```javascript -import codePush, { codePushify } from "react-native-code-push"; - -let codePushOptions; - -class MyApp extends Component { -} - -MyApp = codePushify(codePushOptions)(MyApp); -``` - -**Option 3: Manually call the `sync` method from within the `componentDidMount` lifecycle event, to initiate a background update on each app start:** - -```javascript -import codePush from "react-native-code-push"; - -class MyApp extends Component { - componentDidMount() { - let syncOptions; - - codePush.sync(syncOptions); - } -} -``` - 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. If you would like your app to discover updates more quickly, you can also choose to sync up with the CodePush server every time the app resumes from the background. -If you are using the `codePushify` decorator function: +```javascript +let codePushOptions = { checkFrequency: codePush.CheckFrequency.ON_APP_RESUME }; - ```javascript - let codePushOptions = { syncMode: codePush.SyncMode.ON_APP_RESUME }; - - @codePushify(CodePushOptions) - class MyApp extends Component { - } - ``` +class MyApp extends Component { +} -If you are calling `sync` directly: - - ```javascript - AppState.addEventListener("change", (newState) => { - newState === "active" && codePush.sync(); - }); - ``` +MyApp = codePush(CodePushOptions)(MyApp); +``` + +Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a `MANUAL` check frequency and do the following: + +```javascript +let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL }; + +class MyApp extends Component { + onButtonPress() { + codePush.sync(); + } + + render() { + + + Check for updates + + + } +} + +MyApp = codePush(CodePushOptions)(MyApp); +``` *NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* -Additionally, if you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush.codePushify()`](#codepushcodepushify) or [`codePush.sync()`](#codepushsync) API reference for information on how to tweak this default behavior. +Additionally, if you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush()`](#codepush) API reference for information on how to tweak this default behavior. *NOTE: While [Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf) fully allows performing over-the-air updates of JavaScript and assets (which is what enables CodePush!), it is against their policy for an app to display an update prompt. Because of this, we recommend that App Store-distributed apps don't enable the `updateDialog` option when calling `sync`, whereas Google Play and internally distributed apps (e.g. Enterprise, Fabric, HockeyApp) can choose to enable/customize it.* @@ -654,7 +653,7 @@ Additionally, if you want to give them seperate names and/or icons, you can modi The above section illustrated how you can leverage multiple CodePush deployments in order to effectively test your updates before broadly releasing them to your end users. However, since that workflow statically embeds the deployment assignment into the actual binary, a staging or production build will only ever sync updates from that deployment. In many cases, this is sufficient, since you only want your team, customers, stakeholders, etc. to sync with your pre-production releases, and therefore, only they need a build that knows how to sync with staging. However, if you want to be able to perform A/B tests, or provide early access of your app to certain users, it can prove very useful to be able to dynamically place specific users (or audiences) into specific deployments at runtime. -In order to achieve this kind of workflow, all you need to do is specify the deployment key you want the current user to syncronize with when calling the `checkForUpdate` or `sync` methods. When specified, this key will override the "default" one that was provided in your app's `Info.plist` (iOS) or `MainActivity.java` (Android) files. This allows you to produce a build for staging or production, that is also capabable of being dynamically "redirected" as needed. +In order to achieve this kind of workflow, all you need to do is specify the deployment key you want the current user to syncronize with when calling the `codePush` method. When specified, this key will override the "default" one that was provided in your app's `Info.plist` (iOS) or `MainActivity.java` (Android) files. This allows you to produce a build for staging or production, that is also capable of being dynamically "redirected" as needed. ```javascript // Imagine that "userProfile" is a prop that this component received @@ -716,6 +715,123 @@ When you require `react-native-code-push`, the module object provides the follow * [sync](#codepushsync): Allows checking for an update, downloading it and installing it, all with a single call. Unless you need custom UI and/or behavior, we recommend most developers to use this method when integrating CodePush into their apps +#### codePush + +```javascript +codePush(options: CodePushOptions)(rootComponent: React.Component): React.Component; +``` + +Used as a decorator to wrap a React component inside a "higher order" React component that knows how to synchronize your app's JavaScript bundle and image assets with the latest release to the configured deployment when it is mounted. Internally, the wrapper component calls [`sync`](#codepushsync) inside its `componentDidMount` lifecycle handle, which in turns performs an update check, downloads the update if it exists and installs the update for you. + +This decorator provides support for letting you customize its behaviour to easily enable apps with different requirements. Below are some examples of ways you can use it (you can pick one or even use a combination): + +1. **Silent sync on app start** *(the simplest, default behavior)*. Your app will automatically download available updates, and apply them the next time the app restarts (e.g. the OS or end user killed it, or the device was restarted). 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. + + ```javascript + // Fully silent update which keeps the app in + // sync with the server, without ever + // interrupting the end user + @codePush() + class MyApp extends Component {} + ``` + +2. **Silent sync everytime the app resumes**. Same as 1, except we check for updates, or apply an update if one exists every time the app returns to the foreground after being "backgrounded". + + ```javascript + // Sync for updates everytime the app resumes. + @codePush({ checkFrequency: codePush.CheckFrequency.ON_APP_RESUME, installMode: codePush.InstallMode.ON_NEXT_RESUME }) + class MyApp extends Component {} + ``` + +3. **Interactive**. When an update is available, prompt the end user for permission before downloading it, and then immediately apply 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. + + ```javascript + // Active update, which lets the end user know + // about each update, and displays it to them + // immediately after downloading it + @codePush({ updateDialog: true, installMode: codePush.InstallMode.IMMEDIATE }) + class MyApp extends Component {} + ``` + +4. **Log/display progress**. While the app is syncing with the server for updates, make use of the `codePushStatusDidChange` and/or `codePushDownloadDidProgress` event hooks to log down the different stages of this process, or even display a progress bar to the user. + + ```javascript + // Make use of the event hooks to keep track of + // the different stages of the sync process. + @codePush() + class MyApp extends Component { + codePushStatusDidChange(status) { + switch(syncStatus) { + case codePush.SyncStatus.CHECKING_FOR_UPDATE: + console.log("Checking for updates."); + break; + case codePush.SyncStatus.DOWNLOADING_PACKAGE: + console.log("Downloading package."); + break; + case codePush.SyncStatus.INSTALLING_UPDATE: + console.log("Installing update."); + break; + case codePush.SyncStatus.UP_TO_DATE: + console.log("Installing update."); + break; + case codePush.SyncStatus.UPDATE_INSTALLED: + console.log("Update installed."); + break; + } + } + + codePushDownloadDidProgress(progress) { + console.log(progess.receivedBytes + " of " + progress.totalBytes + " received."); + } + } + ``` + +##### CodePushOptions + +The `codePush` decorator accepts an "options" object that allows you to customize numerous aspects of the default behavior mentioned above: + +* __checkFrequency__ *(codePush.CheckFrequency)* - Specifies when you would like to check for updates. Defaults to `codePush.CheckFrequency.ON_APP_START`. Refer to the [`CheckFrequency`](#checkfrequency) enum reference for a description of the available options and what they do. + +* __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. + +* __installMode__ *(codePush.InstallMode)* - Specifies when you would like to install optional updates (i.e. those that aren't marked as mandatory). 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. + +* __mandatoryInstallMode__ *(codePush.InstallMode)* - Specifies when you would like to install updates which are marked as mandatory. Defaults to `codePush.InstallMode.IMMEDIATE`. Refer to the [`InstallMode`](#installmode) enum reference for a description of the available options and what they do. + +* __minimumBackgroundDuration__ *(Number)* - Specifies the minimum number of seconds that the app needs to have been in the background before restarting the app. This property only applies to updates which are installed using `InstallMode.ON_NEXT_RESUME`, and can be useful for getting your update in front of end users sooner, without being too obtrusive. Defaults to `0`, which has the effect of applying the update immediately after a resume, regardless how long it was in the background. + +* __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. Before enabling this option within an App Store-distributed app, please refer to [this note](#user-content-apple-note). + + 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`. + + * __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"`. + +##### codePushStatusDidChange (event hook) + +Called when the sync process moves from one stage to another in the overall update process. The event hook is called with a status code which represents the current state, and can be any of the [`SyncStatus`](#syncstatus) values. + +##### codePushDownloadDidProgress (event hook) + +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 (i.e. the size of the set of files which changed from the previous release). + +* __receivedBytes__ *(Number)* - The number of bytes downloaded thus far, which can be used to track download progress. + #### codePush.allowRestart ```javascript @@ -773,105 +889,6 @@ codePush.checkForUpdate() }); ``` -#### codePush.codePushify - -```javascript -codePush.codePushify(options: CodePushOptions)(rootComponent: React.Component): React.Component; -``` - -Used as a decorator to wrap a React component inside a "higher order" React component that knows how to synchronize your app's JavaScript bundle and image assets with the latest release to the configured deployment when it is mounted. Internally, the wrapper component calls [`sync`](#codepushsync) inside its `componentDidMount` lifecycle handle, which in turns performs an update check, downloads the update if it exists and installs the update for you. - -This decorator provides support for letting you customize its behaviour to easily enable apps with different requirements. Below are some examples of ways you can use it (you can pick one or even use a combination): - -1. **Silent sync on app start** *(the simplest, default behavior)*. Your app will automatically download available updates, and apply them the next time the app restarts (e.g. the OS or end user killed it, or the device was restarted). 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. - - ```javascript - // Fully silent update which keeps the app in - // sync with the server, without ever - // interrupting the end user - @codePushify() - class MyApp extends Component {} - ``` - -2. **Silent sync everytime the app resumes**. Same as 1, except we check for updates, or apply an update if one exists every time the app returns to the foreground after being "backgrounded". - - ```javascript - // Sync for updates everytime the app resumes. - @codePushify({ syncMode: codePush.SyncMode.ON_APP_RESUME, installMode: codePush.InstallMode.ON_NEXT_RESUME }) - class MyApp extends Component {} - ``` - -3. **Interactive**. When an update is available, prompt the end user for permission before downloading it, and then immediately apply 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. - - ```javascript - // Active update, which lets the end user know - // about each update, and displays it to them - // immediately after downloading it - @codePushify({ updateDialog: true, installMode: codePush.InstallMode.IMMEDIATE }) - class MyApp extends Component {} - ``` - -4. **Log/display progress**. While the app is syncing with the server for updates, make use of the `codePushStatusDidChange` and/or `codePushDownloadDidProgress` event hooks to log down the different stages of this process, or even display a progress bar to the user. - - ```javascript - // Make use of the event hooks to keep track of - // the different stages of the sync process. - @codePushify() - class MyApp extends Component { - codePushStatusDidChange(status) { - switch(syncStatus) { - case codePush.SyncStatus.CHECKING_FOR_UPDATE: - console.log("Checking for updates."); - break; - case CodePush.SyncStatus.DOWNLOADING_PACKAGE: - console.log("Downloading package."); - break; - case CodePush.SyncStatus.INSTALLING_UPDATE: - console.log("Installing update."); - break; - case CodePush.SyncStatus.UP_TO_DATE: - console.log("Installing update."); - break; - case CodePush.SyncStatus.UPDATE_INSTALLED: - console.log("Update installed."); - break; - } - } - - codePushDownloadDidProgress(progress) { - console.log(progess.receivedBytes + " of " + progress.totalBytes + " received."); - } - } - ``` - -##### CodePushOptions - -The `codePushify` decorator accepts an "options" object that allows you to customize numerous aspects of the default behavior mentioned above: - -* __syncMode__ *(codePush.SyncMode)* - Specifies when you would like to check for updates. Defaults to `codePush.SyncMode.ON_APP_START`. Refer to the [`SyncMode`](#syncmode) enum reference for a description of the available options and what they do. - -* __deploymentKey__ *(String)* - Refer to [`SyncOptions`](#syncoptions). - -* __installMode__ *(codePush.InstallMode)* - Refer to [`SyncOptions`](#syncoptions). - -* __mandatoryInstallMode__ *(codePush.InstallMode)* - Refer to [`SyncOptions`](#syncoptions). - -* __minimumBackgroundDuration__ *(Number)* - Refer to [`SyncOptions`](#syncoptions). - -* __updateDialog__ *(UpdateDialogOptions)* - Refer to [`SyncOptions`](#syncoptions). - -##### codePushStatusDidChange (event hook) - -Called when the sync process moves from one stage to another in the overall update process. The event hook is called with a status code which represents the current state, and can be any of the [`SyncStatus`](#syncstatus) values. - -##### codePushDownloadDidProgress (event hook) - -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 (i.e. the size of the set of files which changed from the previous release). - -* __receivedBytes__ *(Number)* - The number of bytes downloaded thus far, which can be used to track download progress. - #### codePush.disallowRestart ```javascript @@ -1048,35 +1065,17 @@ codePush.sync({ updateDialog: true, installMode: codePush.InstallMode.IMMEDIATE ##### SyncOptions -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: +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. The options available are identical to the [CodePushOptions](#codepushoptions), with the exception of the `checkFrequency` option: -* __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`. +* __deploymentKey__ *(String)* - Refer to [`CodePushOptions`](#codepushoptions). -* __installMode__ *(codePush.InstallMode)* - Specifies when you would like to install optional updates (i.e. those that aren't marked as mandatory). 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. +* __installMode__ *(codePush.InstallMode)* - Refer to [`CodePushOptions`](#codepushoptions). -* __mandatoryInstallMode__ *(codePush.InstallMode)* - Specifies when you would like to install updates which are marked as mandatory. Defaults to `codePush.InstallMode.IMMEDIATE`. Refer to the [`InstallMode`](#installmode) enum reference for a description of the available options and what they do. +* __mandatoryInstallMode__ *(codePush.InstallMode)* - Refer to [`CodePushOptions`](#codepushoptions). -* __minimumBackgroundDuration__ *(Number)* - Specifies the minimum number of seconds that the app needs to have been in the background before restarting the app. This property only applies to updates which are installed using `InstallMode.ON_NEXT_RESUME`, and can be useful for getting your update in front of end users sooner, without being too obtrusive. Defaults to `0`, which has the effect of applying the update immediately after a resume, regardless how long it was in the background. +* __minimumBackgroundDuration__ *(Number)* - Refer to [`CodePushOptions`](#codepushoptions). -* __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. Before enabling this option within an App Store-distributed app, please refer to [this note](#user-content-apple-note). - - 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`. - - * __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"`. +* __updateDialog__ *(UpdateDialogOptions)* - Refer to [`CodePushOptions`](#codepushoptions). Example Usage: @@ -1210,13 +1209,15 @@ This enum specifies when you would like an installed update to actually be appli * __codePush.InstallMode.ON_NEXT_RESUME__ *(2)* - Indicates that you want to install the update, but don't want to restart the app until the next time the end user resumes it from the background. This way, you don't disrupt their current session, but you can get the update in front of them sooner then having to wait for the next natural restart. This value is appropriate for silent installs that can be applied on resume in a non-invasive way. -##### SyncMode +##### CheckFrequency This enum specifies when you would like your app to sync with the server for updates, and can be passed to the `codePushify` decorator. It includes the following values: -* __codePush.SyncMode.ON_APP_START__ *(0)* - Indicates that you want to check for updates whenever the app's process is started. +* __codePush.CheckFrequency.ON_APP_START__ *(0)* - Indicates that you want to check for updates whenever the app's process is started. -* __codePush.InstallMode.ON_APP_RESUME__ *(1)* - Indicates that you want to check for updates whenever the app is brought back to the foreground after being "backgrounded" (user pressed the home button, app launches a seperate payment process, etc). +* __codePush.CheckFrequency.ON_APP_RESUME__ *(1)* - Indicates that you want to check for updates whenever the app is brought back to the foreground after being "backgrounded" (user pressed the home button, app launches a seperate payment process, etc). + +* __codePush.CheckFrequency.MANUAL__ *(2)* - Disable automatic checking for updates, but only check when [`codePush.sync()`](#codepushsync) is called in app code. ##### SyncStatus @@ -1320,7 +1321,7 @@ The `sync` method includes a lot of diagnostic logging out-of-the-box, so if you The simplest way to view these logs is to run the `code-push debug` command for the specific platform you are currently working with (e.g. `code-push debug ios`). This will output a log stream that is filtered to just CodePush messages, for the specified platform. This makes it easy to identify issues, without needing to use a platform-specific tool, or wade through a potentially high volume of logs. screen shot 2016-06-21 at 10 15 42 am - + Additionally, you can also use any of the platform-specific tools to view the CodePush logs, if you are more comfortable with them. Simple start up the Chrome DevTools Console, the Xcode Console (iOS), the [OS X Console](https://en.wikipedia.org/wiki/Console_%28OS_X%29#.7E.2FLibrary.2FLogs) (iOS) and/or ADB logcat (Android), and look for messages which are prefixed with `[CodePush]`. Note that by default, React Native logs are disabled on iOS in release builds, so if you want to view them in a release build, you need to make the following changes to your `AppDelegate.m` file: diff --git a/typings/react-native-code-push.d.ts b/typings/react-native-code-push.d.ts index c8730f9..2d6a2cc 100644 --- a/typings/react-native-code-push.d.ts +++ b/typings/react-native-code-push.d.ts @@ -23,9 +23,9 @@ export type SyncStatusChangedCallback = (status: CodePush.SyncStatus) => void; export interface CodePushOptions extends SyncOptions { /** * Specifies when you would like to synchronize updates with the CodePush server. - * Defaults to codePush.SyncMode.ON_APP_START. + * Defaults to codePush.CheckFrequency.ON_APP_START. */ - syncMode: CodePush.SyncMode; + checkFrequency: CodePush.CheckFrequency; } export interface DownloadProgress { @@ -400,9 +400,9 @@ declare namespace CodePush { } /** - * Indicates when you would like to synchronize updates with the CodePush server. + * Indicates when you would like to check for (and install) updates from the CodePush server. */ - enum SyncMode { + enum CheckFrequency { /** * When the app is fully initialized (or more specifically, when the root component is mounted). */ @@ -411,7 +411,12 @@ declare namespace CodePush { /** * When the app re-enters the foreground. */ - ON_APP_RESUME + ON_APP_RESUME, + + /** + * Don't automatically check for updates, but only do it when codePush.sync() is manully called inside app code. + */ + MANUAL } } From 6fa99e10943e68e937cc3e9b8ea3d38d013ab4bd Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 16:24:43 -0700 Subject: [PATCH 08/22] CR feedback --- CodePush.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CodePush.js b/CodePush.js index e626fa6..0e5071a 100644 --- a/CodePush.js +++ b/CodePush.js @@ -412,13 +412,14 @@ function codePushify(options = {}) { if (!React) { throw new Error("Unable to find the 'React' module."); } - if (!React.Component) { - throw new Error( -`Unable to find the 'Component' class, please either: + } + + if (!React.Component) { + throw new Error( +`Unable to find the "Component" class, please either: 1. Upgrade to a newer version of React Native that supports it, or -2. Call the codePush.sync API in your component instead of using the @CodePushify decorator` - ); - } +2. Call the codePush.sync API in your component instead of using the @codePush decorator` + ); } return class CodePushComponent extends React.Component { From f0379ffb3eef52ce1ec37278bd72bc6931d8003d Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 16:25:03 -0700 Subject: [PATCH 09/22] CR feedback --- CodePush.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodePush.js b/CodePush.js index 0e5071a..ae34b21 100644 --- a/CodePush.js +++ b/CodePush.js @@ -430,7 +430,7 @@ function codePushify(options = {}) { let rootComponentInstance = this.refs.rootComponent; let syncStatusCallback = rootComponentInstance && rootComponentInstance.codePushStatusDidChange; let downloadProgressCallback = rootComponentInstance && rootComponentInstance.codePushDownloadDidProgress; - CodePush.sync(options, syncStatusCallback, downloadProgressCallback) + CodePush.sync(options, syncStatusCallback, downloadProgressCallback); if (options.checkFrequency === CodePush.CheckFrequency.ON_APP_RESUME) { ReactNative.AppState.addEventListener("change", (newState) => { newState === "active" && CodePush.sync(options, syncStatusCallback, downloadProgressCallback); From 7d7e12a7ae5b01ed52519480404986731a695e7e Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 17:34:57 -0700 Subject: [PATCH 10/22] update typings --- typings/react-native-code-push.d.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/typings/react-native-code-push.d.ts b/typings/react-native-code-push.d.ts index 2d6a2cc..1120c00 100644 --- a/typings/react-native-code-push.d.ts +++ b/typings/react-native-code-push.d.ts @@ -227,6 +227,13 @@ export interface StatusReport { previousLabelOrAppVersion?: string; } +/** + * Decorates a React Component configuring it to sync for updates with the CodePush server. + * + * @param options Options used to configure the end-user sync and update experience (e.g. when to check for updates?, show an prompt?, install the update immediately?). + */ +declare function CodePush(options?: CodePushOptions): Function; + declare namespace CodePush { /** * Represents the default settings that will be used by the sync method if @@ -241,13 +248,6 @@ declare namespace CodePush { */ function checkForUpdate(deploymentKey?: string): Promise; - /** - * Decorates a React Component configuring it to sync for updates with the CodePush server. - * - * @param options Options used to configure the end-user sync and update experience (e.g. when to check for updates?, show an prompt?, install the update immediately?). - */ - function codePushify(options?: CodePushOptions): Function; - /** * Retrieves the metadata for an installed update (e.g. description, mandatory). * From 8c9c44d823525d6f9be366bc79bddc26bcd73493 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 17:52:42 -0700 Subject: [PATCH 11/22] CR feedback --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e8c1bc3..6f3e393 100644 --- a/README.md +++ b/README.md @@ -406,7 +406,7 @@ With the CodePush plugin downloaded and linked, and your app asking CodePush whe The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following three options: -**Option 1: Call `codePush` on your root component and assign the result to itself after the component declaration:** +**Option 1: Wrap your root component using the `codePush` decorator function:** ```javascript import codePush from "react-native-code-push"; @@ -459,14 +459,17 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a `MANUAL` check frequency and do the following: +Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a `MANUAL` `checkFrequency` and call [`CodePush.sync()`](#codepushsync) with your desired `SyncOptions`: ```javascript let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL }; class MyApp extends Component { onButtonPress() { - codePush.sync(); + codePush.sync({ + updateDialog: true, + installMode: codePush.installMode.IMMEDIATE + }); } render() { From dfd6a4f8cefe8c2320964dec3cf985a59e62cf52 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 17:55:09 -0700 Subject: [PATCH 12/22] remove codepushify property --- CodePush.js | 1 - 1 file changed, 1 deletion(-) diff --git a/CodePush.js b/CodePush.js index ae34b21..ce2c967 100644 --- a/CodePush.js +++ b/CodePush.js @@ -455,7 +455,6 @@ if (NativeCodePush) { Object.assign(CodePush, { AcquisitionSdk: Sdk, checkForUpdate, - codePushify, getConfiguration, getCurrentPackage, getUpdateMetadata, From bad61c8e310df2ae7d5352d120856365d4c4c041 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 18:00:50 -0700 Subject: [PATCH 13/22] three -> two --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f3e393..2ff3a75 100644 --- a/README.md +++ b/README.md @@ -404,7 +404,7 @@ With the CodePush plugin downloaded and linked, and your app asking CodePush whe 2. When an update is available, how to present it to the end user? -The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following three options: +The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following two options: **Option 1: Wrap your root component using the `codePush` decorator function:** From 9c9770434956fb560d43b6b1a6d6b66186de1ef5 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Mon, 1 Aug 2016 18:11:19 -0700 Subject: [PATCH 14/22] link to babel-preset-react-native-stage-0 --- README.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/README.md b/README.md index 2ff3a75..8cd099f 100644 --- a/README.md +++ b/README.md @@ -431,20 +431,7 @@ class MyApp extends Component { } ``` -*NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by doing the following: -- Install the [`babel-preset-react-native-stage-0` package](https://github.com/skevy/babel-preset-react-native-stage-0) - - ``` - npm install babel-preset-react-native-stage-0 --save-dev - ``` - -- In your `.babelrc` file, include the following: - - ``` - { - "presets": ["react-native-stage-0/decorator-support"] - } - ``` +*NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by installing and using [babel-preset-react-native-stage-0](https://github.com/skevy/babel-preset-react-native-stage-0#babel-preset-react-native-stage-0). 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. From 4c560b9643909fd4b928b6728b10244d8b1f61ba Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 14:56:37 -0700 Subject: [PATCH 15/22] Bullet point and indent decorator options for clearer separation --- README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8cd099f..9e11659 100644 --- a/README.md +++ b/README.md @@ -406,32 +406,32 @@ With the CodePush plugin downloaded and linked, and your app asking CodePush whe The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following two options: -**Option 1: Wrap your root component using the `codePush` decorator function:** +* **Option 1: Wrap your root component using the `codePush` decorator function:** -```javascript -import codePush from "react-native-code-push"; + ```javascript + import codePush from "react-native-code-push"; + + let codePushOptions; + + class MyApp extends Component { + } + + MyApp = codePush(codePushOptions)(MyApp); + ``` -let codePushOptions; +* **Option 2: Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax:** -class MyApp extends Component { -} + ```javascript + import codePush from "react-native-code-push"; -MyApp = codePush(codePushOptions)(MyApp); -``` + let codePushOptions; -**Option 2: Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax:** + @codePush(codePushOptions) + class MyApp extends Component { + } + ``` -```javascript -import codePush from "react-native-code-push"; - -let codePushOptions; - -@codePush(codePushOptions) -class MyApp extends Component { -} -``` - -*NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by installing and using [babel-preset-react-native-stage-0](https://github.com/skevy/babel-preset-react-native-stage-0#babel-preset-react-native-stage-0). + *NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by installing and using [babel-preset-react-native-stage-0](https://github.com/skevy/babel-preset-react-native-stage-0#babel-preset-react-native-stage-0). 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. From 982d484d98b88cf72c76d0c21a72befe2a9f9119 Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 14:59:02 -0700 Subject: [PATCH 16/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e11659..aa305f1 100644 --- a/README.md +++ b/README.md @@ -433,7 +433,7 @@ The simplest way to do this is to "CodePush-ify" your app's root component. To d *NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by installing and using [babel-preset-react-native-stage-0](https://github.com/skevy/babel-preset-react-native-stage-0#babel-preset-react-native-stage-0). -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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. +With the default options, CodePush will check for updates on every app start. 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. If you would like your app to discover updates more quickly, you can also choose to sync up with the CodePush server every time the app resumes from the background. From aabf7f453d1607e88355ee0a37e76cb2398db817 Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 15:08:29 -0700 Subject: [PATCH 17/22] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa305f1..23fcef1 100644 --- a/README.md +++ b/README.md @@ -421,6 +421,8 @@ The simplest way to do this is to "CodePush-ify" your app's root component. To d * **Option 2: Use the [ES7 decorator](https://github.com/wycats/javascript-decorators) syntax:** + *NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by installing and using [babel-preset-react-native-stage-0](https://github.com/skevy/babel-preset-react-native-stage-0#babel-preset-react-native-stage-0). + ```javascript import codePush from "react-native-code-push"; @@ -431,8 +433,6 @@ The simplest way to do this is to "CodePush-ify" your app's root component. To d } ``` - *NOTE: Decorators are not yet supported in Babel 6.x pending proposal update.* You may need to enable it by installing and using [babel-preset-react-native-stage-0](https://github.com/skevy/babel-preset-react-native-stage-0#babel-preset-react-native-stage-0). - With the default options, CodePush will check for updates on every app start. 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. If you would like your app to discover updates more quickly, you can also choose to sync up with the CodePush server every time the app resumes from the background. From 7ae8ddf53e8e38d49e3a88be72095f2c9669a053 Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 15:22:13 -0700 Subject: [PATCH 18/22] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 23fcef1..f213f88 100644 --- a/README.md +++ b/README.md @@ -433,7 +433,7 @@ The simplest way to do this is to "CodePush-ify" your app's root component. To d } ``` -With the default options, CodePush will check for updates on every app start. 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. +By default, CodePush will check for updates on every app start. 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 an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible. If you would like your app to discover updates more quickly, you can also choose to sync up with the CodePush server every time the app resumes from the background. @@ -446,7 +446,7 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a `MANUAL` `checkFrequency` and call [`CodePush.sync()`](#codepushsync) with your desired `SyncOptions`: +Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a manual `checkFrequency` and call [`CodePush.sync()`](#codepushsync) with your desired `SyncOptions`: ```javascript let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL }; @@ -473,7 +473,7 @@ MyApp = codePush(CodePushOptions)(MyApp); *NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* -Additionally, if you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush()`](#codepush) API reference for information on how to tweak this default behavior. +If you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush()`](#codepush) API reference for information on how to tweak this default behavior. *NOTE: While [Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf) fully allows performing over-the-air updates of JavaScript and assets (which is what enables CodePush!), it is against their policy for an app to display an update prompt. Because of this, we recommend that App Store-distributed apps don't enable the `updateDialog` option when calling `sync`, whereas Google Play and internally distributed apps (e.g. Enterprise, Fabric, HockeyApp) can choose to enable/customize it.* From cad23676cd419c12acc5f822f2a12c5f11fd382f Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 15:27:15 -0700 Subject: [PATCH 19/22] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f213f88..ae5cfbe 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a manual `checkFrequency` and call [`CodePush.sync()`](#codepushsync) with your desired `SyncOptions`: +Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a manual `checkFrequency` and call [`CodePush.sync()`](#codepushsync) at any time with your desired `SyncOptions`: ```javascript let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL }; @@ -471,10 +471,10 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -*NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* - If you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush()`](#codepush) API reference for information on how to tweak this default behavior. +*NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* + *NOTE: While [Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf) fully allows performing over-the-air updates of JavaScript and assets (which is what enables CodePush!), it is against their policy for an app to display an update prompt. Because of this, we recommend that App Store-distributed apps don't enable the `updateDialog` option when calling `sync`, whereas Google Play and internally distributed apps (e.g. Enterprise, Fabric, HockeyApp) can choose to enable/customize it.* ## Releasing Updates From f7a1dd36b924023756313ea7647dc2a0c1742886 Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 15:35:43 -0700 Subject: [PATCH 20/22] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ae5cfbe..49c077b 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -Alternatively, if you don't want CodePush to trigger checking for updates automatically for you, but want to trigger it manually in app code in response to some event (e.g. a button press), you can specify a manual `checkFrequency` and call [`CodePush.sync()`](#codepushsync) at any time with your desired `SyncOptions`: +Alternatively, if you want fine-grained control over when the check happens (e.g. a button press or timer interval), you can call [`CodePush.sync()`](#codepushsync) at any time with your desired `SyncOptions` and/or turn off CodePush's automatic checking by specifying a manual `checkFrequency`: ```javascript let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL }; @@ -471,10 +471,10 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -If you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush()`](#codepush) API reference for information on how to tweak this default behavior. - *NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* +If you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush()`](#codepush) API reference for information on how to tweak this default behavior. + *NOTE: While [Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf) fully allows performing over-the-air updates of JavaScript and assets (which is what enables CodePush!), it is against their policy for an app to display an update prompt. Because of this, we recommend that App Store-distributed apps don't enable the `updateDialog` option when calling `sync`, whereas Google Play and internally distributed apps (e.g. Enterprise, Fabric, HockeyApp) can choose to enable/customize it.* ## Releasing Updates From ae8ac7aef1dff7b1846ba42c88112a55d8c43bda Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 15:40:06 -0700 Subject: [PATCH 21/22] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 49c077b..803a74e 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -Alternatively, if you want fine-grained control over when the check happens (e.g. a button press or timer interval), you can call [`CodePush.sync()`](#codepushsync) at any time with your desired `SyncOptions` and/or turn off CodePush's automatic checking by specifying a manual `checkFrequency`: +Alternatively, if you want fine-grained control over when the check happens (e.g. a button press or timer interval), you can call [`CodePush.sync()`](#codepushsync) at any time with your desired `SyncOptions`, and optionally turn off CodePush's automatic checking by specifying a manual `checkFrequency`: ```javascript let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL }; @@ -471,10 +471,10 @@ class MyApp extends Component { MyApp = codePush(CodePushOptions)(MyApp); ``` -*NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* - If you would like to display an update confirmation dialog (an "active install"), configure when an available update is installed (e.g. force an immediate restart) or customize the update experience in any other way, refer to the [`codePush()`](#codepush) API reference for information on how to tweak this default behavior. +*NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](http://yelouafi.github.io/redux-saga/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.* + *NOTE: While [Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf) fully allows performing over-the-air updates of JavaScript and assets (which is what enables CodePush!), it is against their policy for an app to display an update prompt. Because of this, we recommend that App Store-distributed apps don't enable the `updateDialog` option when calling `sync`, whereas Google Play and internally distributed apps (e.g. Enterprise, Fabric, HockeyApp) can choose to enable/customize it.* ## Releasing Updates From 907354b6557820bbd70fbaaf6198719f056cbf57 Mon Sep 17 00:00:00 2001 From: Richard Hua Date: Tue, 2 Aug 2016 15:50:49 -0700 Subject: [PATCH 22/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 803a74e..281e7cf 100644 --- a/README.md +++ b/README.md @@ -406,7 +406,7 @@ With the CodePush plugin downloaded and linked, and your app asking CodePush whe The simplest way to do this is to "CodePush-ify" your app's root component. To do so, you can choose one of the following two options: -* **Option 1: Wrap your root component using the `codePush` decorator function:** +* **Option 1: Wrap your root component with the `codePush` higher-order component:** ```javascript import codePush from "react-native-code-push";