Start refactoring package management into a distinct class

This commit is contained in:
Will Anderson
2015-09-03 11:30:22 -07:00
parent 777b9b6fc4
commit f8906642d2
6 changed files with 187 additions and 71 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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)
{

View File

@@ -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 = "<group>"; };
13BE3DED1AC21097009241FE /* CodePush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePush.m; sourceTree = "<group>"; };
810D4E6C1B96935000B397E9 /* CodePushPackage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushPackage.m; sourceTree = "<group>"; };
81D51F391B6181C2000DA084 /* CodePushConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodePushConfig.m; sourceTree = "<group>"; };
/* 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;
};

131
CodePushPackage.m Normal file
View File

@@ -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

View File

@@ -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;