From f8906642d2a5ecf62c560f7983d73bf57052db02 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Thu, 3 Sep 2015 11:30:22 -0700 Subject: [PATCH] Start refactoring package management into a distinct class --- CodePush.h | 13 +++ CodePush.ios.js | 11 +-- CodePush.m | 95 +++++++------------ CodePush.xcodeproj/project.pbxproj | 4 + CodePushPackage.m | 131 ++++++++++++++++++++++++++ Examples/CodePushDemoApp/index.ios.js | 4 +- 6 files changed, 187 insertions(+), 71 deletions(-) create mode 100644 CodePushPackage.m diff --git a/CodePush.h b/CodePush.h index e1f92c2..c147beb 100644 --- a/CodePush.h +++ b/CodePush.h @@ -25,4 +25,17 @@ + (NSDictionary *)getConfiguration; +@end + +@interface CodePushPackage : NSObject + ++ (NSString *)getCurrentPackageFolderPath:(NSError **)error; + ++ (NSString *)getPackageFolderPath:(NSString *)packageHash; + ++ (void)downloadPackage:(NSDictionary *)updatePackage + error:(NSError **)error; + ++ (void)applyPackage:(NSString *)packageHash; + @end \ No newline at end of file diff --git a/CodePush.ios.js b/CodePush.ios.js index cb9da64..373e5e3 100644 --- a/CodePush.ios.js +++ b/CodePush.ios.js @@ -5,9 +5,11 @@ 'use strict'; +var extend = require("extend"); var NativeCodePush = require('react-native').NativeModules.CodePush; var requestFetchAdapter = require("./request-fetch-adapter.js"); var Sdk = require("code-push/script/acquisition-sdk").AcquisitionManager; +var packageMixins = require("./package-mixins")(NativeCodePush); // This function is only used for tests. Replaces the default SDK, configuration and native bridge function setUpTestDependencies(testSdk, testConfiguration, testNativeBridge){ @@ -67,18 +69,12 @@ function checkForUpdate() { return new Promise((resolve, reject) => { sdk.queryUpdateWithCurrentPackage(queryPackage, (err, update) => { if (err) return reject(err); - resolve(update); + resolve(extend({}, update, packageMixins.remote)); }); }); }); } -function download(updatePackage) { - // Use the downloaded package info. Native code will save the package info - // so that the client knows what the current package version is. - return NativeCodePush.downloadUpdate(updatePackage); -} - function apply(updatePackage) { return NativeCodePush.applyUpdate(updatePackage); } @@ -94,7 +90,6 @@ function notifyApplicationReady() { var CodePush = { getConfiguration: getConfiguration, checkForUpdate: checkForUpdate, - download: download, apply: apply, getCurrentPackage: getCurrentPackage, notifyApplicationReady: notifyApplicationReady, diff --git a/CodePush.m b/CodePush.m index 13fb3df..960700e 100644 --- a/CodePush.m +++ b/CodePush.m @@ -14,17 +14,9 @@ BOOL usingTestFolder = NO; @synthesize bridge = _bridge; -+ (NSString *) getBundleFolderPath -{ - NSString* home = NSHomeDirectory(); - NSString* pathExtension = [[@"CodePush/" stringByAppendingString: (usingTestFolder ? @"test/" : @"")] stringByAppendingString: @"bundle"]; - NSString* bundleFolder = [home stringByAppendingPathComponent:pathExtension]; - return bundleFolder; -} - + (NSString *) getBundlePath { - NSString * bundleFolderPath = [self getBundleFolderPath]; + NSString * bundleFolderPath = [self getPackageFolderPath]; NSString* appBundleName = @"main.jsbundle"; return [bundleFolderPath stringByAppendingPathComponent:appBundleName]; } @@ -32,14 +24,29 @@ BOOL usingTestFolder = NO; + (NSString *) getPackageFolderPath { NSString* home = NSHomeDirectory(); - NSString* pathExtension = [[@"CodePush/" stringByAppendingString: (usingTestFolder ? @"test/" : @"")] stringByAppendingString: @"package"]; + NSString* pathExtension = [[@"CodePush/" stringByAppendingString: (usingTestFolder ? @"test/" : @"")] stringByAppendingString: @"currentPackage"]; + NSString* packageFolder = [home stringByAppendingPathComponent:pathExtension]; + return packageFolder; +} + ++ (NSString *) getPreviousPackageFolderPath +{ + NSString* home = NSHomeDirectory(); + NSString* pathExtension = [[@"CodePush/" stringByAppendingString: (usingTestFolder ? @"test/" : @"")] stringByAppendingString: @"previous"]; NSString* packageFolder = [home stringByAppendingPathComponent:pathExtension]; return packageFolder; } + (NSString *) getPackagePath { - NSString * packageFolderPath = [self getPackageFolderPath]; + NSString *packageFolderPath = [self getPackageFolderPath]; + NSString* appPackageName = @"localpackage.json"; + return [packageFolderPath stringByAppendingPathComponent:appPackageName]; +} + ++ (NSString *) getPreviousPackagePath +{ + NSString * packageFolderPath = [self getPreviousPackageFolderPath]; NSString* appPackageName = @"localpackage.json"; return [packageFolderPath stringByAppendingPathComponent:appPackageName]; } @@ -86,67 +93,31 @@ RCT_EXPORT_METHOD(getConfiguration:(RCTPromiseResolveBlock)resolve resolve([CodePushConfig getConfiguration]); } -RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage +RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSURL* url = [NSURL URLWithString:updatePackage[@"downloadUrl"]]; NSError *err; - - NSString *updateContents = [[NSString alloc] initWithContentsOfURL:url - encoding:NSUTF8StringEncoding - error:&err]; - if (err) { - // TODO send download url - return reject(err); - } + [CodePushPackage downloadPackage:updatePackage + error:&err]; - dispatch_async(dispatch_get_main_queue(), ^{ - NSError *saveError; - NSString *bundleFolderPath = [CodePush getBundleFolderPath]; - if (![[NSFileManager defaultManager] fileExistsAtPath:bundleFolderPath]) { - [[NSFileManager defaultManager] createDirectoryAtPath:bundleFolderPath withIntermediateDirectories:YES attributes:nil error:&saveError]; - } - - [updateContents writeToFile:[CodePush getBundlePath] - atomically:YES - encoding:NSUTF8StringEncoding - error:&saveError]; - if (saveError) { - // TODO send file path - return reject(saveError); - } - - // Save the package info too. - NSString *packageFolderPath = [CodePush getPackageFolderPath]; - if (![[NSFileManager defaultManager] fileExistsAtPath:packageFolderPath]) { - [[NSFileManager defaultManager] createDirectoryAtPath:packageFolderPath withIntermediateDirectories:YES attributes:nil error:&saveError]; - } - - NSError *updateSerializeError; - NSData *updateSerializedData = [NSJSONSerialization dataWithJSONObject:updatePackage options:0 error:&updateSerializeError]; - - if (updateSerializeError) { - return reject(updateSerializeError); - } - - NSString *packageJsonString = [[NSString alloc] initWithData:updateSerializedData encoding:NSUTF8StringEncoding]; - [packageJsonString writeToFile:[CodePush getPackagePath] - atomically:YES - encoding:NSUTF8StringEncoding - error:&saveError]; - - if (saveError) { - return reject(saveError); - } - - [CodePush loadBundle:[CodePushConfig getRootComponent]]; + if (err) { + reject(err); + } else { resolve([NSNull null]); - }); + } }); } +RCT_EXPORT_METHOD(applyUpdate:(NSDictionary*)updatePackage + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [CodePush loadBundle:[CodePushConfig getRootComponent]]; + resolve([NSNull null]); +} + RCT_EXPORT_METHOD(writeToLocalPackage:(NSString*)packageJsonString callback:(RCTResponseSenderBlock)callback) { diff --git a/CodePush.xcodeproj/project.pbxproj b/CodePush.xcodeproj/project.pbxproj index ab476b4..1286c46 100644 --- a/CodePush.xcodeproj/project.pbxproj +++ b/CodePush.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 13BE3DEE1AC21097009241FE /* CodePush.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.m */; }; + 810D4E6D1B96935000B397E9 /* CodePushPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 810D4E6C1B96935000B397E9 /* CodePushPackage.m */; }; 81D51F3A1B6181C2000DA084 /* CodePushConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D51F391B6181C2000DA084 /* CodePushConfig.m */; }; /* End PBXBuildFile section */ @@ -27,6 +28,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 = ""; }; + 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 = ""; }; /* End PBXFileReference section */ @@ -52,6 +54,7 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( + 810D4E6C1B96935000B397E9 /* CodePushPackage.m */, 81D51F391B6181C2000DA084 /* CodePushConfig.m */, 13BE3DEC1AC21097009241FE /* CodePush.h */, 13BE3DED1AC21097009241FE /* CodePush.m */, @@ -117,6 +120,7 @@ files = ( 81D51F3A1B6181C2000DA084 /* CodePushConfig.m in Sources */, 13BE3DEE1AC21097009241FE /* CodePush.m in Sources */, + 810D4E6D1B96935000B397E9 /* CodePushPackage.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/CodePushPackage.m b/CodePushPackage.m new file mode 100644 index 0000000..66e1f29 --- /dev/null +++ b/CodePushPackage.m @@ -0,0 +1,131 @@ +#import "CodePush.h" + +@implementation CodePushPackage + +NSString * const PackageInfoFile = @"packages.json"; + ++ (NSString *)getCodePushPath +{ + return [NSHomeDirectory() stringByAppendingPathComponent:@"CodePush"]; +} + ++ (NSString *)getCurrentPackageInfoPath +{ + return [[self getCodePushPath] stringByAppendingPathComponent:PackageInfoFile]; +} + ++ (NSDictionary *)getCurrentPackageInfo:(NSError **)error +{ + NSString *content = [NSString stringWithContentsOfFile:[self getCurrentPackageInfoPath] + encoding:NSUTF8StringEncoding + error:error]; + if (*error) { + return NULL; + } + + NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:error]; + if (*error) { + return NULL; + } + + return json; +} + ++ (void)updateCurrentPackageInfo:(NSDictionary *)packageInfo + error:(NSError **)error +{ + + NSData *packageInfoData = [NSJSONSerialization dataWithJSONObject:packageInfo + options:0 + error:error]; + + NSString *packageInfoString = [[NSString alloc] initWithData:packageInfoData + encoding:NSUTF8StringEncoding]; + [packageInfoString writeToFile:[self getCurrentPackageInfoPath] + atomically:YES + encoding:NSUTF8StringEncoding + error:error]; +} + ++ (NSString *)getCurrentPackageFolderPath:(NSError **)error +{ + NSDictionary *info = [self getCurrentPackageInfo:error]; + + if (*error) { + return NULL; + } + + return [self getPackageFolderPath:info[@"currentPackage"]]; +} + ++ (NSString *)getPackageFolderPath:(NSString *)packageHash +{ + return [[self getCodePushPath] stringByAppendingPathComponent:packageHash]; +} + ++ (void)downloadPackage:(NSDictionary *)updatePackage + error:(NSError **)error +{ + NSString *packageFolderPath = [self getPackageFolderPath:updatePackage[@"packageHash"]]; + + if (![[NSFileManager defaultManager] fileExistsAtPath:packageFolderPath]) { + [[NSFileManager defaultManager] createDirectoryAtPath:packageFolderPath + withIntermediateDirectories:YES + attributes:nil + error:error]; + } + + if (error) { + return; + } + + NSURL *url = [[NSURL alloc] initWithString:updatePackage[@"downloadUrl"]]; + NSString *updateContents = [[NSString alloc] initWithContentsOfURL:url + encoding:NSUTF8StringEncoding + error:error]; + if (error) { + return; + } + + [updateContents writeToFile:[packageFolderPath stringByAppendingPathComponent:@"app.jsbundle"] + atomically:YES + encoding:NSUTF8StringEncoding + error:error]; + if (error) { + return; + } + + NSData *updateSerializedData = [NSJSONSerialization dataWithJSONObject:updatePackage + options:0 + error:error]; + + if (error) { + return; + } + + NSString *packageJsonString = [[NSString alloc] initWithData:updateSerializedData encoding:NSUTF8StringEncoding]; + [packageJsonString writeToFile:[packageFolderPath stringByAppendingPathComponent:@"app.json"] + atomically:YES + encoding:NSUTF8StringEncoding + error:error]; +} + ++ (void)applyPackage:(NSString *)packageHash + error:(NSError **)error +{ + NSDictionary *info = [self getCurrentPackageInfo:error]; + + if (error) { + return; + } + + [info setValue:packageHash forKey:@"currentPackage"]; + + [self updateCurrentPackageInfo:info + error:error]; +} + +@end \ No newline at end of file diff --git a/Examples/CodePushDemoApp/index.ios.js b/Examples/CodePushDemoApp/index.ios.js index bbd792d..fc8cfbb 100644 --- a/Examples/CodePushDemoApp/index.ios.js +++ b/Examples/CodePushDemoApp/index.ios.js @@ -30,7 +30,9 @@ var CodePushDemoApp = React.createClass({ return { update: false }; }, handlePress: function() { - CodePush.installUpdate(this.state.update).done(); + this.state.update.download((localPackage) => { + localPackage.apply().done(); + }); }, render: function() { var updateView;