Merge pull request #186 from Microsoft/bundle-date-ios

Attach binary bundle date to package metadata on download
This commit is contained in:
Geoffrey Goh
2016-02-05 18:20:02 -08:00
3 changed files with 75 additions and 24 deletions

View File

@@ -15,10 +15,6 @@ RCT_EXPORT_MODULE()
#pragma mark - Private constants
static BOOL needToReportRollback = NO;
static BOOL isRunningBinaryVersion = NO;
static BOOL testConfigurationFlag = NO;
// These constants represent valid deployment statuses
static NSString *const DeploymentFailed = @"DeploymentFailed";
static NSString *const DeploymentSucceeded = @"DeploymentSucceeded";
@@ -34,52 +30,64 @@ static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
// These keys are used to inspect/augment the metadata
// that is associated with an update's package.
static NSString *const BinaryBundleDateKey = @"binaryDate";
static NSString *const PackageHashKey = @"packageHash";
static NSString *const PackageIsPendingKey = @"isPending";
#pragma mark - Static variables
static BOOL isRunningBinaryVersion = NO;
static BOOL needToReportRollback = NO;
static BOOL testConfigurationFlag = NO;
// These values are used to save the bundleURL and extension for the JS bundle
// in the binary.
static NSString *bundleResourceExtension = @"jsbundle";
static NSString *bundleResourceName = @"main";
#pragma mark - Public Obj-C API
+ (NSURL *)bundleURL
{
return [self bundleURLForResource:@"main"];
return [self bundleURLForResource:bundleResourceName];
}
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
{
bundleResourceName = resourceName;
return [self bundleURLForResource:resourceName
withExtension:@"jsbundle"];
withExtension:bundleResourceExtension];
}
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
withExtension:(NSString *)resourceExtension
{
bundleResourceName = resourceName;
bundleResourceExtension = resourceExtension;
NSError *error;
NSString *packageFile = [CodePushPackage getCurrentPackageBundlePath:&error];
NSURL *binaryJsBundleUrl = [[NSBundle mainBundle] URLForResource:resourceName withExtension:resourceExtension];
NSURL *binaryBundleURL = [self binaryBundleURL];
NSString *logMessageFormat = @"Loading JS bundle from %@";
if (error || !packageFile) {
NSLog(logMessageFormat, binaryJsBundleUrl);
NSLog(logMessageFormat, binaryBundleURL);
isRunningBinaryVersion = YES;
return binaryJsBundleUrl;
return binaryBundleURL;
}
NSDictionary *binaryFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[binaryJsBundleUrl path] error:nil];
NSDictionary *appFileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:packageFile error:nil];
NSDate *binaryDate = [binaryFileAttributes objectForKey:NSFileModificationDate];
NSDate *packageDate = [appFileAttribs objectForKey:NSFileModificationDate];
NSString *binaryAppVersion = [[CodePushConfig current] appVersion];
NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage:&error];
if (error || !currentPackageMetadata) {
NSLog(logMessageFormat, binaryJsBundleUrl);
NSLog(logMessageFormat, binaryBundleURL);
isRunningBinaryVersion = YES;
return binaryJsBundleUrl;
return binaryBundleURL;
}
NSString *packageDate = [currentPackageMetadata objectForKey:BinaryBundleDateKey];
NSString *packageAppVersion = [currentPackageMetadata objectForKey:@"appVersion"];
if ([binaryDate compare:packageDate] == NSOrderedAscending && ([CodePush isUsingTestConfiguration] ||[binaryAppVersion isEqualToString:packageAppVersion])) {
if ([[self modifiedDateStringOfFileAtURL:binaryBundleURL] isEqualToString:packageDate] && ([CodePush isUsingTestConfiguration] ||[binaryAppVersion isEqualToString:packageAppVersion])) {
// Return package file because it is newer than the app store binary's JS bundle
NSURL *packageUrl = [[NSURL alloc] initFileURLWithPath:packageFile];
NSLog(logMessageFormat, packageUrl);
@@ -90,9 +98,9 @@ static NSString *const PackageIsPendingKey = @"isPending";
[CodePush clearUpdates];
#endif
NSLog(logMessageFormat, binaryJsBundleUrl);
NSLog(logMessageFormat, binaryBundleURL);
isRunningBinaryVersion = YES;
return binaryJsBundleUrl;
return binaryBundleURL;
}
}
@@ -141,6 +149,11 @@ static NSString *const PackageIsPendingKey = @"isPending";
@synthesize bridge = _bridge;
+ (NSURL *)binaryBundleURL
{
return [[NSBundle mainBundle] URLForResource:bundleResourceName withExtension:bundleResourceExtension];
}
/*
* This method is used by the React Native bridge to allow
* our plugin to expose constants to the JS-side. In our case
@@ -273,6 +286,20 @@ static NSString *const PackageIsPendingKey = @"isPending";
});
}
/*
* This returns the modified date as a string for a given file URL.
*/
+ (NSString *)modifiedDateStringOfFileAtURL:(NSURL *)fileURL
{
if (fileURL != nil) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:nil];
NSDate *modifiedDate = [fileAttributes objectForKey:NSFileModificationDate];
return [NSString stringWithFormat:@"%f", [modifiedDate timeIntervalSince1970]];
} else {
return nil;
}
}
/*
* This method is used when an update has failed installation
* and the app needs to be rolled back to the previous bundle.
@@ -367,7 +394,14 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
rejecter:(RCTPromiseRejectBlock)reject)
{
dispatch_async(dispatch_get_main_queue(), ^{
[CodePushPackage downloadPackage:updatePackage
NSDictionary *mutableUpdatePackage = [updatePackage mutableCopy];
NSURL *binaryBundleURL = [CodePush binaryBundleURL];
if (binaryBundleURL != nil) {
[mutableUpdatePackage setValue:[CodePush modifiedDateStringOfFileAtURL:binaryBundleURL]
forKey:BinaryBundleDateKey];
}
[CodePushPackage downloadPackage:mutableUpdatePackage
// The download is progressing forward
progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
// Notify the script-side about the progress
@@ -381,7 +415,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
// The download completed
doneCallback:^{
NSError *err;
NSDictionary *newPackage = [CodePushPackage getPackage:updatePackage[PackageHashKey] error:&err];
NSDictionary *newPackage = [CodePushPackage getPackage:mutableUpdatePackage[PackageHashKey] error:&err];
if (err) {
return reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
@@ -392,7 +426,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
// The download failed
failCallback:^(NSError *err) {
if ([CodePushPackage isCodePushError:err]) {
[self saveFailedUpdate:updatePackage];
[self saveFailedUpdate:mutableUpdatePackage];
}
reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);

View File

@@ -228,8 +228,18 @@ NSString * const UnzippedFolderName = @"unzipped";
NSString * unzippedFolderPath = [CodePushPackage getUnzippedFolderPath];
NSMutableDictionary * mutableUpdatePackage = [updatePackage mutableCopy];
if (isZip) {
NSError *nonFailingError = nil;
if ([[NSFileManager defaultManager] fileExistsAtPath:unzippedFolderPath]) {
// This removes any unzipped download data that could have been left
// uncleared due to a crash or error during the download process.
[[NSFileManager defaultManager] removeItemAtPath:unzippedFolderPath
error:&error];
if (error) {
failCallback(error);
return;
}
}
NSError *nonFailingError = nil;
[SSZipArchive unzipFileAtPath:downloadFilePath
toDestination:unzippedFolderPath];
[[NSFileManager defaultManager] removeItemAtPath:downloadFilePath
@@ -285,6 +295,11 @@ NSString * const UnzippedFolderName = @"unzipped";
[CodePushPackage copyEntriesInFolder:unzippedFolderPath
destFolder:newPackageFolderPath
error:&error];
if (error) {
failCallback(error);
return;
}
[[NSFileManager defaultManager] removeItemAtPath:unzippedFolderPath
error:&nonFailingError];
if (nonFailingError) {

View File

@@ -152,9 +152,11 @@ public class FileUtils {
ZipEntry entry;
File destinationFolder = new File(destination);
if (!destinationFolder.exists()) {
destinationFolder.mkdirs();
if (destinationFolder.exists()) {
deleteDirectory(destinationFolder);
}
destinationFolder.mkdirs();
byte[] buffer = new byte[WRITE_BUFFER_SIZE];
while ((entry = zipStream.getNextEntry()) != null) {