mirror of
https://github.com/zhigang1992/react-native-code-push.git
synced 2026-06-20 03:48:44 +08:00
Merge pull request #294 from Microsoft/updateMetadata
codePush.getUpdateMetadata
This commit is contained in:
97
CodePush.js
97
CodePush.js
@@ -100,12 +100,16 @@ const getConfiguration = (() => {
|
||||
})();
|
||||
|
||||
async function getCurrentPackage() {
|
||||
const localPackage = await NativeCodePush.getCurrentPackage();
|
||||
if (localPackage) {
|
||||
localPackage.failedInstall = await NativeCodePush.isFailedUpdate(localPackage.packageHash);
|
||||
localPackage.isFirstRun = await NativeCodePush.isFirstRun(localPackage.packageHash);
|
||||
return await getUpdateMetadata(CodePush.UpdateState.LATEST);
|
||||
}
|
||||
|
||||
async function getUpdateMetadata(updateState) {
|
||||
const updateMetadata = await NativeCodePush.getUpdateMetadata(updateState || CodePush.UpdateState.RUNNING);
|
||||
if (updateMetadata) {
|
||||
updateMetadata.failedInstall = await NativeCodePush.isFailedUpdate(updateMetadata.packageHash);
|
||||
updateMetadata.isFirstRun = await NativeCodePush.isFirstRun(updateMetadata.packageHash);
|
||||
}
|
||||
return localPackage;
|
||||
return updateMetadata;
|
||||
}
|
||||
|
||||
function getPromisifiedSdk(requestFetchAdapter, config) {
|
||||
@@ -383,45 +387,52 @@ let CodePush;
|
||||
// and therefore, it doesn't make sense initializing
|
||||
// the JS interface when it wouldn't work anyways.
|
||||
if (NativeCodePush) {
|
||||
CodePush = {
|
||||
AcquisitionSdk: Sdk,
|
||||
checkForUpdate,
|
||||
getConfiguration,
|
||||
getCurrentPackage,
|
||||
log,
|
||||
notifyApplicationReady,
|
||||
restartApp,
|
||||
setUpTestDependencies,
|
||||
sync,
|
||||
InstallMode: {
|
||||
IMMEDIATE: NativeCodePush.codePushInstallModeImmediate, // Restart the app immediately
|
||||
ON_NEXT_RESTART: NativeCodePush.codePushInstallModeOnNextRestart, // Don't artificially restart the app. Allow the update to be "picked up" on the next app restart
|
||||
ON_NEXT_RESUME: NativeCodePush.codePushInstallModeOnNextResume // Restart the app the next time it is resumed from the background
|
||||
},
|
||||
SyncStatus: {
|
||||
CHECKING_FOR_UPDATE: 0,
|
||||
AWAITING_USER_ACTION: 1,
|
||||
DOWNLOADING_PACKAGE: 2,
|
||||
INSTALLING_UPDATE: 3,
|
||||
UP_TO_DATE: 4, // The running app is up-to-date
|
||||
UPDATE_IGNORED: 5, // The app had an optional update and the end-user chose to ignore it
|
||||
UPDATE_INSTALLED: 6, // The app had an optional/mandatory update that was successfully downloaded and is about to be installed.
|
||||
SYNC_IN_PROGRESS: 7, // There is an ongoing "sync" operation in progress.
|
||||
UNKNOWN_ERROR: -1
|
||||
},
|
||||
DEFAULT_UPDATE_DIALOG: {
|
||||
appendReleaseDescription: false,
|
||||
descriptionPrefix: " Description: ",
|
||||
mandatoryContinueButtonLabel: "Continue",
|
||||
mandatoryUpdateMessage: "An update is available that must be installed.",
|
||||
optionalIgnoreButtonLabel: "Ignore",
|
||||
optionalInstallButtonLabel: "Install",
|
||||
optionalUpdateMessage: "An update is available. Would you like to install it?",
|
||||
title: "Update available"
|
||||
}
|
||||
CodePush = {
|
||||
AcquisitionSdk: Sdk,
|
||||
checkForUpdate,
|
||||
getConfiguration,
|
||||
getCurrentPackage,
|
||||
getUpdateMetadata,
|
||||
log,
|
||||
notifyAppReady: notifyApplicationReady,
|
||||
notifyApplicationReady,
|
||||
restartApp,
|
||||
setUpTestDependencies,
|
||||
sync,
|
||||
InstallMode: {
|
||||
IMMEDIATE: NativeCodePush.codePushInstallModeImmediate, // Restart the app immediately
|
||||
ON_NEXT_RESTART: NativeCodePush.codePushInstallModeOnNextRestart, // Don't artificially restart the app. Allow the update to be "picked up" on the next app restart
|
||||
ON_NEXT_RESUME: NativeCodePush.codePushInstallModeOnNextResume // Restart the app the next time it is resumed from the background
|
||||
},
|
||||
SyncStatus: {
|
||||
CHECKING_FOR_UPDATE: 0,
|
||||
AWAITING_USER_ACTION: 1,
|
||||
DOWNLOADING_PACKAGE: 2,
|
||||
INSTALLING_UPDATE: 3,
|
||||
UP_TO_DATE: 4, // The running app is up-to-date
|
||||
UPDATE_IGNORED: 5, // The app had an optional update and the end-user chose to ignore it
|
||||
UPDATE_INSTALLED: 6, // The app had an optional/mandatory update that was successfully downloaded and is about to be installed.
|
||||
SYNC_IN_PROGRESS: 7, // There is an ongoing "sync" operation in progress.
|
||||
UNKNOWN_ERROR: -1
|
||||
},
|
||||
UpdateState: {
|
||||
RUNNING: NativeCodePush.codePushUpdateStateRunning,
|
||||
PENDING: NativeCodePush.codePushUpdateStatePending,
|
||||
LATEST: NativeCodePush.codePushUpdateStateLatest
|
||||
},
|
||||
DEFAULT_UPDATE_DIALOG: {
|
||||
appendReleaseDescription: false,
|
||||
descriptionPrefix: " Description: ",
|
||||
mandatoryContinueButtonLabel: "Continue",
|
||||
mandatoryUpdateMessage: "An update is available that must be installed.",
|
||||
optionalIgnoreButtonLabel: "Ignore",
|
||||
optionalInstallButtonLabel: "Install",
|
||||
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.");
|
||||
log("The CodePush module doesn't appear to be properly installed. Please double-check that everything is setup correctly.");
|
||||
}
|
||||
|
||||
module.exports = CodePush;
|
||||
module.exports = CodePush;
|
||||
49
README.md
49
README.md
@@ -378,7 +378,7 @@ 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): Retrieves the metadata about the currently installed update (e.g. description, installation time, size).
|
||||
* [getUpdateMetadata](#codepushgetupdatemetadata): Retrieves the metadata for an installed update (e.g. description, mandatory).
|
||||
|
||||
* [notifyApplicationReady](#codepushnotifyapplicationready): Notifies the CodePush runtime that an installed update is considered successful. If you are manually checking for and installing updates (i.e. not using the [sync](#codepushsync) method to handle it all for you), then this method **MUST** be called; otherwise CodePush will treat the update as failed and rollback to the previous version when the app next restarts.
|
||||
|
||||
@@ -417,33 +417,41 @@ codePush.checkForUpdate()
|
||||
});
|
||||
```
|
||||
|
||||
#### codePush.getCurrentPackage
|
||||
#### codePush.getUpdateMetadata
|
||||
|
||||
```javascript
|
||||
codePush.getCurrentPackage(): Promise<LocalPackage>;
|
||||
codePush.getUpdateMetadata(updateState: UpdateState = UpdateState.RUNNING): Promise<LocalPackage>;
|
||||
```
|
||||
|
||||
Retrieves the metadata about the currently installed "package" (e.g. description, installation time). This can be useful for scenarios such as displaying a "what's new?" dialog after an update has been applied or checking whether there is a pending update that is waiting to be applied via a resume or restart.
|
||||
Retrieves the metadata for an installed update (e.g. description, mandatory) whose state matches the specified `updateState` parameter. This can be useful for scenarios such as displaying a "what's new?" dialog after an update has been applied or checking whether there is a pending update that is waiting to be applied via a resume or restart. For more details about the possible update states, and what they represent, refer to the [UpdateState reference](#updatestate).
|
||||
|
||||
This method returns a `Promise` which resolves to one of two possible values:
|
||||
|
||||
1. `null` if the app is currently running the JS bundle from the binary and not a CodePush update. This occurs in the following scenarios:
|
||||
1. `null` if an update with the specified state doesn't currently exist. This occurs in the following scenarios:
|
||||
|
||||
1. The end-user installed the app binary and has yet to install a CodePush update
|
||||
1. The end-user hasn't installed any CodePush updates yet, and therefore, no metadata is available for any updates, regardless what you specify as the `updateState` parameter.
|
||||
1. The end-user installed an update of the binary (e.g. from the store), which cleared away the old CodePush updates, and gave precedence back to the JS binary in the binary.
|
||||
1. The `updateState` parameter is set to `UpdateState.RUNNING`, but the app isn't currently running a CodePush update. There may be a pending update, which requires an app restart to become active.
|
||||
1. The `updateState` parameter is set to `UpdateState.PENDING`, but the app doesn't have any pending updates.
|
||||
|
||||
2. A [`LocalPackage`](#localpackage) instance which represents the metadata for the currently running CodePush update.
|
||||
2. A [`LocalPackage`](#localpackage) instance which represents the metadata for the currently requested CodePush update (either the running or pending).
|
||||
|
||||
Example Usage:
|
||||
|
||||
```javascript
|
||||
codePush.getCurrentPackage()
|
||||
.then((update) => {
|
||||
// If the current app "session" represents the first time
|
||||
// this update has run, and it had a description provided
|
||||
// with it upon release, let's show it to the end user
|
||||
if (update.isFirstRun && update.description) {
|
||||
// Display a "what's new?" modal
|
||||
// Check if there is currently a CodePush update running, and if
|
||||
// so, register it with the HockeyApp SDK (https://github.com/slowpath/react-native-hockeyapp)
|
||||
// so that crash reports will correctly display the JS bundle version the user was running.
|
||||
codePush.getUpdateMetadata().then((update) => {
|
||||
if (update) {
|
||||
hockeyApp.addMetadata({ CodePushRelease: update.label });
|
||||
}
|
||||
});
|
||||
|
||||
// Check to see if there is still an update pending.
|
||||
codePush.getUpdateMetadata(UpdateState.PENDING).then((update) => {
|
||||
if (update) {
|
||||
// There's a pending update, do we want to force a restart?
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -486,7 +494,6 @@ This method provides support for two different (but customizable) "modes" to eas
|
||||
|
||||
2. **Active mode**, which when an update is available, prompts the end user for permission before downloading it, and then immediately applies the update. If an update was released using the `mandatory` flag, the end user would still be notified about the update, but they wouldn't have the choice to ignore it.
|
||||
|
||||
|
||||
Example Usage:
|
||||
|
||||
```javascript
|
||||
@@ -650,7 +657,7 @@ The CodePush API includes the following enums which can be used to customize the
|
||||
|
||||
##### InstallMode
|
||||
|
||||
This enum specified when you would like an installed update to actually be applied, and can be passed to either the `sync` or `LocalPackage.install` methods. It includes the following values:
|
||||
This enum specifies when you would like an installed update to actually be applied, and can be passed to either the `sync` or `LocalPackage.install` methods. It includes the following values:
|
||||
|
||||
* __codePush.InstallMode.IMMEDIATE__ *(0)* - Indicates that you want to install the update and restart the app immediately. This value is appropriate for debugging scenarios as well as when displaying an update prompt to the user, since they would expect to see the changes immediately after accepting the installation. Additionally, this mode can be used to enforce mandatory updates, since it removes the potentially undesired latency between the update installation and the next time the end user restarts or resumes the app.
|
||||
|
||||
@@ -672,6 +679,16 @@ This enum is provided to the `syncStatusChangedCallback` function that can be pa
|
||||
* __codePush.SyncStatus.SYNC_IN_PROGRESS__ *(7)* - There is an ongoing `sync` operation running which prevents the current call from being executed.
|
||||
* __codePush.SyncStatus.UNKNOWN_ERROR__ *(-1)* - The sync operation encountered an unknown error.
|
||||
|
||||
##### UpdateState
|
||||
|
||||
This enum specifies the state that an update is currently in, and can be specified when calling the `getUpdateMetadata` method. It includes the following values:
|
||||
|
||||
* __codePush.UpdateState.RUNNING__ *(0)* - Indicates that an update represents the version of the app that is currently running. This can be useful for identifying attributes about the app, for scenarios such as displaying the release description in a "what's new?" dialog or reporting the latest version to an analytics and/or crash reporting service.
|
||||
|
||||
* __codePush.UpdateState.PENDING__ *(1)* - Indicates than an update has been installed, but the app hasn't been restarted yet in order to apply it. This can be useful for determining whether there is a pending update, which you may want to force a programmatic restart (via `restartApp`) in order to apply.
|
||||
|
||||
* __codePush.UpdateState.LATEST__ *(2)* - Indicates than an update represents the latest available release, and can be either currently running or pending.
|
||||
|
||||
### Objective-C API Reference (iOS)
|
||||
|
||||
The Objective-C API is made available by importing the `CodePush.h` header into your `AppDelegate.m` file, and consists of a single public class named `CodePush`.
|
||||
|
||||
@@ -353,9 +353,15 @@ public class CodePush implements ReactPackage {
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
final Map<String, Object> constants = new HashMap<>();
|
||||
|
||||
constants.put("codePushInstallModeImmediate", CodePushInstallMode.IMMEDIATE.getValue());
|
||||
constants.put("codePushInstallModeOnNextRestart", CodePushInstallMode.ON_NEXT_RESTART.getValue());
|
||||
constants.put("codePushInstallModeOnNextResume", CodePushInstallMode.ON_NEXT_RESUME.getValue());
|
||||
|
||||
constants.put("codePushUpdateStateRunning", CodePushUpdateState.RUNNING.getValue());
|
||||
constants.put("codePushUpdateStatePending", CodePushUpdateState.PENDING.getValue());
|
||||
constants.put("codePushUpdateStateLatest", CodePushUpdateState.LATEST.getValue());
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
@@ -481,31 +487,51 @@ public class CodePush implements ReactPackage {
|
||||
|
||||
promise.resolve(configMap);
|
||||
}
|
||||
|
||||
|
||||
@ReactMethod
|
||||
public void getCurrentPackage(final Promise promise) {
|
||||
public void getUpdateMetadata(final int updateState, final Promise promise) {
|
||||
AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
WritableMap currentPackage = codePushPackage.getCurrentPackage();
|
||||
|
||||
if (currentPackage == null) {
|
||||
promise.resolve("");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isRunningBinaryVersion) {
|
||||
currentPackage.putBoolean("_isDebugOnly", true);
|
||||
}
|
||||
|
||||
Boolean isPendingUpdate = false;
|
||||
Boolean currentUpdateIsPending = false;
|
||||
|
||||
if (currentPackage.hasKey(PACKAGE_HASH_KEY)) {
|
||||
String currentHash = currentPackage.getString(PACKAGE_HASH_KEY);
|
||||
isPendingUpdate = CodePush.this.isPendingUpdate(currentHash);
|
||||
currentUpdateIsPending = CodePush.this.isPendingUpdate(currentHash);
|
||||
}
|
||||
|
||||
if (updateState == CodePushUpdateState.PENDING.getValue() && !currentUpdateIsPending) {
|
||||
// The caller wanted a pending update
|
||||
// but there isn't currently one.
|
||||
promise.resolve("");
|
||||
} else if (updateState == CodePushUpdateState.RUNNING.getValue() && currentUpdateIsPending) {
|
||||
// The caller wants the running update, but the current
|
||||
// one is pending, so we need to grab the previous.
|
||||
promise.resolve(codePushPackage.getPreviousPackage());
|
||||
} else {
|
||||
// The current package satisfies the request:
|
||||
// 1) Caller wanted a pending, and there is a pending update
|
||||
// 2) Caller wanted the running update, and there isn't a pending
|
||||
// 3) Caller wants the latest update, regardless if it's pending or not
|
||||
if (isRunningBinaryVersion) {
|
||||
// This only matters in Debug builds. Since we do not clear "outdated" updates,
|
||||
// we need to indicate to the JS side that somehow we have a current update on
|
||||
// disk that is not actually running.
|
||||
currentPackage.putBoolean("_isDebugOnly", true);
|
||||
}
|
||||
|
||||
currentPackage.putBoolean("isPending", isPendingUpdate);
|
||||
promise.resolve(currentPackage);
|
||||
// Enable differentiating pending vs. non-pending updates
|
||||
currentPackage.putBoolean("isPending", currentUpdateIsPending);
|
||||
promise.resolve(currentPackage);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class CodePushPackage {
|
||||
|
||||
private final String CODE_PUSH_FOLDER_PREFIX = "CodePush";
|
||||
private final String CURRENT_PACKAGE_KEY = "currentPackage";
|
||||
private final String DIFF_MANIFEST_FILE_NAME = "hotcodepush.json";
|
||||
@@ -124,18 +123,21 @@ public class CodePushPackage {
|
||||
}
|
||||
|
||||
public WritableMap getCurrentPackage() {
|
||||
String folderPath = getCurrentPackageFolderPath();
|
||||
if (folderPath == null) {
|
||||
String packageHash = getCurrentPackageHash();
|
||||
if (packageHash == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String packagePath = CodePushUtils.appendPathComponent(folderPath, PACKAGE_FILE_NAME);
|
||||
try {
|
||||
return CodePushUtils.getWritableMapFromFile(packagePath);
|
||||
} catch (IOException e) {
|
||||
// Should not happen unless the update metadata was somehow deleted.
|
||||
|
||||
return getPackage(packageHash);
|
||||
}
|
||||
|
||||
public WritableMap getPreviousPackage() {
|
||||
String packageHash = getPreviousPackageHash();
|
||||
if (packageHash == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getPackage(packageHash);
|
||||
}
|
||||
|
||||
public WritableMap getPackage(String packageHash) {
|
||||
@@ -340,8 +342,6 @@ public class CodePushPackage {
|
||||
}
|
||||
|
||||
public void clearUpdates() {
|
||||
File statusFile = new File(getStatusFilePath());
|
||||
statusFile.delete();
|
||||
FileUtils.deleteDirectoryAtPath(getCodePushPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.microsoft.codepush.react;
|
||||
|
||||
public enum CodePushUpdateState {
|
||||
RUNNING(0),
|
||||
PENDING(1),
|
||||
LATEST(2);
|
||||
|
||||
private final int value;
|
||||
CodePushUpdateState(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
13BE3DEE1AC21097009241FE /* CodePush.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.m */; };
|
||||
1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */; };
|
||||
1B762E901C9A5E9A006EF800 /* CodePushErrorUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B762E8F1C9A5E9A006EF800 /* CodePushErrorUtils.m */; };
|
||||
1BCC09A71CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BCC09A61CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m */; };
|
||||
540D20121C7684FE00D6EF41 /* CodePushUpdateUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 540D20111C7684FE00D6EF41 /* CodePushUpdateUtils.m */; };
|
||||
5421FE311C58AD5A00986A55 /* CodePushTelemetryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5421FE301C58AD5A00986A55 /* CodePushTelemetryManager.m */; };
|
||||
54A0026C1C0E2880004C3CEC /* aescrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 54A0024C1C0E2880004C3CEC /* aescrypt.c */; };
|
||||
@@ -49,6 +50,7 @@
|
||||
13BE3DED1AC21097009241FE /* CodePush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePush.m; path = CodePush/CodePush.m; sourceTree = "<group>"; };
|
||||
1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RCTConvert+CodePushInstallMode.m"; path = "CodePush/RCTConvert+CodePushInstallMode.m"; sourceTree = "<group>"; };
|
||||
1B762E8F1C9A5E9A006EF800 /* CodePushErrorUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushErrorUtils.m; path = CodePush/CodePushErrorUtils.m; sourceTree = "<group>"; };
|
||||
1BCC09A61CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RCTConvert+CodePushUpdateState.m"; path = "CodePush/RCTConvert+CodePushUpdateState.m"; sourceTree = "<group>"; };
|
||||
540D20111C7684FE00D6EF41 /* CodePushUpdateUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushUpdateUtils.m; path = CodePush/CodePushUpdateUtils.m; sourceTree = "<group>"; };
|
||||
5421FE301C58AD5A00986A55 /* CodePushTelemetryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushTelemetryManager.m; path = CodePush/CodePushTelemetryManager.m; sourceTree = "<group>"; };
|
||||
54A0024A1C0E2880004C3CEC /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; };
|
||||
@@ -168,16 +170,17 @@
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54A002481C0E2880004C3CEC /* SSZipArchive */,
|
||||
1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */,
|
||||
13BE3DEC1AC21097009241FE /* CodePush.h */,
|
||||
13BE3DED1AC21097009241FE /* CodePush.m */,
|
||||
81D51F391B6181C2000DA084 /* CodePushConfig.m */,
|
||||
54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */,
|
||||
1B762E8F1C9A5E9A006EF800 /* CodePushErrorUtils.m */,
|
||||
810D4E6C1B96935000B397E9 /* CodePushPackage.m */,
|
||||
5421FE301C58AD5A00986A55 /* CodePushTelemetryManager.m */,
|
||||
540D20111C7684FE00D6EF41 /* CodePushUpdateUtils.m */,
|
||||
13BE3DEC1AC21097009241FE /* CodePush.h */,
|
||||
13BE3DED1AC21097009241FE /* CodePush.m */,
|
||||
1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */,
|
||||
1BCC09A61CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m */,
|
||||
54A002481C0E2880004C3CEC /* SSZipArchive */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
@@ -242,6 +245,7 @@
|
||||
540D20121C7684FE00D6EF41 /* CodePushUpdateUtils.m in Sources */,
|
||||
54A0026E1C0E2880004C3CEC /* aestab.c in Sources */,
|
||||
54A002761C0E2880004C3CEC /* mztools.c in Sources */,
|
||||
1BCC09A71CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m in Sources */,
|
||||
54A002781C0E2880004C3CEC /* zip.c in Sources */,
|
||||
54A002791C0E2880004C3CEC /* SSZipArchive.m in Sources */,
|
||||
1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m in Sources */,
|
||||
|
||||
@@ -84,6 +84,7 @@ failCallback:(void (^)(NSError *err))failCallback;
|
||||
|
||||
+ (NSString *)getBinaryAssetsPath;
|
||||
+ (NSDictionary *)getCurrentPackage:(NSError **)error;
|
||||
+ (NSDictionary *)getPreviousPackage:(NSError **)error;
|
||||
+ (NSString *)getCurrentPackageFolderPath:(NSError **)error;
|
||||
+ (NSString *)getCurrentPackageBundlePath:(NSError **)error;
|
||||
+ (NSString *)getCurrentPackageHash:(NSError **)error;
|
||||
@@ -140,4 +141,10 @@ typedef NS_ENUM(NSInteger, CodePushInstallMode) {
|
||||
CodePushInstallModeImmediate,
|
||||
CodePushInstallModeOnNextRestart,
|
||||
CodePushInstallModeOnNextResume
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CodePushUpdateState) {
|
||||
CodePushUpdateStateRunning,
|
||||
CodePushUpdateStatePending,
|
||||
CodePushUpdateStateLatest
|
||||
};
|
||||
@@ -180,12 +180,16 @@ static NSString *bundleResourceName = @"main";
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
// Export the values of the CodePushInstallMode enum
|
||||
// so that the script-side can easily stay in sync
|
||||
// Export the values of the CodePushInstallMode and CodePushUpdateState
|
||||
// enums so that the script-side can easily stay in sync
|
||||
return @{
|
||||
@"codePushInstallModeOnNextRestart":@(CodePushInstallModeOnNextRestart),
|
||||
@"codePushInstallModeImmediate": @(CodePushInstallModeImmediate),
|
||||
@"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume)
|
||||
@"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume),
|
||||
|
||||
@"codePushUpdateStateRunning": @(CodePushUpdateStateRunning),
|
||||
@"codePushUpdateStatePending": @(CodePushUpdateStatePending),
|
||||
@"codePushUpdateStateLatest": @(CodePushUpdateStateLatest)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -532,35 +536,51 @@ RCT_EXPORT_METHOD(getConfiguration:(RCTPromiseResolveBlock)resolve
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is the native side of the CodePush.getCurrentPackage method.
|
||||
* This method is the native side of the CodePush.getUpdateMetadata method.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(getCurrentPackage:(RCTPromiseResolveBlock)resolve
|
||||
RCT_EXPORT_METHOD(getUpdateMetadata:(CodePushUpdateState)updateState
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSError *error;
|
||||
NSMutableDictionary *package = [[CodePushPackage getCurrentPackage:&error] mutableCopy];
|
||||
|
||||
|
||||
if (error) {
|
||||
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
|
||||
return;
|
||||
return reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
|
||||
} else if (package == nil) {
|
||||
// The app hasn't downloaded any CodePush updates yet,
|
||||
// so we simply return nil regardless if the user
|
||||
// wanted to retrieve the pending or running update.
|
||||
return resolve(nil);
|
||||
}
|
||||
|
||||
// We have a CodePush update, so let's see if it's currently in a pending state.
|
||||
BOOL currentUpdateIsPending = [self isPendingUpdate:[package objectForKey:PackageHashKey]];
|
||||
|
||||
if (updateState == CodePushUpdateStatePending && !currentUpdateIsPending) {
|
||||
// The caller wanted a pending update
|
||||
// but there isn't currently one.
|
||||
resolve(nil);
|
||||
return;
|
||||
} else if (updateState == CodePushUpdateStateRunning && currentUpdateIsPending) {
|
||||
// The caller wants the running update, but the current
|
||||
// one is pending, so we need to grab the previous.
|
||||
resolve([CodePushPackage getPreviousPackage:nil]);
|
||||
} else {
|
||||
// The current package satisfies the request:
|
||||
// 1) Caller wanted a pending, and there is a pending update
|
||||
// 2) Caller wanted the running update, and there isn't a pending
|
||||
// 3) Caller wants the latest update, regardless if it's pending or not
|
||||
if (isRunningBinaryVersion) {
|
||||
// This only matters in Debug builds. Since we do not clear "outdated" updates,
|
||||
// we need to indicate to the JS side that somehow we have a current update on
|
||||
// disk that is not actually running.
|
||||
[package setObject:@(YES) forKey:@"_isDebugOnly"];
|
||||
}
|
||||
|
||||
// Enable differentiating pending vs. non-pending updates
|
||||
[package setObject:@(currentUpdateIsPending) forKey:PackageIsPendingKey];
|
||||
resolve(package);
|
||||
}
|
||||
|
||||
if (isRunningBinaryVersion) {
|
||||
// This only matters in Debug builds. Since we do not clear "outdated" updates,
|
||||
// we need to indicate to the JS side that somehow we have a current update on
|
||||
// disk that is not actually running.
|
||||
[package setObject:@(YES) forKey:@"_isDebugOnly"];
|
||||
}
|
||||
|
||||
// Add the "isPending" virtual property to the package at this point, so that
|
||||
// the script-side doesn't need to immediately call back into native to populate it.
|
||||
BOOL isPendingUpdate = [self isPendingUpdate:[package objectForKey:PackageHashKey]];
|
||||
[package setObject:@(isPendingUpdate) forKey:PackageIsPendingKey];
|
||||
|
||||
resolve(package);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -10,6 +10,7 @@ static NSString *const DownloadFileName = @"download.zip";
|
||||
static NSString *const RelativeBundlePathKey = @"bundlePath";
|
||||
static NSString *const StatusFile = @"codepush.json";
|
||||
static NSString *const UpdateBundleFileName = @"app.jsbundle";
|
||||
static NSString *const UpdateMetadataFileName = @"app.json";
|
||||
static NSString *const UnzippedFolderName = @"unzipped";
|
||||
|
||||
#pragma mark - Public methods
|
||||
@@ -17,7 +18,6 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
+ (void)clearUpdates
|
||||
{
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self getCodePushPath] error:nil];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self getStatusFilePath] error:nil];
|
||||
}
|
||||
|
||||
+ (void)downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl
|
||||
@@ -47,7 +47,7 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
{
|
||||
NSString *newUpdateHash = updatePackage[@"packageHash"];
|
||||
NSString *newUpdateFolderPath = [self getPackageFolderPath:newUpdateHash];
|
||||
NSString *newUpdateMetadataPath = [newUpdateFolderPath stringByAppendingPathComponent:@"app.json"];
|
||||
NSString *newUpdateMetadataPath = [newUpdateFolderPath stringByAppendingPathComponent:UpdateMetadataFileName];
|
||||
NSError *error;
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:newUpdateFolderPath]) {
|
||||
@@ -290,27 +290,13 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
|
||||
+ (NSDictionary *)getCurrentPackage:(NSError **)error
|
||||
{
|
||||
NSString *folderPath = [CodePushPackage getCurrentPackageFolderPath:error];
|
||||
if (!*error) {
|
||||
if (!folderPath) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *packagePath = [folderPath stringByAppendingPathComponent:@"app.json"];
|
||||
NSString *content = [NSString stringWithContentsOfFile:packagePath
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:error];
|
||||
if (!*error) {
|
||||
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary* jsonDict = [NSJSONSerialization JSONObjectWithData:data
|
||||
options:kNilOptions
|
||||
error:error];
|
||||
|
||||
return jsonDict;
|
||||
}
|
||||
NSString *packageHash = [CodePushPackage getCurrentPackageHash:error];
|
||||
|
||||
if (*error || !packageHash) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return [CodePushPackage getPackage:packageHash error:error];
|
||||
}
|
||||
|
||||
+ (NSString *)getCurrentPackageBundlePath:(NSError **)error
|
||||
@@ -338,8 +324,8 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
+ (NSString *)getCurrentPackageHash:(NSError **)error
|
||||
{
|
||||
NSDictionary *info = [self getCurrentPackageInfo:error];
|
||||
if (*error) {
|
||||
return NULL;
|
||||
if (*error || !info) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return info[@"currentPackage"];
|
||||
@@ -373,7 +359,7 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:error];
|
||||
if (*error) {
|
||||
return NULL;
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -381,7 +367,7 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
options:kNilOptions
|
||||
error:error];
|
||||
if (*error) {
|
||||
return NULL;
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [json mutableCopy];
|
||||
@@ -395,27 +381,25 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
+ (NSDictionary *)getPackage:(NSString *)packageHash
|
||||
error:(NSError **)error
|
||||
{
|
||||
NSString *folderPath = [self getPackageFolderPath:packageHash];
|
||||
NSString *updateDirectoryPath = [self getPackageFolderPath:packageHash];
|
||||
NSString *updateMetadataFilePath = [updateDirectoryPath stringByAppendingPathComponent:UpdateMetadataFileName];
|
||||
|
||||
if (!folderPath) {
|
||||
return [NSDictionary dictionary];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:updateMetadataFilePath]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *packageFilePath = [folderPath stringByAppendingPathComponent:@"app.json"];
|
||||
NSString *updateMetadataString = [NSString stringWithContentsOfFile:updateMetadataFilePath
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:error];
|
||||
|
||||
NSString *content = [NSString stringWithContentsOfFile:packageFilePath
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:error];
|
||||
if (!*error) {
|
||||
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary* jsonDict = [NSJSONSerialization JSONObjectWithData:data
|
||||
options:kNilOptions
|
||||
error:error];
|
||||
|
||||
return jsonDict;
|
||||
if (*error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
NSData *updateMetadata = [updateMetadataString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [NSJSONSerialization JSONObjectWithData:updateMetadata
|
||||
options:kNilOptions
|
||||
error:error];
|
||||
}
|
||||
|
||||
+ (NSString *)getPackageFolderPath:(NSString *)packageHash
|
||||
@@ -423,11 +407,22 @@ static NSString *const UnzippedFolderName = @"unzipped";
|
||||
return [[self getCodePushPath] stringByAppendingPathComponent:packageHash];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)getPreviousPackage:(NSError **)error
|
||||
{
|
||||
NSString *packageHash = [self getPreviousPackageHash:error];
|
||||
|
||||
if (*error || !packageHash) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [CodePushPackage getPackage:packageHash error:error];
|
||||
}
|
||||
|
||||
+ (NSString *)getPreviousPackageHash:(NSError **)error
|
||||
{
|
||||
NSDictionary *info = [self getCurrentPackageInfo:error];
|
||||
if (*error) {
|
||||
return NULL;
|
||||
return nil;
|
||||
}
|
||||
|
||||
return info[@"previousPackage"];
|
||||
|
||||
15
ios/CodePush/RCTConvert+CodePushUpdateState.m
Normal file
15
ios/CodePush/RCTConvert+CodePushUpdateState.m
Normal file
@@ -0,0 +1,15 @@
|
||||
#import "CodePush.h"
|
||||
#import "RCTConvert.h"
|
||||
|
||||
// Extending the RCTConvert class allows the React Native
|
||||
// bridge to handle args of type "CodePushUpdateState"
|
||||
@implementation RCTConvert (CodePushUpdateState)
|
||||
|
||||
RCT_ENUM_CONVERTER(CodePushUpdateState, (@{ @"codePushUpdateStateRunning": @(CodePushUpdateStateRunning),
|
||||
@"codePushUpdateStatePending": @(CodePushUpdateStatePending),
|
||||
@"codePushUpdateStateLatest": @(CodePushUpdateStateLatest)
|
||||
}),
|
||||
CodePushUpdateStateRunning, // Default enum value
|
||||
integerValue)
|
||||
|
||||
@end
|
||||
38
react-native-code-push.d.ts
vendored
38
react-native-code-push.d.ts
vendored
@@ -193,16 +193,18 @@ declare namespace CodePush {
|
||||
* @param deploymentKey The deployment key to use to query the CodePush server for an update.
|
||||
*/
|
||||
function checkForUpdate(deploymentKey?: string): ReactNativePromise<RemotePackage>;
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the metadata about the currently installed update (e.g. description, installation time, size).
|
||||
* Retrieves the metadata for an installed update (e.g. description, mandatory).
|
||||
*
|
||||
* @param updateState The state of the update you want to retrieve the metadata for. Defaults to UpdateState.RUNNING.
|
||||
*/
|
||||
function getCurrentPackage(): ReactNativePromise<LocalPackage>;
|
||||
function getUpdateMetadata(updateState?: UpdateState) : ReactNativePromise<LocalPackage>;
|
||||
|
||||
/**
|
||||
* Notifies the CodePush runtime that an installed update is considered successful.
|
||||
*/
|
||||
function notifyApplicationReady(): ReactNativePromise<void>;
|
||||
function notifyAppReady(): ReactNativePromise<void>;
|
||||
|
||||
/**
|
||||
* Immediately restarts the app.
|
||||
@@ -218,7 +220,7 @@ declare namespace CodePush {
|
||||
* @param syncStatusChangedCallback An optional callback that allows tracking the status of the sync operation, as opposed to simply checking the resolved state via the returned Promise.
|
||||
* @param downloadProgressCallback An optional callback that allows tracking the progress of an update while it is being downloaded.
|
||||
*/
|
||||
function sync(options?: SyncOptions, syncStatusChangedCallback?: SyncStatusChangedCallback, downloadProgressCallback?: DowloadProgressCallback): __React.Promise<SyncStatus>;
|
||||
function sync(options?: SyncOptions, syncStatusChangedCallback?: SyncStatusChangedCallback, downloadProgressCallback?: DowloadProgressCallback): ReactNativePromise<SyncStatus>;
|
||||
|
||||
/**
|
||||
* Indicates when you would like an installed update to actually be applied.
|
||||
@@ -241,6 +243,9 @@ declare namespace CodePush {
|
||||
ON_NEXT_RESUME
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the current status of a sync operation.
|
||||
*/
|
||||
enum SyncStatus {
|
||||
/**
|
||||
* The CodePush server is being queried for an update.
|
||||
@@ -291,6 +296,29 @@ declare namespace CodePush {
|
||||
*/
|
||||
UNKNOWN_ERROR
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the state that an update is currently in.
|
||||
*/
|
||||
enum UpdateState {
|
||||
/**
|
||||
* Indicates that an update represents the
|
||||
* version of the app that is currently running.
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* Indicates than an update has been installed, but the
|
||||
* app hasn't been restarted yet in order to apply it.
|
||||
*/
|
||||
PENDING,
|
||||
|
||||
/**
|
||||
* Indicates than an update represents the latest available
|
||||
* release, and can be either currently running or pending.
|
||||
*/
|
||||
LATEST
|
||||
}
|
||||
}
|
||||
|
||||
declare module "react-native-code-push" {
|
||||
|
||||
Reference in New Issue
Block a user