Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit 21a4394

Browse files
committed
Merge pull request #186 from Microsoft/bundle-date-ios
Attach binary bundle date to package metadata on download
2 parents e70516b + 038112e commit 21a4394

File tree

3 files changed

+75
-24
lines changed

3 files changed

+75
-24
lines changed

CodePush.m

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ @implementation CodePush {
1515

1616
#pragma mark - Private constants
1717

18-
static BOOL needToReportRollback = NO;
19-
static BOOL isRunningBinaryVersion = NO;
20-
static BOOL testConfigurationFlag = NO;
21-
2218
// These constants represent valid deployment statuses
2319
static NSString *const DeploymentFailed = @"DeploymentFailed";
2420
static NSString *const DeploymentSucceeded = @"DeploymentSucceeded";
@@ -34,52 +30,64 @@ @implementation CodePush {
3430

3531
// These keys are used to inspect/augment the metadata
3632
// that is associated with an update's package.
33+
static NSString *const BinaryBundleDateKey = @"binaryDate";
3734
static NSString *const PackageHashKey = @"packageHash";
3835
static NSString *const PackageIsPendingKey = @"isPending";
3936

37+
#pragma mark - Static variables
38+
39+
static BOOL isRunningBinaryVersion = NO;
40+
static BOOL needToReportRollback = NO;
41+
static BOOL testConfigurationFlag = NO;
42+
43+
// These values are used to save the bundleURL and extension for the JS bundle
44+
// in the binary.
45+
static NSString *bundleResourceExtension = @"jsbundle";
46+
static NSString *bundleResourceName = @"main";
47+
4048
#pragma mark - Public Obj-C API
4149

4250
+ (NSURL *)bundleURL
4351
{
44-
return [self bundleURLForResource:@"main"];
52+
return [self bundleURLForResource:bundleResourceName];
4553
}
4654

4755
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
4856
{
57+
bundleResourceName = resourceName;
4958
return [self bundleURLForResource:resourceName
50-
withExtension:@"jsbundle"];
59+
withExtension:bundleResourceExtension];
5160
}
5261

5362
+ (NSURL *)bundleURLForResource:(NSString *)resourceName
5463
withExtension:(NSString *)resourceExtension
5564
{
65+
bundleResourceName = resourceName;
66+
bundleResourceExtension = resourceExtension;
5667
NSError *error;
5768
NSString *packageFile = [CodePushPackage getCurrentPackageBundlePath:&error];
58-
NSURL *binaryJsBundleUrl = [[NSBundle mainBundle] URLForResource:resourceName withExtension:resourceExtension];
69+
NSURL *binaryBundleURL = [self binaryBundleURL];
5970

6071
NSString *logMessageFormat = @"Loading JS bundle from %@";
6172

6273
if (error || !packageFile) {
63-
NSLog(logMessageFormat, binaryJsBundleUrl);
74+
NSLog(logMessageFormat, binaryBundleURL);
6475
isRunningBinaryVersion = YES;
65-
return binaryJsBundleUrl;
76+
return binaryBundleURL;
6677
}
6778

68-
NSDictionary *binaryFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[binaryJsBundleUrl path] error:nil];
69-
NSDictionary *appFileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:packageFile error:nil];
70-
NSDate *binaryDate = [binaryFileAttributes objectForKey:NSFileModificationDate];
71-
NSDate *packageDate = [appFileAttribs objectForKey:NSFileModificationDate];
7279
NSString *binaryAppVersion = [[CodePushConfig current] appVersion];
7380
NSDictionary *currentPackageMetadata = [CodePushPackage getCurrentPackage:&error];
7481
if (error || !currentPackageMetadata) {
75-
NSLog(logMessageFormat, binaryJsBundleUrl);
82+
NSLog(logMessageFormat, binaryBundleURL);
7683
isRunningBinaryVersion = YES;
77-
return binaryJsBundleUrl;
84+
return binaryBundleURL;
7885
}
7986

87+
NSString *packageDate = [currentPackageMetadata objectForKey:BinaryBundleDateKey];
8088
NSString *packageAppVersion = [currentPackageMetadata objectForKey:@"appVersion"];
8189

82-
if ([binaryDate compare:packageDate] == NSOrderedAscending && ([CodePush isUsingTestConfiguration] ||[binaryAppVersion isEqualToString:packageAppVersion])) {
90+
if ([[self modifiedDateStringOfFileAtURL:binaryBundleURL] isEqualToString:packageDate] && ([CodePush isUsingTestConfiguration] ||[binaryAppVersion isEqualToString:packageAppVersion])) {
8391
// Return package file because it is newer than the app store binary's JS bundle
8492
NSURL *packageUrl = [[NSURL alloc] initFileURLWithPath:packageFile];
8593
NSLog(logMessageFormat, packageUrl);
@@ -90,9 +98,9 @@ + (NSURL *)bundleURLForResource:(NSString *)resourceName
9098
[CodePush clearUpdates];
9199
#endif
92100

93-
NSLog(logMessageFormat, binaryJsBundleUrl);
101+
NSLog(logMessageFormat, binaryBundleURL);
94102
isRunningBinaryVersion = YES;
95-
return binaryJsBundleUrl;
103+
return binaryBundleURL;
96104
}
97105
}
98106

@@ -141,6 +149,11 @@ + (void)clearUpdates
141149

142150
@synthesize bridge = _bridge;
143151

152+
+ (NSURL *)binaryBundleURL
153+
{
154+
return [[NSBundle mainBundle] URLForResource:bundleResourceName withExtension:bundleResourceExtension];
155+
}
156+
144157
/*
145158
* This method is used by the React Native bridge to allow
146159
* our plugin to expose constants to the JS-side. In our case
@@ -273,6 +286,20 @@ - (void)loadBundle
273286
});
274287
}
275288

289+
/*
290+
* This returns the modified date as a string for a given file URL.
291+
*/
292+
+ (NSString *)modifiedDateStringOfFileAtURL:(NSURL *)fileURL
293+
{
294+
if (fileURL != nil) {
295+
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:nil];
296+
NSDate *modifiedDate = [fileAttributes objectForKey:NSFileModificationDate];
297+
return [NSString stringWithFormat:@"%f", [modifiedDate timeIntervalSince1970]];
298+
} else {
299+
return nil;
300+
}
301+
}
302+
276303
/*
277304
* This method is used when an update has failed installation
278305
* and the app needs to be rolled back to the previous bundle.
@@ -367,7 +394,14 @@ - (void)savePendingUpdate:(NSString *)packageHash
367394
rejecter:(RCTPromiseRejectBlock)reject)
368395
{
369396
dispatch_async(dispatch_get_main_queue(), ^{
370-
[CodePushPackage downloadPackage:updatePackage
397+
NSDictionary *mutableUpdatePackage = [updatePackage mutableCopy];
398+
NSURL *binaryBundleURL = [CodePush binaryBundleURL];
399+
if (binaryBundleURL != nil) {
400+
[mutableUpdatePackage setValue:[CodePush modifiedDateStringOfFileAtURL:binaryBundleURL]
401+
forKey:BinaryBundleDateKey];
402+
}
403+
404+
[CodePushPackage downloadPackage:mutableUpdatePackage
371405
// The download is progressing forward
372406
progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
373407
// Notify the script-side about the progress
@@ -381,7 +415,7 @@ - (void)savePendingUpdate:(NSString *)packageHash
381415
// The download completed
382416
doneCallback:^{
383417
NSError *err;
384-
NSDictionary *newPackage = [CodePushPackage getPackage:updatePackage[PackageHashKey] error:&err];
418+
NSDictionary *newPackage = [CodePushPackage getPackage:mutableUpdatePackage[PackageHashKey] error:&err];
385419

386420
if (err) {
387421
return reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);
@@ -392,7 +426,7 @@ - (void)savePendingUpdate:(NSString *)packageHash
392426
// The download failed
393427
failCallback:^(NSError *err) {
394428
if ([CodePushPackage isCodePushError:err]) {
395-
[self saveFailedUpdate:updatePackage];
429+
[self saveFailedUpdate:mutableUpdatePackage];
396430
}
397431

398432
reject([NSString stringWithFormat: @"%lu", (long)err.code], err.localizedDescription, err);

CodePushPackage.m

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,18 @@ + (void)downloadPackage:(NSDictionary *)updatePackage
228228
NSString * unzippedFolderPath = [CodePushPackage getUnzippedFolderPath];
229229
NSMutableDictionary * mutableUpdatePackage = [updatePackage mutableCopy];
230230
if (isZip) {
231-
NSError *nonFailingError = nil;
231+
if ([[NSFileManager defaultManager] fileExistsAtPath:unzippedFolderPath]) {
232+
// This removes any unzipped download data that could have been left
233+
// uncleared due to a crash or error during the download process.
234+
[[NSFileManager defaultManager] removeItemAtPath:unzippedFolderPath
235+
error:&error];
236+
if (error) {
237+
failCallback(error);
238+
return;
239+
}
240+
}
232241

242+
NSError *nonFailingError = nil;
233243
[SSZipArchive unzipFileAtPath:downloadFilePath
234244
toDestination:unzippedFolderPath];
235245
[[NSFileManager defaultManager] removeItemAtPath:downloadFilePath
@@ -285,6 +295,11 @@ + (void)downloadPackage:(NSDictionary *)updatePackage
285295
[CodePushPackage copyEntriesInFolder:unzippedFolderPath
286296
destFolder:newPackageFolderPath
287297
error:&error];
298+
if (error) {
299+
failCallback(error);
300+
return;
301+
}
302+
288303
[[NSFileManager defaultManager] removeItemAtPath:unzippedFolderPath
289304
error:&nonFailingError];
290305
if (nonFailingError) {

android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,11 @@ public static void unzipFile(File zipFile, String destination) throws IOExceptio
152152
ZipEntry entry;
153153

154154
File destinationFolder = new File(destination);
155-
if (!destinationFolder.exists()) {
156-
destinationFolder.mkdirs();
155+
if (destinationFolder.exists()) {
156+
deleteDirectory(destinationFolder);
157157
}
158+
159+
destinationFolder.mkdirs();
158160

159161
byte[] buffer = new byte[WRITE_BUFFER_SIZE];
160162
while ((entry = zipStream.getNextEntry()) != null) {

0 commit comments

Comments
 (0)