mirror of
https://github.com/zhigang1992/react-native-code-push.git
synced 2026-05-19 19:39:54 +08:00
Adding comments to CodePush class
This commit is contained in:
15
CodePush.h
15
CodePush.h
@@ -1,7 +1,18 @@
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
@interface CodePush : NSObject<RCTBridgeModule>
|
||||
@interface CodePush : NSObject <RCTBridgeModule>
|
||||
|
||||
/*
|
||||
* This method is used to retreive the URL for the most recent
|
||||
* version of the JavaScript bundle. This could be either the
|
||||
* bundle that was packaged with the app binary, or the bundle
|
||||
* that was downloaded as part of a CodePush update. The value returned
|
||||
* should be used to "bootstrap" the React Native bridge.
|
||||
*
|
||||
* This method assumes that your JS bundle is named "main.jsbundle"
|
||||
* and therefore, if it isn't, you should use either the bundleURLForResource:
|
||||
* or bundleURLForResource:withExtension: methods to override thsat behavior.
|
||||
*/
|
||||
+ (NSURL *)bundleURL;
|
||||
|
||||
+ (NSURL *)bundleURLForResource:(NSString *)resourceName;
|
||||
@@ -25,7 +36,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface CodePushDownloadHandler : NSObject<NSURLConnectionDelegate>
|
||||
@interface CodePushDownloadHandler : NSObject <NSURLConnectionDelegate>
|
||||
|
||||
@property (strong) NSOutputStream *outputFileStream;
|
||||
@property long expectedContentLength;
|
||||
|
||||
185
CodePush.m
185
CodePush.m
@@ -1,8 +1,9 @@
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#import "CodePush.h"
|
||||
|
||||
@implementation CodePush {
|
||||
@@ -15,16 +16,17 @@ static BOOL didUpdate = NO;
|
||||
static NSTimer *_timer;
|
||||
static BOOL usingTestFolder = NO;
|
||||
|
||||
static NSString * const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
|
||||
static 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
|
||||
static NSString * const PendingUpdateHashKey = @"hash";
|
||||
static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
static NSString *const PendingUpdateHashKey = @"hash";
|
||||
static NSString *const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
// Public Obj-C API (see header for method comments)
|
||||
+ (NSURL *)bundleURL
|
||||
{
|
||||
return [self bundleURLForResource:@"main"];
|
||||
@@ -43,8 +45,7 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
NSString *packageFile = [CodePushPackage getCurrentPackageBundlePath:&error];
|
||||
NSURL *binaryJsBundleUrl = [[NSBundle mainBundle] URLForResource:resourceName withExtension:resourceExtension];
|
||||
|
||||
if (error || !packageFile)
|
||||
{
|
||||
if (error || !packageFile) {
|
||||
return binaryJsBundleUrl;
|
||||
}
|
||||
|
||||
@@ -61,14 +62,21 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
}
|
||||
}
|
||||
|
||||
// Public Obj-C API
|
||||
+ (NSString *)getDocumentsDirectory
|
||||
{
|
||||
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
|
||||
return documentsDirectory;
|
||||
}
|
||||
|
||||
// Internal API methods
|
||||
// Private API methods
|
||||
|
||||
/*
|
||||
* This method cancels the currently running rollback
|
||||
* timer, which has the effect of stopping an automatic
|
||||
* rollback from occuring.
|
||||
*
|
||||
* Note: This method is safe to call from any thread.
|
||||
*/
|
||||
- (void)cancelRollbackTimer
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@@ -76,6 +84,14 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* This method checks to see whether a "pending udpate" has been applied
|
||||
* (e.g. install was called with a non-immediate mode), but the app hasn't
|
||||
* yet been restarted (either naturally or synthentically). If there is one,
|
||||
* it will restart the app (if specified), and start the rollback timer.
|
||||
*
|
||||
* Note: This method is safe to call from any thread.
|
||||
*/
|
||||
- (void)checkForPendingUpdate:(BOOL)needsRestart
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
@@ -93,15 +109,24 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
if ([pendingHash isEqualToString:currentHash]) {
|
||||
int rollbackTimeout = [pendingUpdate[PendingUpdateRollbackTimeoutKey] intValue];
|
||||
[self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:needsRestart];
|
||||
|
||||
// Clear the pending update and sync
|
||||
[preferences removeObjectForKey:PendingUpdateKey];
|
||||
[preferences synchronize];
|
||||
} else {
|
||||
// NOTE: We shouldn't ever reach here
|
||||
}
|
||||
|
||||
// Clear the pending update and sync
|
||||
[preferences removeObjectForKey:PendingUpdateKey];
|
||||
[preferences synchronize];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is meant as a handler for the global app
|
||||
* resume notification, and therefore, should not be called
|
||||
* directly. It simply checks to see whether there is a pending
|
||||
* update that is meant to be installed on resume, and if so
|
||||
* it applies it and restarts the app.
|
||||
*/
|
||||
- (void)checkForPendingUpdateDuringResume
|
||||
{
|
||||
// In order to ensure that CodePush doesn't impact the app's
|
||||
@@ -112,6 +137,12 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is used by the React Native bridge to allow
|
||||
* our plugin to expose constants to the JS-side. In our case
|
||||
* we're simply exporting enum values so that the JS and Native
|
||||
* sides of the plugin can be in sync.
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
// Export the values of the CodePushInstallMode enum
|
||||
@@ -151,6 +182,14 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method performs the actual initialization work for a update
|
||||
* to ensure that the neccessary state is setup, including:
|
||||
* --------------------------------------------------------
|
||||
* 1. Updating the current bundle URL to point at the latest update on disk
|
||||
* 2. Optionally restarting the app to load the new bundle
|
||||
* 3. Optionally starting the rollback protection timer
|
||||
*/
|
||||
- (void)initializeUpdateWithRollbackTimeout:(int)rollbackTimeout
|
||||
needsRestart:(BOOL)needsRestart
|
||||
{
|
||||
@@ -167,6 +206,10 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method checks to see whether a specific package hash
|
||||
* has previously failed installation.
|
||||
*/
|
||||
- (BOOL)isFailedHash:(NSString*)packageHash
|
||||
{
|
||||
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
||||
@@ -174,6 +217,11 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
return (failedUpdates != nil && [failedUpdates containsObject:packageHash]);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method updates the React Native bridge's bundle URL
|
||||
* to point at the latest CodePush update, and then restarts
|
||||
* the bridge. This isn't meant to be called directly.
|
||||
*/
|
||||
- (void)loadBundle
|
||||
{
|
||||
// If the current bundle URL is using http(s), then assume the dev
|
||||
@@ -187,6 +235,13 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
[_bridge reload];
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is used when an update has failed installation
|
||||
* and the app needs to be rolled back to the previous bundle.
|
||||
* This method is automatically called when the rollback timer
|
||||
* expires without the app indicating whether the update succeeded,
|
||||
* and therefore, it shouldn't be called directly.
|
||||
*/
|
||||
- (void)rollbackPackage
|
||||
{
|
||||
NSError *error;
|
||||
@@ -201,6 +256,11 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
[self loadBundle];
|
||||
}
|
||||
|
||||
/*
|
||||
* When an update failed to apply, this method can be called
|
||||
* to store its hash so that it can be ignored on future
|
||||
* attempts to check the server for an update.
|
||||
*/
|
||||
- (void)saveFailedUpdate:(NSString *)packageHash
|
||||
{
|
||||
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
||||
@@ -218,6 +278,11 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
[preferences synchronize];
|
||||
}
|
||||
|
||||
/*
|
||||
* When an update is installed whose mode isn't IMMEDIATE, this method
|
||||
* can be called to store the pending update's metadata (e.g. rollbackTimeout)
|
||||
* so that it can be used when the actual update application occurs at a later point.
|
||||
*/
|
||||
- (void)savePendingUpdate:(NSString *)packageHash
|
||||
rollbackTimeout:(int)rollbackTimeout
|
||||
{
|
||||
@@ -232,6 +297,10 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
[preferences synchronize];
|
||||
}
|
||||
|
||||
/*
|
||||
* This method handles starting the actual rollback timer
|
||||
* after an update has been installed.
|
||||
*/
|
||||
- (void)startRollbackTimer:(int)rollbackTimeout
|
||||
{
|
||||
double timeoutInSeconds = rollbackTimeout / 1000;
|
||||
@@ -243,42 +312,57 @@ static NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout";
|
||||
}
|
||||
|
||||
// JavaScript-exported module methods
|
||||
|
||||
/*
|
||||
* This is native-side of the RemotePackage.download method
|
||||
*/
|
||||
RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
[CodePushPackage downloadPackage:updatePackage
|
||||
progressCallback:^(long expectedContentLength, long receivedContentLength) {
|
||||
[self.bridge.eventDispatcher
|
||||
sendAppEventWithName:@"CodePushDownloadProgress"
|
||||
body:@{
|
||||
@"totalBytes":[NSNumber numberWithLong:expectedContentLength],
|
||||
@"receivedBytes":[NSNumber numberWithLong:receivedContentLength]
|
||||
}];
|
||||
}
|
||||
doneCallback:^{
|
||||
NSError *err;
|
||||
NSDictionary *newPackage = [CodePushPackage
|
||||
getPackage:updatePackage[@"packageHash"]
|
||||
error:&err];
|
||||
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(newPackage);
|
||||
}
|
||||
failCallback:^(NSError *err) {
|
||||
reject(err);
|
||||
}];
|
||||
// The download is progressing forward
|
||||
progressCallback:^(long expectedContentLength, long receivedContentLength) {
|
||||
// Notify the script-side about the progress
|
||||
[self.bridge.eventDispatcher
|
||||
sendAppEventWithName:@"CodePushDownloadProgress"
|
||||
body:@{
|
||||
@"totalBytes":[NSNumber numberWithLong:expectedContentLength],
|
||||
@"receivedBytes":[NSNumber numberWithLong:receivedContentLength]
|
||||
}];
|
||||
}
|
||||
// The download completed
|
||||
doneCallback:^{
|
||||
NSError *err;
|
||||
NSDictionary *newPackage = [CodePushPackage getPackage:updatePackage[@"packageHash"] error:&err];
|
||||
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(newPackage);
|
||||
}
|
||||
// The download failed
|
||||
failCallback:^(NSError *err) {
|
||||
reject(err);
|
||||
}];
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the native side of the CodePush.getConfiguration method. It isn't
|
||||
* currently exposed via the "react-native-code-push" module, and is used
|
||||
* internally only by the CodePush.checkForUpdate method in order to get the
|
||||
* app version, as well as the deployment key that was configured in the Info.plist file.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(getConfiguration:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
resolve([[CodePushConfig current] configuration]);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is the native side of the CodePush.getCurrentPackage method.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(getCurrentPackage:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
@@ -293,6 +377,9 @@ RCT_EXPORT_METHOD(getCurrentPackage:(RCTPromiseResolveBlock)resolve
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is the native side of the LocalPackage.install method.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
|
||||
rollbackTimeout:(int)rollbackTimeout
|
||||
installMode:(CodePushInstallMode)installMode
|
||||
@@ -318,6 +405,10 @@ RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* This method isn't publically exposed via the "react-native-code-push"
|
||||
* module, and is only used internally to populate the RemotePackage.failedApply property.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(isFailedUpdate:(NSString *)packageHash
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject)
|
||||
@@ -326,19 +417,26 @@ RCT_EXPORT_METHOD(isFailedUpdate:(NSString *)packageHash
|
||||
resolve(@(isFailedHash));
|
||||
}
|
||||
|
||||
/*
|
||||
* This method isn't publically exposed via the "react-native-code-push"
|
||||
* module, and is only used internally to populate the LocalPackage.isFirstRun property.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(isFirstRun:(NSString *)packageHash
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSError *error;
|
||||
BOOL isFirstRun = didUpdate
|
||||
&& nil != packageHash
|
||||
&& [packageHash length] > 0
|
||||
&& [packageHash isEqualToString:[CodePushPackage getCurrentPackageHash:&error]];
|
||||
&& nil != packageHash
|
||||
&& [packageHash length] > 0
|
||||
&& [packageHash isEqualToString:[CodePushPackage getCurrentPackageHash:&error]];
|
||||
|
||||
resolve(@(isFirstRun));
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is the native side of the CodePush.notifyApplicationReady() method.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
@@ -346,13 +444,18 @@ RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
|
||||
resolve([NSNull null]);
|
||||
}
|
||||
|
||||
// This function is exposed solely for immediately installed
|
||||
// update support, and shouldn't be consumed directly by user code.
|
||||
/*
|
||||
* This method isn't publically exposed via the "react-native-code-push"
|
||||
* module, and is only used internally to support immediately installed updates.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(restartImmedidateUpdate:(int)rollbackTimeout)
|
||||
{
|
||||
[self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is the native side of the CodePush.restartPendingUpdate() method.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(restartPendingUpdate)
|
||||
{
|
||||
[self checkForPendingUpdate:YES];
|
||||
|
||||
Reference in New Issue
Block a user