From a97407508b51972eed6623bc16a90f92880ff9f9 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Tue, 17 Nov 2015 18:24:56 -0800 Subject: [PATCH] updated sync --- CodePush.h | 10 +- CodePush.ios.js | 131 ++++++++++++------ CodePush.m | 24 ++-- CodePush.xcodeproj/project.pbxproj | 8 +- CodePushPackage.m | 2 +- .../CodePushDemoApp.xcodeproj/project.pbxproj | 8 +- ...pplyUpdateTests.m => InstallUpdateTests.m} | 10 +- .../CodePushDemoApp.ios.js | 0 .../DownloadAndInstallUpdateTest.js} | 8 +- .../InstallUpdateTestApp.ios.js} | 6 +- .../TestPackage.js | 2 +- .../CodePushDemoAppTests/README.md | 7 +- Examples/CodePushDemoApp/index.ios.js | 117 +++++++++++----- RCTConvert+CodePushInstallMode.m | 14 ++ RCTConvert+CodePushRestartMode.m | 14 -- README.md | 46 +++--- Recipes/UpdateButton.ios.js | 2 +- Recipes/UpdateOnStart.ios.js | 2 +- jsconfig.json | 7 + package-mixins.js | 4 +- 20 files changed, 266 insertions(+), 156 deletions(-) rename Examples/CodePushDemoApp/CodePushDemoAppTests/{ApplyUpdateTests.m => InstallUpdateTests.m} (93%) rename Examples/CodePushDemoApp/CodePushDemoAppTests/{ApplyUpdateTests => InstallUpdateTests}/CodePushDemoApp.ios.js (100%) rename Examples/CodePushDemoApp/CodePushDemoAppTests/{ApplyUpdateTests/DownloadAndApplyUpdateTest.js => InstallUpdateTests/DownloadAndInstallUpdateTest.js} (80%) rename Examples/CodePushDemoApp/CodePushDemoAppTests/{ApplyUpdateTests/ApplyUpdateTestApp.ios.js => InstallUpdateTests/InstallUpdateTestApp.ios.js} (89%) rename Examples/CodePushDemoApp/CodePushDemoAppTests/{ApplyUpdateTests => InstallUpdateTests}/TestPackage.js (63%) create mode 100644 RCTConvert+CodePushInstallMode.m delete mode 100644 RCTConvert+CodePushRestartMode.m create mode 100644 jsconfig.json diff --git a/CodePush.h b/CodePush.h index 390373a8e..84e4a6b30 100644 --- a/CodePush.h +++ b/CodePush.h @@ -49,7 +49,7 @@ failCallback:(void (^)(NSError *err))failCallback; @interface CodePushPackage : NSObject -+ (void)applyPackage:(NSDictionary *)updatePackage ++ (void)installPackage:(NSDictionary *)updatePackage error:(NSError **)error; + (NSDictionary *)getCurrentPackage:(NSError **)error; @@ -72,8 +72,8 @@ failCallback:(void (^)(NSError *err))failCallback; @end -typedef NS_ENUM(NSInteger, CodePushRestartMode) { - CodePushRestartModeNone, - CodePushRestartModeImmediate, - CodePushRestartModeOnNextResume +typedef NS_ENUM(NSInteger, CodePushInstallMode) { + CodePushInstallModeImmediate, + CodePushInstallModeOnNextRestart, + CodePushInstallModeOnNextResume }; \ No newline at end of file diff --git a/CodePush.ios.js b/CodePush.ios.js index 23d0c4e44..30b99863d 100644 --- a/CodePush.ios.js +++ b/CodePush.ios.js @@ -60,7 +60,7 @@ function getCurrentPackage() { return NativeCodePush.isFailedUpdate(currentPackage.packageHash); }) .then((failedUpdate) => { - localPackage.failedApply = failedUpdate; + localPackage.failedInstall = failedUpdate; return NativeCodePush.isFirstRun(localPackage.packageHash); }) .then((isFirstRun) => { @@ -98,7 +98,7 @@ function checkForUpdate() { } // Ignore updates that require a newer app version, - // since the end-user couldn't reliably apply it + // since the end-user couldn't reliably install it if (!update || update.updateAppVersion) { return resolve(null); } @@ -107,7 +107,7 @@ function checkForUpdate() { NativeCodePush.isFailedUpdate(update.packageHash) .then((isFailedHash) => { - update.failedApply = isFailedHash; + update.failedInstall = isFailedHash; resolve(update); }) .catch(reject) @@ -126,72 +126,106 @@ function checkForUpdate() { * releases, and displaying a standard confirmation UI to the end-user * when an update is available. */ -function sync(options = {}) { +function sync(options = {}, onSyncStatusChange, onDownloadProgress) { var syncOptions = { - descriptionPrefix: " Description: ", - appendReleaseDescription: false, ignoreFailedUpdates: true, - - 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?", - + installMode: CodePush.InstallMode.ON_NEXT_RESTART, rollbackTimeout: 0, - - updateTitle: "Update available", + updateDialog: null, ...options }; - + + onSyncStatusChange = typeof onSyncStatusChange == "function" + ? onSyncStatusChange + : function(syncStatus) { + switch(syncStatus) { + case CodePush.SyncStatus.CHECKING_FOR_UPDATE: + console.log("Checking for update."); + break; + case CodePush.SyncStatus.DOWNLOADING_PACKAGE: + console.log("Downloading package."); + break; + case CodePush.SyncStatus.AWAITING_USER_ACTION: + console.log("Awaiting user action."); + break; + case CodePush.SyncStatus.INSTALLING_UPDATE: + console.log("Installing update."); + break; + case CodePush.SyncStatus.IDLE: + console.log("Sync is idle."); + break; + } + }; + + onDownloadProgress = typeof onDownloadProgress == "function" + ? onDownloadProgress + : function(downloadProgress) { + console.log(`Expecting ${downloadProgress.totalBytes} bytes, received ${downloadProgress.receivedBytes} bytes.`); + }; + return new Promise((resolve, reject) => { + onSyncStatusChange(CodePush.SyncStatus.CHECKING_FOR_UPDATE); checkForUpdate() .then((remotePackage) => { - if (!remotePackage || (remotePackage.failedApply && syncOptions.ignoreFailedUpdates)) { - resolve(CodePush.SyncStatus.UP_TO_DATE); + var doDownloadAndInstall = () => { + onSyncStatusChange(CodePush.SyncStatus.DOWNLOADING_PACKAGE); + remotePackage.download(onDownloadProgress) + .then((localPackage) => { + onSyncStatusChange(CodePush.SyncStatus.INSTALLING_UPDATE); + return localPackage.install(syncOptions.rollbackTimeout, syncOptions.installMode) + }) + .then(() => { + onSyncStatusChange(CodePush.SyncStatus.IDLE); + resolve(CodePush.SyncResult.UPDATE_INSTALLED) + }) + .catch(reject) + .done(); } - else { + + if (!remotePackage || (remotePackage.failedInstall && syncOptions.ignoreFailedUpdates)) { + onSyncStatusChange(CodePush.SyncStatus.IDLE); + resolve(CodePush.SyncResult.UP_TO_DATE); + } + else if (syncOptions.updateNotification) { + syncOptions.updateNotification = Object.assign(CodePush.DEFAULT_UPDATE_DIALOG, syncOptions.updateNotification); + var message = null; var dialogButtons = [ { text: null, onPress: () => { - remotePackage.download() - .then((localPackage) => { - resolve(CodePush.SyncStatus.UPDATE_APPLIED) - return localPackage.apply(syncOptions.rollbackTimeout); - }) - .catch(reject) - .done(); + doDownloadAndInstall(); } } ]; if (remotePackage.isMandatory) { - message = syncOptions.mandatoryUpdateMessage; + message = syncOptions.updateNotification.mandatoryUpdateMessage; dialogButtons[0].text = syncOptions.mandatoryContinueButtonLabel; } else { - message = syncOptions.optionalUpdateMessage; - dialogButtons[0].text = syncOptions.optionalInstallButtonLabel; + message = syncOptions.updateNotification.optionalUpdateMessage; + dialogButtons[0].text = syncOptions.updateNotification.optionalInstallButtonLabel; // Since this is an optional update, add another button // to allow the end-user to ignore it dialogButtons.push({ - text: syncOptions.optionalIgnoreButtonLabel, - onPress: () => resolve(CodePush.SyncStatus.UPDATE_IGNORED) + text: syncOptions.updateNotification.optionalIgnoreButtonLabel, + onPress: () => resolve(CodePush.SyncResult.UPDATE_IGNORED) }); } // If the update has a description, and the developer // explicitly chose to display it, then set that as the message - if (syncOptions.appendReleaseDescription && remotePackage.description) { - message += `${syncOptions.descriptionPrefix} ${remotePackage.description}`; + if (syncOptions.updateNotification.appendReleaseDescription && remotePackage.description) { + message += `${syncOptions.updateNotification.descriptionPrefix} ${remotePackage.description}`; } + onSyncStatusChange(CodePush.SyncStatus.AWAITING_USER_ACTION); AlertIOS.alert(syncOptions.updateTitle, message, dialogButtons); + } else { + doDownloadAndInstall(); } }) .catch(reject) @@ -206,15 +240,32 @@ var CodePush = { notifyApplicationReady: NativeCodePush.notifyApplicationReady, setUpTestDependencies: setUpTestDependencies, sync: sync, - RestartMode: { - NONE: NativeCodePush.codePushRestartModeNone, // Don't artificially restart the app. Allow the update to be "picked up" on the next app restart - IMMEDIATE: NativeCodePush.codePushRestartModeImmediate, // Restart the app immediately - ON_NEXT_RESUME: NativeCodePush.codePushRestartModeOnNextResume // Restart the app the next time it is resumed from the background + 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: { + SyncResult: { UP_TO_DATE: 0, // The running app is up-to-date UPDATE_IGNORED: 1, // The app had an optional update and the end-user chose to ignore it - UPDATE_APPLIED: 2 // The app had an optional/mandatory update that was successfully downloaded and is about to be applied + UPDATE_INSTALLED: 2 // The app had an optional/mandatory update that was successfully downloaded and is about to be installed. + }, + SyncStatus: { + CHECKING_FOR_UPDATE: 1, + AWAITING_USER_ACTION: 2, + DOWNLOADING_PACKAGE: 3, + INSTALLING_UPDATE: 4, + IDLE: 5 + }, + 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?", + updateTitle: "Update available", } }; diff --git a/CodePush.m b/CodePush.m index 7298685d0..9cd7f1032 100644 --- a/CodePush.m +++ b/CodePush.m @@ -102,11 +102,11 @@ - (void)checkForPendingUpdateDuringResume - (NSDictionary *)constantsToExport { - // Export the values of the CodePushRestartMode enum + // Export the values of the CodePushInstallMode enum // so that the script-side can easily stay in sync - return @{ @"codePushRestartModeNone": @(CodePushRestartModeNone), - @"codePushRestartModeImmediate": @(CodePushRestartModeImmediate), - @"codePushRestartModeOnNextResume": @(CodePushRestartModeOnNextResume) + return @{ @"codePushInstallModeOnNextRestart": @(CodePushInstallModeOnNextRestart), + @"codePushInstallModeImmediate": @(CodePushInstallModeImmediate), + @"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume) }; }; @@ -124,7 +124,7 @@ - (CodePush *)init if (self) { // Do an async check to see whether // we need to start the rollback timer - // due to a pending update being applied at start + // due to a pending update being installed at start [self checkForPendingUpdate:NO]; // Register for app resume notifications so that we @@ -204,7 +204,7 @@ - (void)savePendingUpdate:(NSString *)packageHash rollbackTimeout:(int)rollbackTimeout { // Since we're not restarting, we need to store the fact that the update - // was applied, but hasn't yet become "active". + // was installed, but hasn't yet become "active". NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; NSDictionary *pendingUpdate = [[NSDictionary alloc] initWithObjectsAndKeys: packageHash,PendingUpdateHashKey, @@ -225,26 +225,28 @@ - (void)startRollbackTimer:(int)rollbackTimeout } // JavaScript-exported module methods -RCT_EXPORT_METHOD(applyUpdate:(NSDictionary*)updatePackage +RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage rollbackTimeout:(int)rollbackTimeout - restartMode:(CodePushRestartMode)restartMode + installMode:(CodePushInstallMode)installMode resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError *error; - [CodePushPackage applyPackage:updatePackage + [CodePushPackage installPackage:updatePackage error:&error]; if (error) { reject(error); } else { - if (restartMode == CodePushRestartModeImmediate) { + if (installMode == CodePushInstallModeImmediate) { [self initializeUpdateWithRollbackTimeout:rollbackTimeout needsRestart:YES]; } else { - _resumablePendingUpdateAvailable = (restartMode == CodePushRestartModeOnNextResume); + _resumablePendingUpdateAvailable = (installMode == CodePushInstallModeOnNextResume); [self savePendingUpdate:updatePackage[@"packageHash"] rollbackTimeout:rollbackTimeout]; + // Signal to JS that the update has been applied. + resolve(nil); } } }); diff --git a/CodePush.xcodeproj/project.pbxproj b/CodePush.xcodeproj/project.pbxproj index 9c87fecd9..a0dc04081 100644 --- a/CodePush.xcodeproj/project.pbxproj +++ b/CodePush.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 13BE3DEE1AC21097009241FE /* CodePush.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.m */; }; - 1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m */; }; + 1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */; }; 54FFEDE01BF550630061DD23 /* CodePushDownloadHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */; }; 810D4E6D1B96935000B397E9 /* CodePushPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 810D4E6C1B96935000B397E9 /* CodePushPackage.m */; }; 81D51F3A1B6181C2000DA084 /* CodePushConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D51F391B6181C2000DA084 /* CodePushConfig.m */; }; @@ -30,7 +30,7 @@ 134814201AA4EA6300B7C361 /* libCodePush.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCodePush.a; sourceTree = BUILT_PRODUCTS_DIR; }; 13BE3DEC1AC21097009241FE /* CodePush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodePush.h; sourceTree = ""; }; 13BE3DED1AC21097009241FE /* CodePush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePush.m; sourceTree = ""; }; - 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+CodePushRestartMode.m"; sourceTree = ""; }; + 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+CodePushInstallMode.m"; sourceTree = ""; }; 54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushDownloadHandler.m; sourceTree = ""; }; 810D4E6C1B96935000B397E9 /* CodePushPackage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushPackage.m; sourceTree = ""; }; 81D51F391B6181C2000DA084 /* CodePushConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushConfig.m; sourceTree = ""; }; @@ -58,7 +58,7 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m */, + 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */, 54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */, 810D4E6C1B96935000B397E9 /* CodePushPackage.m */, 81D51F391B6181C2000DA084 /* CodePushConfig.m */, @@ -124,7 +124,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushRestartMode.m in Sources */, + 1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m in Sources */, 81D51F3A1B6181C2000DA084 /* CodePushConfig.m in Sources */, 54FFEDE01BF550630061DD23 /* CodePushDownloadHandler.m in Sources */, 13BE3DEE1AC21097009241FE /* CodePush.m in Sources */, diff --git a/CodePushPackage.m b/CodePushPackage.m index 1fa696bbb..cb39dafe4 100644 --- a/CodePushPackage.m +++ b/CodePushPackage.m @@ -208,7 +208,7 @@ + (void)downloadPackage:(NSDictionary *)updatePackage [downloadHandler download:updatePackage[@"downloadUrl"]]; } -+ (void)applyPackage:(NSDictionary *)updatePackage ++ (void)installPackage:(NSDictionary *)updatePackage error:(NSError **)error { NSString *packageHash = updatePackage[@"packageHash" ]; diff --git a/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj b/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj index 3bab46c66..7ab13095a 100644 --- a/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj +++ b/Examples/CodePushDemoApp/CodePushDemoApp.xcodeproj/project.pbxproj @@ -24,7 +24,7 @@ 544161591B8BCA81000D9E25 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 5451ACBA1B86A5B600E2A7DF /* QueryUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */; }; 5451ACEC1B86E40A00E2A7DF /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5451ACEB1B86E34300E2A7DF /* libRCTTest.a */; }; - 54D774BA1B87DAF800F2ABF8 /* ApplyUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D774B91B87DAF800F2ABF8 /* ApplyUpdateTests.m */; }; + 54D774BA1B87DAF800F2ABF8 /* InstallUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D774B91B87DAF800F2ABF8 /* InstallUpdateTests.m */; }; 54F5F2B41BF6B45D007C3CEA /* DownloadProgressTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */; settings = {ASSET_TAGS = (); }; }; 81551E1B1B3B428000F5B9F1 /* libCodePush.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81551E0F1B3B427200F5B9F1 /* libCodePush.a */; }; /* End PBXBuildFile section */ @@ -151,7 +151,7 @@ 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QueryUpdateTests.m; sourceTree = ""; }; 5451ACE61B86E34300E2A7DF /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = "node_modules/react-native/Libraries/RCTTest/RCTTest.xcodeproj"; sourceTree = ""; }; - 54D774B91B87DAF800F2ABF8 /* ApplyUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ApplyUpdateTests.m; sourceTree = ""; }; + 54D774B91B87DAF800F2ABF8 /* InstallUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstallUpdateTests.m; sourceTree = ""; }; 54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadProgressTests.m; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 81551E0A1B3B427200F5B9F1 /* CodePush.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CodePush.xcodeproj; path = ../../CodePush.xcodeproj; sourceTree = ""; }; @@ -242,7 +242,7 @@ children = ( 54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */, 5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */, - 54D774B91B87DAF800F2ABF8 /* ApplyUpdateTests.m */, + 54D774B91B87DAF800F2ABF8 /* InstallUpdateTests.m */, 00E356F01AD99517003FC87E /* Supporting Files */, ); path = CodePushDemoAppTests; @@ -612,7 +612,7 @@ files = ( 5451ACBA1B86A5B600E2A7DF /* QueryUpdateTests.m in Sources */, 54F5F2B41BF6B45D007C3CEA /* DownloadProgressTests.m in Sources */, - 54D774BA1B87DAF800F2ABF8 /* ApplyUpdateTests.m in Sources */, + 54D774BA1B87DAF800F2ABF8 /* InstallUpdateTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests.m b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests.m similarity index 93% rename from Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests.m rename to Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests.m index cfdaa8c56..836c233e7 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests.m +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests.m @@ -15,11 +15,11 @@ #define TIMEOUT_SECONDS 60 #define TEXT_TO_LOOK_FOR @"If you see this, you have successfully installed an update!" -@interface ApplyUpdateTests : XCTestCase +@interface InstallUpdateTests : XCTestCase @end -@implementation ApplyUpdateTests +@implementation InstallUpdateTests { RCTTestRunner *_runner; NSString* app; @@ -27,7 +27,7 @@ @implementation ApplyUpdateTests - (void)setUp { - app = @"CodePushDemoAppTests/ApplyUpdateTests/ApplyUpdateTestApp.ios"; + app = @"CodePushDemoAppTests/InstallUpdateTests/InstallUpdateTestApp.ios"; #if __LP64__ RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)"); #endif @@ -51,7 +51,7 @@ - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test } #pragma mark Logic Tests -- (void)testDownloadAndApplyUpdate +- (void)testDownloadAndInstallUpdate { NSString *sanitizedAppName = [app stringByReplacingOccurrencesOfString:@"/" withString:@"-"]; sanitizedAppName = [sanitizedAppName stringByReplacingOccurrencesOfString:@"\\" withString:@"-"]; @@ -68,7 +68,7 @@ - (void)testDownloadAndApplyUpdate moduleProvider:nil launchOptions:nil]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge - moduleName:@"DownloadAndApplyUpdateTest" + moduleName:@"DownloadAndInstallUpdateTest" initialProperties:nil]; NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/CodePushDemoApp.ios.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/CodePushDemoApp.ios.js similarity index 100% rename from Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/CodePushDemoApp.ios.js rename to Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/CodePushDemoApp.ios.js diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/DownloadAndApplyUpdateTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/DownloadAndInstallUpdateTest.js similarity index 80% rename from Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/DownloadAndApplyUpdateTest.js rename to Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/DownloadAndInstallUpdateTest.js index 52dcbce71..39b870760 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/DownloadAndApplyUpdateTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/DownloadAndInstallUpdateTest.js @@ -9,7 +9,7 @@ var { View, } = React; -var DownloadAndApplyUpdateTest = React.createClass({ +var DownloadAndInstallUpdateTest = React.createClass({ propTypes: { shouldThrow: React.PropTypes.bool, waitOneFrame: React.PropTypes.bool, @@ -39,7 +39,7 @@ var DownloadAndApplyUpdateTest = React.createClass({ runTest() { var update = require("./TestPackage"); NativeBridge.downloadUpdate(update).done((downloadedPackage) => { - NativeBridge.applyUpdate(downloadedPackage, /*rollbackTimeout*/ 1000, /*restartImmediately*/ true); + NativeBridge.installUpdate(downloadedPackage, /*rollbackTimeout*/ 1000, /*restartImmediately*/ true); }); }, @@ -55,6 +55,6 @@ var DownloadAndApplyUpdateTest = React.createClass({ } }); -DownloadAndApplyUpdateTest.displayName = 'DownloadAndApplyUpdateTest'; +DownloadAndInstallUpdateTest.displayName = 'DownloadAndInstallUpdateTest'; -module.exports = DownloadAndApplyUpdateTest; \ No newline at end of file +module.exports = DownloadAndInstallUpdateTest; \ No newline at end of file diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/ApplyUpdateTestApp.ios.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/InstallUpdateTestApp.ios.js similarity index 89% rename from Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/ApplyUpdateTestApp.ios.js rename to Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/InstallUpdateTestApp.ios.js index 37cbb3374..01a376b34 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/ApplyUpdateTestApp.ios.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/InstallUpdateTestApp.ios.js @@ -15,14 +15,14 @@ var { } = React; var TESTS = [ - require('./DownloadAndApplyUpdateTest') + require('./DownloadAndInstallUpdateTest') ]; TESTS.forEach( (test) => AppRegistry.registerComponent(test.displayName, () => test) ); -var ApplyUpdateTestApp = React.createClass({ +var InstallUpdateTestApp = React.createClass({ getInitialState: function() { return { test: null, @@ -79,4 +79,4 @@ var styles = StyleSheet.create({ } }); -AppRegistry.registerComponent('ApplyUpdateTestApp', () => ApplyUpdateTestApp); \ No newline at end of file +AppRegistry.registerComponent('InstallUpdateTestApp', () => InstallUpdateTestApp); \ No newline at end of file diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/TestPackage.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/TestPackage.js similarity index 63% rename from Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/TestPackage.js rename to Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/TestPackage.js index 7d7308f6f..a614f83a6 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/ApplyUpdateTests/TestPackage.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/TestPackage.js @@ -1,5 +1,5 @@ var testPackage = { - downloadUrl: "http://localhost:8081/CodePushDemoAppTests/ApplyUpdateTests/CodePushDemoApp.ios.includeRequire.runModule.bundle?dev=true", + downloadUrl: "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/CodePushDemoApp.ios.includeRequire.runModule.bundle?dev=true", description: "Angry flappy birds", appVersion: "1.5.0", label: "2.4.0", diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/README.md b/Examples/CodePushDemoApp/CodePushDemoAppTests/README.md index f31ecb9ad..9ed537588 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/README.md +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/README.md @@ -8,5 +8,8 @@ Test Cases * testNewUpdate - Checks that when the remote server has a new package with a different package hash and same version as the current package, CodePushSdk.queryUpdate returns that new package without throwing an error. * testSamePackage - Checks that when the remote server has a package that is identical to the current package, CodePushSdk.queryUpdate does not return a new package nor throw an error. -* ApplyUpdateTests - Tests the functionality of installing new app updates downloaded from the server via the SDK - * testDownloadAndApplyUpdate - Queries for a new update, downloads it and then verifies that from the UI that the new update has been installed. \ No newline at end of file +* InstallUpdateTests - Tests the functionality of installing new app updates downloaded from the server via the SDK + * testDownloadAndInstallUpdate - Queries for a new update, downloads it and then verifies that from the UI that the new update has been installed. + +* DownloadProgressTests - Tests the functionality of downloading app updates and tracking the download progress via the SDK + * testDownloadProgress - Downloads three files of different sizes and verifies that the reported number of bytes tally. \ No newline at end of file diff --git a/Examples/CodePushDemoApp/index.ios.js b/Examples/CodePushDemoApp/index.ios.js index 97343c730..54f125ccb 100644 --- a/Examples/CodePushDemoApp/index.ios.js +++ b/Examples/CodePushDemoApp/index.ios.js @@ -20,49 +20,98 @@ var CodePush = require('react-native-code-push'); var CodePushDemoApp = React.createClass({ componentDidMount: function() { - this.checkUpdate(); }, - checkUpdate: function() { - CodePush.checkForUpdate().done((update) => { - this.setState({ update: update }); + sync: function() { + var self = this; + CodePush.sync( + { + updateNotification: true, + installMode: CodePush.InstallMode.ON_NEXT_RESUME + }, + function(syncStatus) { + switch(syncStatus) { + case CodePush.SyncStatus.CHECKING_FOR_UPDATE: + self.setState({ + syncMessage: "Checking for update." + }); + break; + case CodePush.SyncStatus.DOWNLOADING_PACKAGE: + self.setState({ + syncMessage: "Downloading package." + }); + break; + case CodePush.SyncStatus.AWAITING_USER_ACTION: + self.setState({ + syncMessage: "Awaiting user action." + }); + break; + case CodePush.SyncStatus.INSTALLING_UPDATE: + self.setState({ + syncMessage: "Installing update." + }); + break; + case CodePush.SyncStatus.IDLE: + self.setState({ + syncMessage: "Update installed and will be run when the app next resumes.", + progress: false + }); + break; + } + }, + function(progress) { + self.setState({ + progress: progress + }); + } + ).then(function(syncResult) { + switch(syncResult) { + case CodePush.SyncResult.UP_TO_DATE: + self.setState({ + syncMessage: "App up to date." + }); + break; + case CodePush.SyncResult.UPDATE_IGNORED: + self.setState({ + syncMessage: "Update cancelled by user." + }); + break; + } }); - CodePush.notifyApplicationReady().done(); }, getInitialState: function() { - return { update: false }; + return { }; }, - handlePress: function() { - this.state.update.download((progress) => { - this.setState({ - progress:progress - }); - }).done((localPackage) => { - localPackage.apply().done(); - }); - }, - render: function() { - var updateView; - if (this.state.progress) { - updateView = ( - {this.state.progress.receivedBytes} of {this.state.progress.totalBytes} bytes received + var syncView; + var syncButton; + var progressView; + + if (this.state.syncMessage) { + syncView = ( + {this.state.syncMessage} + ); + } else { + syncButton = ( + ); - } else if (this.state.update) { - updateView = ( - - Update Available: {'\n'} {this.state.update.scriptVersion} - {this.state.update.description} - - + } + + if (this.state.progress) { + progressView = ( + {this.state.progress.receivedBytes} of {this.state.progress.totalBytes} bytes received ); - }; + } + return ( - Welcome to Code Push! + Welcome to CodePush! - {updateView} + {syncButton} + {syncView} + {progressView} ); } @@ -71,7 +120,6 @@ var CodePushDemoApp = React.createClass({ var styles = StyleSheet.create({ container: { flex: 1, - justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, @@ -79,11 +127,10 @@ var styles = StyleSheet.create({ fontSize: 20, textAlign: 'center', margin: 10, + marginTop: 50 }, - instructions: { + messages: { textAlign: 'center', - color: '#333333', - marginBottom: 5, }, }); diff --git a/RCTConvert+CodePushInstallMode.m b/RCTConvert+CodePushInstallMode.m new file mode 100644 index 000000000..6ef74a805 --- /dev/null +++ b/RCTConvert+CodePushInstallMode.m @@ -0,0 +1,14 @@ +#import "CodePush.h" +#import "RCTConvert.h" + +// Extending the RCTConvert class allows the React Native +// bridge to handle args of type "CodePushInstallMode" +@implementation RCTConvert (CodePushInstallMode) + +RCT_ENUM_CONVERTER(CodePushInstallMode, (@{ @"codePushInstallModeImmediate": @(CodePushInstallModeImmediate), + @"codePushInstallModeOnNextRestart": @(CodePushInstallModeOnNextRestart), + @"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume) }), + CodePushInstallModeImmediate, // Default enum value + integerValue) + +@end \ No newline at end of file diff --git a/RCTConvert+CodePushRestartMode.m b/RCTConvert+CodePushRestartMode.m deleted file mode 100644 index f2d090fdd..000000000 --- a/RCTConvert+CodePushRestartMode.m +++ /dev/null @@ -1,14 +0,0 @@ -#import "CodePush.h" -#import "RCTConvert.h" - -// Extending the RCTConvert class allows the React Native -// bridge to handle args of type "CodePushRestartMode" -@implementation RCTConvert (CodePushRestartMode) - -RCT_ENUM_CONVERTER(CodePushRestartMode, (@{ @"codePushRestartModeNone": @(CodePushRestartModeNone), - @"codePushRestartModeImmediate": @(CodePushRestartModeImmediate), - @"codePushRestartModeOnNextResume": @(CodePushRestartModeOnNextResume) }), - CodePushRestartModeImmediate, // Default enum value - integerValue) - -@end \ No newline at end of file diff --git a/README.md b/README.md index 7599cf048..738bb1da7 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ This plugin provides client-side integration for the [CodePush service](https://microsoft.github.io/code-push), allowing you to easily add a dynamic code update experience to your React Native apps. -The CodePush React Native API provides two primary mechanisms for discovering updates and dynamically applying them within your apps: +The CodePush React Native API provides two primary mechanisms for discovering updates and dynamically installing them within your apps: -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 apply 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) +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 @@ -73,7 +73,7 @@ Once your Xcode project has been setup to build/link the CodePush plugin, you ne jsCodeLocation = [CodePush getBundleUrl]; ``` -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 applied update. +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. To let the CodePush runtime know which deployment it should query for updates against, perform the following steps: @@ -122,9 +122,9 @@ And that's it! For more information regarding the CodePush API, including the va When you require the `react-native-code-push` module, that object provides the following methods directly on it: * [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 applied package (e.g. description, installation time) -* [notifyApplicationReady](#codepushnotifyapplicationready): Notifies the CodePush runtime that an applied 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 -* [sync](#codepushsync): Allows checking for an update, downloading it and applying 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 +* [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 +* [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 @@ -158,7 +158,7 @@ codePush.checkForUpdate().then((update) => { codePush.getCurrentPackage(): Promise; ``` -Gets information about the currently applied package (e.g. description, installation time). +Gets information about the currently installed package (e.g. description, installation time). This method returns a Promise that resolves with the `LocalPackage` instance that represents the running update. This API is only useful for advanced scenarios, and so many devs won't need to concern themselves with it. @@ -168,7 +168,7 @@ This method returns a Promise that resolves with the `LocalPackage` instance tha codePush.notifyApplicationReady(): Promise; ``` -Notifies the CodePush runtime that an update is considered successful, and therefore, a rollback isn't neccessary. Calling this function is required whenever the `rollbackTimeout` parameter is specified when calling either ```LocalPackage.apply``` or `sync`. If you specify a `rollbackTimeout`, and don't call `notifyApplicationReady`, the CodePush runtime will assume that the applied update has failed and roll back to the previous version. +Notifies the CodePush runtime that an update is considered successful, and therefore, a rollback isn't neccessary. Calling this function is required whenever the `rollbackTimeout` parameter is specified when calling either ```LocalPackage.install``` or `sync`. If you specify a `rollbackTimeout`, and don't call `notifyApplicationReady`, the CodePush runtime will assume that the installed update has failed and roll back to the previous version. 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. @@ -178,10 +178,10 @@ If the `rollbackTimeout` parameter was not specified, the CodePush runtime will codePush.sync(options: Object): Promise; ``` -Provides a simple option for checking for an update, displaying a notification to the user, downloading it and then applying it, all while also respecting the policy that your release was published with. This method effectively composes together the "advanced mode" APIs for you, so that you don't need to handle any of the following scenarios yourself: +Provides a simple option for checking for an update, displaying a notification to the user, downloading it and then installing it, all while also respecting the policy that your release was published with. This method effectively composes together the "advanced mode" APIs for you, so that you don't need to handle any of the following scenarios yourself: 1. Checking for an update and displaying a standard confirmation dialog asking if they would like to install it -2. Automatically ignoring updates which have previously failed to apply (due to automatic rollback), and therefore, likely don't make sense trying to apply again (let's blacklist them!) +2. Automatically ignoring updates which have previously failed to install (due to automatic rollback), and therefore, likely don't make sense trying to install again (let's blacklist them!) 3. Looking to see whether an available update is mandatory, and if so, don't give the end-user the choice to ignore it 4. Displaying the description of an update to the end-user as part of the install confirmation experience @@ -197,14 +197,14 @@ The method accepts an options object that allows you to customize numerous aspec * __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?"`. -* __rollbackTimeout__ (String) - The number of seconds that you want the runtime to wait after an update has been applied before considering it failed and rolling it back. Defaults to `0`, which disabled rollback protection. +* __rollbackTimeout__ (String) - The number of seconds that you want the runtime to wait after an update has been installed before considering it failed and rolling it back. Defaults to `0`, which disabled rollback protection. * __updateTitle__ (String) - The text used as the header of an update notification that is displayed to the end-user. Defaults to `"Update available"`. -The method returns a `Promise` that is resolved to a `SyncStatus` integer code, which indicates why the `sync` call succeeded. This code can be one of the following values: +The method returns a `Promise` that is resolved to a `SyncResult` integer code, which indicates why the `sync` call succeeded. This code can be one of the following values: -* __CodePush.SyncStatus.UP_TO_DATE__ *(0)* - The app doesn't have an available update. -* __CodePush.SyncStatus.UPDATE_IGNORED__ *(1)* - The app has an optional update, that the user chose to ignore. -* __CodePush.SyncStatus.UPDATE_APPLIED__ *(2)* - The app had an optional or mandatory update that was successfully downloaded and is about to be applied. If your app needs to do any data persistence/migration before restarting, this is the time to do it. +* __CodePush.SyncResult.UP_TO_DATE__ *(0)* - The app doesn't have an available update. +* __CodePush.SyncResult.UPDATE_IGNORED__ *(1)* - The app has an optional update, that the user chose to ignore. +* __CodePush.SyncResult.UPDATE_INSTALLED__ *(2)* - The app had an optional or mandatory update that was successfully downloaded and is about to be installed. If your app needs to do any data persistence/migration before restarting, this is the time to do it. If the update check and/or the subseqeuent download fails for any reason, the `Promise` object returned by `sync` will be rejected with the reason. @@ -213,9 +213,9 @@ Example Usage: ```javascript codePush.sync() .then((status) => { - if (status == codePush.SyncStatus.UPDATE_APPLIED) { + if (status == codePush.SyncResult.UPDATE_INSTALLED) { // Do any neccessary work here before the app - // is restarted in order to apply the update + // is restarted in order to install the update } }) .catch((reason) => { @@ -229,30 +229,30 @@ The `sync` method can be called anywhere you'd like to check for an update. That The `checkForUpdate` and `getCurrentPackage` methods return promises, that when resolved, provide acces to "package" objects. The package represents your code update as well as any extra metadata (e.g. description, mandatory). The CodePush API has the distinction between the following types of packages: -* [LocalPackage](#localpackage): Represents a locally available package that either representing the currently running code or an update that hasn't been applied yet +* [LocalPackage](#localpackage): Represents a locally available package that either representing the currently running code or an update that hasn't been installed yet * [RemotePackage](#remotepackage): Represents a remotely available package that provides an update to the app, and can be downloaded #### LocalPackage -Contains details about an update package that has been downloaded locally or already applied (currently installed package). You can get a reference to an instance of this object either by calling the module-level `getCurrentPackage` method, or as the value of the promise returned by the `download` method of a RemotePackage. +Contains details about an update package that has been downloaded locally or already installed (currently installed package). You can get a reference to an instance of this object either by calling the module-level `getCurrentPackage` method, or as the value of the promise returned by the `download` method of a RemotePackage. ##### Properties - __appVersion__: The native version of the application this package update is intended for. (String) - __deploymentKey__: Deployment key of the package. (String) This is the same value that you added to your `Info.plst` file. - __description__: Package description. (String) This is the same value that you specified in the CLI when you released the update -- __failedApply__: Indicates whether this package instance had been previously applied but was rolled back. (Boolean) The `sync` method will automatically ignore updates which have previously failed, so you only need to worry about this property if using `checkForUpdate`. +- __failedInstall__: Indicates whether this package instance had been previously installed but was rolled back. (Boolean) The `sync` method will automatically ignore updates which have previously failed, so you only need to worry about this property if using `checkForUpdate`. - __label__: Package label. (String) - __isMandatory__: Flag indicating if the update is mandatory. (Boolean) This is the value that you specified in the CLI when you released the update - __packageHash__: The hash value of the package. (String) - __packageSize__: The size of the package, in bytes. (Number) -- __isFirstRun__: Flag indicating whether this is the first time the package has been run after being applied. (Boolean) This is useful for determining whether you would like to show a "What's New?" UI to the end-user after applying an update. +- __isFirstRun__: Flag indicating whether this is the first time the package has been run after being installed. (Boolean) This is useful for determining whether you would like to show a "What's New?" UI to the end-user after installing an update. ##### Methods -- __apply(rollbackTimeout: Number = 0, restartImmediately: Boolean = true): Promise<void>__: Applies this package to the application by unzipping its contents (e.g. the JS bundle) and saving it to the location on disk where the runtime expects to find the latest version of the app. If the `restartImmediately` parameter is set to `false`, the apply will complete, but it won't take effect until the next time that the app is restarted. Otherwise, the app will be immediately restarted after performing the apply, so that the end-user sees the changes. +- __install(rollbackTimeout: Number = 0, installMode: CodePush.InstallMode = CodePush.InstallMode.UPDATE_ON_RESTART): Promise<void>__: Installs this package to the application by unzipping its contents (e.g. the JS bundle) and saving it to the location on disk where the runtime expects to find the latest version of the app. If the `restartImmediately` parameter is set to `false`, the install will complete, but it won't take effect until the next time that the app is restarted. Otherwise, the app will be immediately restarted after performing the install, so that the end-user sees the changes.

If a value greater than zero is provided to the `rollbackTimeout` parameter, the application will wait for the `notifyApplicationReady` method to be called for the given number of milliseconds.

-Note: The "rollback timer" doesn't start until the update has actually become active. If you pass a truthy value to the `restartImmediately` parameter, then the rollback timer will also start immediately. However, if you pass a falsey value, then the rollback timer will start the next time the app starts, not at the point that you called `apply`. +Note: The "rollback timer" doesn't start until the update has actually become active. If you pass a truthy value to the `restartImmediately` parameter, then the rollback timer will also start immediately. However, if you pass a falsey value, then the rollback timer will start the next time the app starts, not at the point that you called `install`. #### RemotePackage diff --git a/Recipes/UpdateButton.ios.js b/Recipes/UpdateButton.ios.js index ded37e703..80be39099 100644 --- a/Recipes/UpdateButton.ios.js +++ b/Recipes/UpdateButton.ios.js @@ -27,7 +27,7 @@ var UpdateButton = React.createClass({ }, update: function() { this.state.update.download().done((newPackage) => { - newPackage.apply(); + newPackage.install(); }); }, render: function() { diff --git a/Recipes/UpdateOnStart.ios.js b/Recipes/UpdateOnStart.ios.js index 9cb3bde2c..1a937ba2b 100644 --- a/Recipes/UpdateOnStart.ios.js +++ b/Recipes/UpdateOnStart.ios.js @@ -16,7 +16,7 @@ var UpdateOnStart = React.createClass({ CodePush.checkForUpdate().done((update) => { if (update && update.downloadUrl) { update.download().done((newPackage) => { - newPackage.apply(); + newPackage.install(); }); } }); diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..65f50ea4c --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs", + "experimentalDecorators" : true + } +} \ No newline at end of file diff --git a/package-mixins.js b/package-mixins.js index d3225a638..fe245d96a 100644 --- a/package-mixins.js +++ b/package-mixins.js @@ -36,8 +36,8 @@ module.exports = (NativeCodePush) => { }; var local = { - apply: function apply(rollbackTimeout = 0, restartMode = NativeCodePush.codePushRestartModeImmediate) { - return NativeCodePush.applyUpdate(this, rollbackTimeout, restartMode); + install: function install(rollbackTimeout = 0, installMode = NativeCodePush.codePushInstallModeImmediate) { + return NativeCodePush.installUpdate(this, rollbackTimeout, installMode); } };