mirror of
https://github.com/zhigang1992/react-native-code-push.git
synced 2026-05-14 02:14:52 +08:00
add changes from master
This commit is contained in:
30
CodePush.h
30
CodePush.h
@@ -2,30 +2,26 @@
|
||||
|
||||
@interface CodePush : NSObject<RCTBridgeModule>
|
||||
|
||||
+ (NSURL *)getBundleUrl;
|
||||
+ (NSURL *)bundleURL;
|
||||
|
||||
+ (NSURL *)bundleURLForResourceName:(NSString *)resourceName;
|
||||
|
||||
+ (NSURL *)bundleURLForResourceName:(NSString *)resourceName
|
||||
withExtension:(NSString *)resourceExtension;
|
||||
|
||||
+ (NSString *)getDocumentsDirectory;
|
||||
|
||||
@end
|
||||
|
||||
@interface CodePushConfig : NSObject
|
||||
|
||||
+ (void)setDeploymentKey:(NSString *)deploymentKey;
|
||||
+ (NSString *)getDeploymentKey;
|
||||
@property (readonly) NSString *appVersion;
|
||||
@property (readonly) NSString *buildVersion;
|
||||
@property (readonly) NSDictionary *configuration;
|
||||
@property (copy) NSString *deploymentKey;
|
||||
@property (copy) NSString *serverURL;
|
||||
|
||||
+ (void)setServerUrl:(NSString *)setServerUrl;
|
||||
+ (NSString *)getServerUrl;
|
||||
|
||||
+ (void)setAppVersion:(NSString *)appVersion;
|
||||
+ (NSString *)getAppVersion;
|
||||
|
||||
+ (void)setBuildVersion:(NSString *)buildVersion;
|
||||
+ (NSString *)getBuildVersion;
|
||||
|
||||
+ (void)setRootComponent:(NSString *)rootComponent;
|
||||
|
||||
+ (NSString *)getRootComponent;
|
||||
|
||||
+ (NSDictionary *)getConfiguration;
|
||||
+ (instancetype)current;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
127
CodePush.js
127
CodePush.js
@@ -4,19 +4,57 @@ var requestFetchAdapter = require("./request-fetch-adapter.js");
|
||||
var Sdk = require("code-push/script/acquisition-sdk").AcquisitionManager;
|
||||
var { NativeCodePush, PackageMixins, Alert } = require("./CodePushNativePlatformAdapter");
|
||||
|
||||
// This function is only used for tests. Replaces the default SDK, configuration and native bridge
|
||||
function setUpTestDependencies(providedTestSdk, providedTestConfig, testNativeBridge){
|
||||
if (providedTestSdk) testSdk = providedTestSdk;
|
||||
if (providedTestConfig) testConfig = providedTestConfig;
|
||||
if (testNativeBridge) NativeCodePush = testNativeBridge;
|
||||
function checkForUpdate() {
|
||||
var config;
|
||||
var sdk;
|
||||
|
||||
return getConfiguration()
|
||||
.then((configResult) => {
|
||||
config = configResult;
|
||||
return getSdk();
|
||||
})
|
||||
.then((sdkResult) => {
|
||||
sdk = sdkResult;
|
||||
// Allow dynamic overwrite of function. This is only to be used for tests.
|
||||
return module.exports.getCurrentPackage();
|
||||
})
|
||||
.then((localPackage) => {
|
||||
var queryPackage = { appVersion: config.appVersion };
|
||||
if (localPackage && localPackage.appVersion === config.appVersion) {
|
||||
queryPackage = localPackage;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
sdk.queryUpdateWithCurrentPackage(queryPackage, (err, update) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
// Ignore updates that require a newer app version,
|
||||
// since the end-user couldn't reliably install it
|
||||
if (!update || update.updateAppVersion) {
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
update = Object.assign(update, PackageMixins.remote);
|
||||
|
||||
NativeCodePush.isFailedUpdate(update.packageHash)
|
||||
.then((isFailedHash) => {
|
||||
update.failedInstall = isFailedHash;
|
||||
resolve(update);
|
||||
})
|
||||
.catch(reject)
|
||||
.done();
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
var testConfig;
|
||||
var testSdk;
|
||||
|
||||
var isConfigValid = true;
|
||||
|
||||
var getConfiguration = (() => {
|
||||
var config;
|
||||
return function getConfiguration() {
|
||||
if (config) {
|
||||
if (config && isConfigValid) {
|
||||
return Promise.resolve(config);
|
||||
} else if (testConfig) {
|
||||
return Promise.resolve(testConfig);
|
||||
@@ -24,6 +62,7 @@ var getConfiguration = (() => {
|
||||
return NativeCodePush.getConfiguration()
|
||||
.then((configuration) => {
|
||||
if (!config) config = configuration;
|
||||
isConfigValid = true;
|
||||
return config;
|
||||
});
|
||||
}
|
||||
@@ -68,57 +107,35 @@ function getCurrentPackage() {
|
||||
});
|
||||
}
|
||||
|
||||
function checkForUpdate() {
|
||||
var config;
|
||||
var sdk;
|
||||
|
||||
return getConfiguration()
|
||||
.then((configResult) => {
|
||||
config = configResult;
|
||||
return getSdk();
|
||||
})
|
||||
.then((sdkResult) => {
|
||||
sdk = sdkResult;
|
||||
// Allow dynamic overwrite of function. This is only to be used for tests.
|
||||
return module.exports.getCurrentPackage();
|
||||
})
|
||||
.then((localPackage) => {
|
||||
var queryPackage = { appVersion: config.appVersion };
|
||||
if (localPackage && localPackage.appVersion === config.appVersion) {
|
||||
queryPackage = localPackage;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
sdk.queryUpdateWithCurrentPackage(queryPackage, (err, update) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
// Ignore updates that require a newer app version,
|
||||
// since the end-user couldn't reliably install it
|
||||
if (!update || update.updateAppVersion) {
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
update = Object.assign(update, PackageMixins.remote);
|
||||
|
||||
NativeCodePush.isFailedUpdate(update.packageHash)
|
||||
.then((isFailedHash) => {
|
||||
update.failedInstall = isFailedHash;
|
||||
resolve(update);
|
||||
})
|
||||
.catch(reject)
|
||||
.done();
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* Logs messages to console with the [CodePush] prefix */
|
||||
function log(message) {
|
||||
console.log(`[CodePush] ${message}`)
|
||||
}
|
||||
|
||||
function restartApp(rollbackTimeout = 0) {
|
||||
NativeCodePush.restartApp(rollbackTimeout);
|
||||
}
|
||||
|
||||
function setDeploymentKey(deploymentKey) {
|
||||
return NativeCodePush.setDeploymentKey(deploymentKey)
|
||||
.then(() => {
|
||||
// Mark the local copy of the config data
|
||||
// as invalid since we just modified it
|
||||
// on the native end.
|
||||
isConfigValid = false;
|
||||
});
|
||||
}
|
||||
|
||||
var testConfig;
|
||||
var testSdk;
|
||||
|
||||
// This function is only used for tests. Replaces the default SDK, configuration and native bridge
|
||||
function setUpTestDependencies(providedTestSdk, providedTestConfig, testNativeBridge) {
|
||||
if (providedTestSdk) testSdk = providedTestSdk;
|
||||
if (providedTestConfig) testConfig = providedTestConfig;
|
||||
if (testNativeBridge) NativeCodePush = testNativeBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sync method provides a simple, one-line experience for
|
||||
* incorporating the check, download and application of an update.
|
||||
@@ -269,6 +286,8 @@ var CodePush = {
|
||||
getCurrentPackage: getCurrentPackage,
|
||||
log: log,
|
||||
notifyApplicationReady: NativeCodePush.notifyApplicationReady,
|
||||
restartApp: restartApp,
|
||||
setDeploymentKey: setDeploymentKey,
|
||||
setUpTestDependencies: setUpTestDependencies,
|
||||
sync: sync,
|
||||
InstallMode: {
|
||||
|
||||
130
CodePush.m
130
CodePush.m
@@ -11,32 +11,37 @@
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
BOOL didUpdate = NO;
|
||||
NSTimer *_timer;
|
||||
BOOL usingTestFolder = NO;
|
||||
static BOOL didUpdate = NO;
|
||||
static NSTimer *_timer;
|
||||
static BOOL usingTestFolder = NO;
|
||||
|
||||
NSString * const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
|
||||
NSString * const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
|
||||
static NSString * const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
|
||||
static NSString * const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
|
||||
|
||||
// These keys are already "namespaced" by the PendingUpdateKey, so
|
||||
// their values don't need to be obfuscated to prevent collision with app data
|
||||
NSString * const PendingUpdateHashKey = @"hash";
|
||||
NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
static NSString * const PendingUpdateHashKey = @"hash";
|
||||
static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
// Public Obj-C API
|
||||
+ (NSString *)getDocumentsDirectory
|
||||
+ (NSURL *)bundleURL
|
||||
{
|
||||
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
|
||||
return documentsDirectory;
|
||||
return [self bundleURLForResourceName:@"main"];
|
||||
}
|
||||
|
||||
+ (NSURL *)getBundleUrl
|
||||
+ (NSURL *)bundleURLForResourceName:(NSString *)resourceName
|
||||
{
|
||||
return [self bundleURLForResourceName:resourceName
|
||||
withExtension:@"jsbundle"];
|
||||
}
|
||||
|
||||
+ (NSURL *)bundleURLForResourceName:(NSString *)resourceName
|
||||
withExtension:(NSString *)resourceExtension
|
||||
{
|
||||
NSError *error;
|
||||
NSString *packageFile = [CodePushPackage getCurrentPackageBundlePath:&error];
|
||||
NSURL *binaryJsBundleUrl = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
NSURL *binaryJsBundleUrl = [[NSBundle mainBundle] URLForResource:resourceName withExtension:resourceExtension];
|
||||
|
||||
if (error || !packageFile)
|
||||
{
|
||||
@@ -56,6 +61,13 @@ NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
}
|
||||
}
|
||||
|
||||
// Public Obj-C API
|
||||
+ (NSString *)getDocumentsDirectory
|
||||
{
|
||||
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
|
||||
return documentsDirectory;
|
||||
}
|
||||
|
||||
// Internal API methods
|
||||
- (void)cancelRollbackTimer
|
||||
{
|
||||
@@ -104,10 +116,10 @@ NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
{
|
||||
// Export the values of the CodePushInstallMode enum
|
||||
// so that the script-side can easily stay in sync
|
||||
return @{ @"codePushInstallModeOnNextRestart": @(CodePushInstallModeOnNextRestart),
|
||||
return @{ @"codePushInstallModeOnNextRestart":@(CodePushInstallModeOnNextRestart),
|
||||
@"codePushInstallModeImmediate": @(CodePushInstallModeImmediate),
|
||||
@"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume)
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
- (void)dealloc
|
||||
@@ -117,7 +129,7 @@ NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (CodePush *)init
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@@ -163,9 +175,14 @@ NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
|
||||
- (void)loadBundle
|
||||
{
|
||||
// Reset the runtime's bundle to be
|
||||
// the latest URL, and then force a refresh
|
||||
_bridge.bundleURL = [CodePush getBundleUrl];
|
||||
// If the current bundle URL is using http(s), then assume the dev
|
||||
// is debugging and therefore, shouldn't be redirected to a local
|
||||
// file (since Chrome wouldn't support it). Otherwise, update
|
||||
// the current bundle URL to point at the latest update
|
||||
if (![_bridge.bundleURL.scheme hasPrefix:@"http"]) {
|
||||
_bridge.bundleURL = [CodePush bundleURL];
|
||||
}
|
||||
|
||||
[_bridge reload];
|
||||
}
|
||||
|
||||
@@ -225,36 +242,6 @@ NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
}
|
||||
|
||||
// JavaScript-exported module methods
|
||||
RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
|
||||
rollbackTimeout:(int)rollbackTimeout
|
||||
installMode:(CodePushInstallMode)installMode
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSError *error;
|
||||
[CodePushPackage installPackage:updatePackage
|
||||
error:&error];
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
if (installMode != CodePushInstallModeImmediate) {
|
||||
_resumablePendingUpdateAvailable = (installMode == CodePushInstallModeOnNextResume);
|
||||
[self savePendingUpdate:updatePackage[@"packageHash"]
|
||||
rollbackTimeout:rollbackTimeout];
|
||||
}
|
||||
// Signal to JS that the update has been applied.
|
||||
resolve(nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Only to be used in the case of installing updates with InstallMode.IMMEDIATE
|
||||
RCT_EXPORT_METHOD(restartApp:(int)rollbackTimeout){
|
||||
[self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:YES];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
@@ -288,7 +275,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
||||
RCT_EXPORT_METHOD(getConfiguration:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
resolve([CodePushConfig getConfiguration]);
|
||||
resolve([[CodePushConfig current] configuration]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getCurrentPackage:(RCTPromiseResolveBlock)resolve
|
||||
@@ -305,6 +292,31 @@ RCT_EXPORT_METHOD(getCurrentPackage:(RCTPromiseResolveBlock)resolve
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
|
||||
rollbackTimeout:(int)rollbackTimeout
|
||||
installMode:(CodePushInstallMode)installMode
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSError *error;
|
||||
[CodePushPackage installPackage:updatePackage
|
||||
error:&error];
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
if (installMode != CodePushInstallModeImmediate) {
|
||||
_resumablePendingUpdateAvailable = (installMode == CodePushInstallModeOnNextResume);
|
||||
[self savePendingUpdate:updatePackage[@"packageHash"]
|
||||
rollbackTimeout:rollbackTimeout];
|
||||
}
|
||||
// Signal to JS that the update has been applied.
|
||||
resolve(nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(isFailedUpdate:(NSString *)packageHash
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject)
|
||||
@@ -314,8 +326,8 @@ RCT_EXPORT_METHOD(isFailedUpdate:(NSString *)packageHash
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(isFirstRun:(NSString *)packageHash
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSError *error;
|
||||
BOOL isFirstRun = didUpdate
|
||||
@@ -327,12 +339,24 @@ RCT_EXPORT_METHOD(isFirstRun:(NSString *)packageHash
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
[self cancelRollbackTimer];
|
||||
resolve([NSNull null]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(restartApp:(int)rollbackTimeout){
|
||||
[self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:YES];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setDeploymentKey:(NSString *)deploymentKey
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
[[CodePushConfig current] setDeploymentKey:deploymentKey];
|
||||
resolve(nil);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setUsingTestFolder:(BOOL)shouldUseTestFolder)
|
||||
{
|
||||
usingTestFolder = shouldUseTestFolder;
|
||||
|
||||
@@ -1,85 +1,83 @@
|
||||
#import "CodePush.h"
|
||||
|
||||
NSMutableDictionary *configuration;
|
||||
@implementation CodePushConfig {
|
||||
NSMutableDictionary *_configDictionary;
|
||||
}
|
||||
|
||||
@implementation CodePushConfig
|
||||
static CodePushConfig *_currentConfig;
|
||||
|
||||
static NSString * const AppVersionConfigKey = @"appVersion";
|
||||
static NSString * const BuildVdersionConfigKey = @"buildVersion";
|
||||
static NSString * const DeploymentKeyConfigKey = @"deploymentKey";
|
||||
static NSString * const ServerURLConfigKey = @"serverUrl";
|
||||
|
||||
+ (instancetype)current
|
||||
{
|
||||
return _currentConfig;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
_currentConfig = [[CodePushConfig alloc] init];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
|
||||
|
||||
NSString *appVersion = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
|
||||
NSString *buildVersion = [infoDictionary objectForKey:(NSString *)kCFBundleVersionKey];
|
||||
NSString *deploymentKey = [infoDictionary objectForKey:@"CodePushDeploymentKey"];
|
||||
NSString *serverUrl = [infoDictionary objectForKey:@"CodePushServerUrl"];
|
||||
if (!serverUrl) {
|
||||
serverUrl = @"https://codepush.azurewebsites.net/";
|
||||
}
|
||||
NSString *rootComponent = [infoDictionary objectForKey:@"CFBundleName"];
|
||||
NSString *serverURL = [infoDictionary objectForKey:@"CodePushServerURL"];
|
||||
|
||||
configuration = [[NSMutableDictionary alloc]
|
||||
initWithObjectsAndKeys:
|
||||
appVersion,@"appVersion",
|
||||
buildVersion,@"buildVersion",
|
||||
deploymentKey,@"deploymentKey",
|
||||
serverUrl,@"serverUrl",
|
||||
rootComponent,@"rootComponent",
|
||||
nil];
|
||||
if (!serverURL) {
|
||||
serverURL = @"https://codepush.azurewebsites.net/";
|
||||
}
|
||||
|
||||
_configDictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
|
||||
appVersion,AppVersionConfigKey,
|
||||
buildVersion,BuildVdersionConfigKey,
|
||||
serverURL,ServerURLConfigKey,
|
||||
deploymentKey,DeploymentKeyConfigKey,
|
||||
nil];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (void)setDeploymentKey:(NSString *)deploymentKey
|
||||
- (NSString *)appVersion
|
||||
{
|
||||
[configuration setValue:deploymentKey forKey:@"deploymentKey"];
|
||||
return [_configDictionary objectForKey:AppVersionConfigKey];
|
||||
}
|
||||
|
||||
+ (NSString *)getDeploymentKey
|
||||
- (NSString *)buildVersion
|
||||
{
|
||||
return [configuration objectForKey:@"deploymentKey"];
|
||||
return [_configDictionary objectForKey:BuildVdersionConfigKey];
|
||||
}
|
||||
|
||||
+ (void)setServerUrl:(NSString *)serverUrl
|
||||
- (NSDictionary *)configuration
|
||||
{
|
||||
[configuration setValue:serverUrl forKey:@"serverUrl"];
|
||||
return _configDictionary;
|
||||
}
|
||||
|
||||
+ (NSString *)getServerUrl
|
||||
- (NSString *)deploymentKey
|
||||
{
|
||||
return [configuration objectForKey:@"serverUrl"];
|
||||
return [_configDictionary objectForKey:DeploymentKeyConfigKey];
|
||||
}
|
||||
|
||||
+ (void)setAppVersion:(NSString *)appVersion
|
||||
- (NSString *)serverURL
|
||||
{
|
||||
[configuration setValue:appVersion forKey:@"appVersion"];
|
||||
return [_configDictionary objectForKey:ServerURLConfigKey];
|
||||
}
|
||||
|
||||
+ (NSString *)getAppVersion
|
||||
- (void)setDeploymentKey:(NSString *)deploymentKey
|
||||
{
|
||||
return [configuration objectForKey:@"appVersion"];
|
||||
[_configDictionary setValue:deploymentKey forKey:DeploymentKeyConfigKey];
|
||||
}
|
||||
|
||||
+ (void)setBuildVersion:(NSString *)buildVersion
|
||||
- (void)setServerURL:(NSString *)serverURL
|
||||
{
|
||||
[configuration setValue:buildVersion forKey:@"buildVersion"];
|
||||
[_configDictionary setValue:serverURL forKey:ServerURLConfigKey];
|
||||
}
|
||||
|
||||
+ (NSString *)getBuildVersion
|
||||
{
|
||||
return [configuration objectForKey:@"buildVersion"];
|
||||
}
|
||||
|
||||
+ (void)setRootComponent:(NSString *)rootComponent
|
||||
{
|
||||
[configuration setValue:rootComponent forKey:@"rootComponent"];
|
||||
}
|
||||
|
||||
+ (NSString *)getRootComponent
|
||||
{
|
||||
return [configuration objectForKey:@"rootComponent"];
|
||||
}
|
||||
|
||||
+ (NSDictionary *) getConfiguration
|
||||
{
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -17,7 +17,8 @@ if (Platform.OS === "android") {
|
||||
"getCurrentPackage",
|
||||
"isFailedUpdate",
|
||||
"isFirstRun",
|
||||
"notifyApplicationReady"
|
||||
"notifyApplicationReady",
|
||||
"setDeploymentKey"
|
||||
];
|
||||
|
||||
methodsToPromisify.forEach((methodName) => {
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
* see http://facebook.github.io/react-native/docs/runningondevice.html
|
||||
*/
|
||||
|
||||
jsCodeLocation = [CodePush getBundleUrl];
|
||||
jsCodeLocation = [CodePush bundleURL];
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
|
||||
moduleName:@"CodePushDemoApp"
|
||||
|
||||
78
README.md
78
README.md
@@ -7,7 +7,7 @@ The CodePush React Native API provides two primary mechanisms for discovering up
|
||||
1. [**Sync mode**](#codepushsync), which allows you to call a single method--presumably as part of mounting your app's root component or in response to a button click--that will automatically check for an update, download and install it, while respecting the policies and metadata associated with each release (e.g. if the release is mandatory then it doesn't give the end-user the option to ignore it)
|
||||
2. [**Advanced mode**](#codepushcheckforupdate), which provides a handful of "low-level" methods which give you complete control over the update experience, at the cost of added complexity.
|
||||
|
||||
When getting started using CodePush, we would recommended using the sync mode until you discover that it doesn't suit your needs. That said, if you have a user scenario
|
||||
When getting started using CodePush, we recommended using the sync method until you discover that it doesn't suit your needs. That said, if you have a user scenario
|
||||
that isn't currently covered well by sync, please [let us know](mailto:codepushfeed@microsoft.com) since that would be valuable feedback.
|
||||
|
||||
*NOTE: We don't currently have support for an "automatic mode" which provides a "code-free" experience to adding in dynamic update discovery and acquisition. If that would be valuable to you, please [let us know](mailto:codepushfeed@microsoft.com).*
|
||||
@@ -19,11 +19,9 @@ that isn't currently covered well by sync, please [let us know](mailto:codepushf
|
||||
|
||||
## How does it work?
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/116461/10835297/20b7cdf0-7e5e-11e5-8e44-ea6144839e5f.png" align="right" />
|
||||
|
||||
A React Native application's assets (JavaScript code and other resources) are traditionally bundled up as a ```.jsbundle``` file which is loaded from the application installation location on the target device during runtime. After you submit an update to the store, the user downloads the update, and those assets will be replaced with the new assets.
|
||||
|
||||
CodePush is here to simplify this process by allowing you to instantly update your application's assets without having to submit a new update to the store. We do this by allowing you to upload and manage your React Native app bundles on our CodePush server. In the application, we check for the presence of updated bundles on the server. If they are available, we will install and persist them to the internal storage of the device. If a new bundle is installed, the application will reload from the updated package location.
|
||||
CodePush is here to simplify this process by allowing you to instantly update your application's assets without having to submit a new update to the store. We do this by allowing you to upload and manage your React Native app bundles on the CodePush server. In the application, we check for the presence of updated bundles on the server. If they are available, we will install and persist them to the internal storage of the device. If a new bundle is installed, the application will reload from the updated package location.
|
||||
|
||||
## Plugin Acquisition
|
||||
|
||||
@@ -81,24 +79,29 @@ Once your Xcode project has been setup to build/link the CodePush plugin, you ne
|
||||
#import "CodePush.h"
|
||||
```
|
||||
|
||||
2. Find the following line of code, which loads your JS Bundle from the packager's dev server:
|
||||
2. Find the following line of code, which loads your JS Bundle from the app binary:
|
||||
|
||||
```
|
||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
|
||||
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
```
|
||||
|
||||
3. Replace it with this line:
|
||||
|
||||
```
|
||||
jsCodeLocation = [CodePush getBundleUrl];
|
||||
jsCodeLocation = [CodePush bundleURL];
|
||||
```
|
||||
|
||||
This change configures your app to always load the most recent version of your app's JS bundle. On the initial launch, this will correspond to the file that was compiled with the app. However, after an update has been pushed via CodePush, this will return the location of the most recently installed update.
|
||||
|
||||
*NOTE: The `bundleURL` method assumes your app's JS bundle is named `main.jsbundle`. If you have configured your app to use a different file name, simply call the `bundleURLForResourceName:` method (which assumes you're using the `.jsbundle` extension) or `bundleURLForResourceName:withExtension:` method instead, in order to overwrite that default behavior*
|
||||
|
||||
To let the CodePush runtime know which deployment it should query for updates against, perform the following steps:
|
||||
|
||||
1. Open your app's `Info.plist` and add a new `CodePushDeploymentKey` entry, whose value is the key of the deployment you want to configure this app against (e.g. the Staging deployment for FooBar app)
|
||||
2. In your app's `Info.plist` make sure your `CFBundleShortVersionString` value is a valid [semver](http://semver.org/) version (e.g. 1.0.0 not 1.0)
|
||||
|
||||
*NOTE: If you'd prefer, you can also set the deployment key in code by assigning the key to the `[CodePushConfig current].deploymentKey` property.*
|
||||
|
||||
## Plugin Configuration - Android
|
||||
|
||||
After installing the plugin and sync-ing your Android Studio project with Gradle, you need to configure your app to consult CodePush for the location of your JS bundle, since it will "take control" of managing the current and all future versions. To do this, perform the following steps:
|
||||
@@ -120,12 +123,16 @@ public class MainActivity extends FragmentActivity implements DefaultHardwareBac
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
...
|
||||
// Initialize CodePush with your deployment key and an instance of your MainActivity
|
||||
// You can also set the deployment key in code by assigning the key to the `[CodePushConfig current].deploymentKey` property.*
|
||||
|
||||
codePush = new CodePush("d73bf5d8-4fbd-4e55-a837-accd328a21ba", this);
|
||||
...
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(getApplication())
|
||||
...
|
||||
// Let CodePush determine which location to load the most updated bundle from
|
||||
// If there is no updated bundle from CodePush, the location will be the assets
|
||||
// folder with the name of the bundle passed in, e.g. index.android.bundle
|
||||
.setJSBundleFile(codePush.getBundleUrl("index.android.bundle"))
|
||||
// Expose the CodePush module to JavaScript
|
||||
.addPackage(codePush.getReactPackage())
|
||||
@@ -167,8 +174,7 @@ The simplest way to do this is to perform the following in your app's root compo
|
||||
CodePush.sync();
|
||||
```
|
||||
|
||||
If an update is available, a dialog will be displayed to the user asking them if they would like to install it. If the update was marked as mandatory, then the dialog will
|
||||
omit the option to decline installation. The `sync` method takes a handful of options to customize this experience, so refer to its [API reference](#codepushsync) if you'd like to tweak its default behavior.
|
||||
If an update is available, it will be silently download, and will be installed the next time the app is restarted. This experience ensures the least invasive experience for your end-users. If you would like to display a confirmation dialog, or customize the update experience in any way, refer to the `sync` method's [API reference](#codepushsync) for information on how to tweak this default behavior.
|
||||
|
||||
## Releasing code updates
|
||||
|
||||
@@ -190,6 +196,8 @@ When you require the `react-native-code-push` module, that object provides the f
|
||||
* [checkForUpdate](#codepushcheckforupdate): Queries the CodePush service for an update against the configured deployment. This method returns a promise which resolves to a `RemotePackage` that can be subsequently downloaded.
|
||||
* [getCurrentPackage](#codepushgetcurrentpackage): Gets information about the currently installed package (e.g. description, installation time)
|
||||
* [notifyApplicationReady](#codepushnotifyapplicationready): Notifies the CodePush runtime that an installed update is considered successful. This is an optional API, but is useful when you want to expicitly enable "rollback protection" in the event that an exception occurs in any code that you've deployed to production
|
||||
* [restartApp](#codepushrestartapp): Installs a pending update by immediately restarting the app.
|
||||
* [setDeploymentKey](#codepushsetdeploymentkey): Dynamically updates the deployment key that the CodePush runtime will use to query for app updates.
|
||||
* [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.checkForUpdate
|
||||
@@ -238,6 +246,40 @@ Notifies the CodePush runtime that an update is considered successful, and there
|
||||
|
||||
If the `rollbackTimeout` parameter was not specified, the CodePush runtime will not enforce any automatic rollback behavior, and therefore, calling this function is not required and will result in a no-op.
|
||||
|
||||
#### codePush.restartApp
|
||||
|
||||
```javascript
|
||||
codePush.restartApp(rollbackTimeout: Number = 0): void;
|
||||
```
|
||||
|
||||
Installs the pending update (if applicable) by immediately restarting the app, and optionally starting the rollback timer. This method is for advanced scenarios, and is useful when the following conditions are true:
|
||||
|
||||
1. Your app is specifying an install mode value of `ON_NEXT_RESTART` when calling `sync` or `LocalPackage.install`, which has the effect of not applying your update until the app has been restarted (by either the end-user or OS)
|
||||
2. You have an app-specific user event (e.g. the end-user navigated back to the app's home page) that allows you to apply the update in an unobtrusive way, and potentially gets the update in front of the end-user sooner then waiting until the next restart.
|
||||
|
||||
The `rollbackTimeout` parameter has the same behavior as the equivalent in the `sync` and `checkForUpdate` method, and allows your app to have control over the point that an update is installed, while still benefitting from rollback production.
|
||||
|
||||
#### codePush.setDeploymentKey
|
||||
|
||||
```javascript
|
||||
codePush.setDeploymentKey(deploymentKey: String): Promise<void>;
|
||||
```
|
||||
|
||||
Dynamically updates the deployment key that the CodePush runtime will use to query for app updates. This is beneficial if your app has a default deployment key which you added to your `Info.plist` file, but you want to dynamically change it at runtime based on some app-specific policy (e.g. you want to give early access to certain users, by pointing them at your staging deployment).
|
||||
|
||||
The method simply takes a string representing the new deployment, and returns a `Promise` that will resolve once the specified deployment key has been applied, and calls to `sync` and/or `checkForUpdate` could be successfully called.
|
||||
|
||||
Example Usage:
|
||||
|
||||
```javascript
|
||||
codePush.setDeploymentKey("SOME_VALID_KEY_VALUE").then(() => {
|
||||
// The following call to sync with query the updated
|
||||
// app deployment for an update
|
||||
codePush.sync();
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
#### codePush.sync
|
||||
|
||||
```javascript
|
||||
@@ -353,20 +395,6 @@ The `RemotePackage` inherits all of the same properties as the `LocalPackage`, b
|
||||
|
||||
---
|
||||
|
||||
## Running the Example
|
||||
## Debugging
|
||||
|
||||
* Clone this repository
|
||||
* From the root of this project, run `npm install`
|
||||
* `cd` into `Examples/CodePushDemoApp`
|
||||
* From this demo app folder, run `npm install`
|
||||
* Open `Info.plist` and fill in the value for CodePushDeploymentKey
|
||||
* Run `npm start` to launch the packager
|
||||
* Open `CodePushDemoApp.xcodeproj` in Xcode
|
||||
* Launch the project
|
||||
|
||||
## Running Tests
|
||||
|
||||
* Open `CodePushDemoApp.xcodeproj` in Xcode
|
||||
* Navigate to the test explorer (small grey diamond near top left)
|
||||
* Click on the 'play' button next to CodePushDemoAppTests
|
||||
* After the tests are completed, green ticks should appear next to the test cases to indicate success
|
||||
When debugging your JavaScript using Chrome, make sure that your JS bundle location is configured in your `AppDelegate.m` file to point at the packager URL, since that will provide you with the most effecient debugging experience. Since your CodePush deployment key is specified in either the `Info.plist` file, by setting the `[CodePushConfig current].deploymentKey` property, or by calling the `codePush.setDeploymentKey()` method from JavaScript, any calls to `sync` or `checkForUpdate` will work just fine regardless if your `AppDelegate.m` file hasn't be configured to use the `[CodePush bundleURL]` method for its JS bundle location.
|
||||
@@ -345,6 +345,12 @@ public class CodePush {
|
||||
usingTestFolder = shouldUseTestFolder;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setDeploymentKey(String deploymentKey, Callback resolve, Callback reject) {
|
||||
codePushConfig.setDeploymentKey(deploymentKey);
|
||||
resolve.invoke("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
final Map<String, Object> constants = new HashMap<>();
|
||||
|
||||
Reference in New Issue
Block a user