diff --git a/CodePush.h b/CodePush.h index 84e4a6b..6ed5f9c 100644 --- a/CodePush.h +++ b/CodePush.h @@ -2,30 +2,26 @@ @interface CodePush : NSObject -+ (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 diff --git a/CodePush.ios.js b/CodePush.ios.js index 2c5d547..a4abcab 100644 --- a/CodePush.ios.js +++ b/CodePush.ios.js @@ -16,6 +16,51 @@ function setUpTestDependencies(providedTestSdk, providedTestConfig, testNativeBr var testConfig; var testSdk; +function checkForUpdate() { + var config; + var sdk; + + return getConfiguration() + .then((configResult) => { + config = configResult; + return getSdk(); + }) + .then((sdkResult) => { + sdk = sdkResult; + return 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 getConfiguration = (() => { var config; return function getConfiguration() { @@ -71,56 +116,15 @@ function getCurrentPackage() { }); } -function checkForUpdate() { - var config; - var sdk; - - return getConfiguration() - .then((configResult) => { - config = configResult; - return getSdk(); - }) - .then((sdkResult) => { - sdk = sdkResult; - return 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); +} + /** * The sync method provides a simple, one-line experience for * incorporating the check, download and application of an update. @@ -214,7 +218,7 @@ function sync(options = {}, syncStatusChangeCallback, downloadProgressCallback) if (typeof syncOptions.updateDialog !== "object") { syncOptions.updateDialog = CodePush.DEFAULT_UPDATE_DIALOG; } else { - syncOptions.updateDialog = Object.assign({}, CodePush.DEFAULT_UPDATE_DIALOG, syncOptions.updateDialog); + syncOptions.updateDialog = Object.assign(CodePush.DEFAULT_UPDATE_DIALOG, syncOptions.updateDialog); } var message = null; @@ -258,6 +262,7 @@ function sync(options = {}, syncStatusChangeCallback, downloadProgressCallback) } }) .catch((error) => { + console.log(error); syncStatusChangeCallback(CodePush.SyncStatus.UNKNOWN_ERROR); reject(error); }) @@ -271,6 +276,8 @@ var CodePush = { getCurrentPackage: getCurrentPackage, log: log, notifyApplicationReady: NativeCodePush.notifyApplicationReady, + restartApp: restartApp, + setDeploymentKey: NativeCodePush.setDeploymentKey, setUpTestDependencies: setUpTestDependencies, sync: sync, InstallMode: { diff --git a/CodePush.m b/CodePush.m index d29c5f0..6f223b0 100644 --- a/CodePush.m +++ b/CodePush.m @@ -11,32 +11,38 @@ 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" + withExtension:@"jsbundle"]; } -+ (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 +62,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 +117,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 +130,7 @@ NSString * const PendingUpdateRollbackTimeoutKey = @"rollbackTimeout"; [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (CodePush *)init +- (instancetype)init { self = [super init]; @@ -163,9 +176,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 +243,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 +276,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 +293,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 +327,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 +340,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; diff --git a/CodePushConfig.m b/CodePushConfig.m index d5b7fd7..0de94de 100644 --- a/CodePushConfig.m +++ b/CodePushConfig.m @@ -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 \ No newline at end of file