mirror of
https://github.com/zhigang1992/react-native-code-push.git
synced 2026-06-11 08:04:23 +08:00
asset-update
This commit is contained in:
@@ -1,15 +1,32 @@
|
||||
#import "CodePush.h"
|
||||
#import "SSZipArchive.h"
|
||||
|
||||
@implementation CodePushPackage
|
||||
|
||||
NSString * const CodePushErrorDomain = @"CodePushError";
|
||||
const int CodePushErrorCode = -1;
|
||||
NSString * const DiffManifestFileName = @"hotcodepush.json";
|
||||
NSString * const DownloadFileName = @"download.zip";
|
||||
NSString * const RelativeBundlePathKey = @"bundlePath";
|
||||
NSString * const StatusFile = @"codepush.json";
|
||||
NSString * const UpdateBundleFileName = @"app.jsbundle";
|
||||
NSString * const UnzippedFolderName = @"unzipped";
|
||||
|
||||
+ (NSString *)getCodePushPath
|
||||
{
|
||||
return [[CodePush getDocumentsDirectory] stringByAppendingPathComponent:@"CodePush"];
|
||||
}
|
||||
|
||||
+ (NSString *)getDownloadFilePath
|
||||
{
|
||||
return [[self getCodePushPath] stringByAppendingPathComponent:DownloadFileName];
|
||||
}
|
||||
|
||||
+ (NSString *)getUnzippedFolderPath
|
||||
{
|
||||
return [[self getCodePushPath] stringByAppendingPathComponent:UnzippedFolderName];
|
||||
}
|
||||
|
||||
+ (NSString *)getStatusFilePath
|
||||
{
|
||||
return [[self getCodePushPath] stringByAppendingPathComponent:StatusFile];
|
||||
@@ -81,7 +98,18 @@ NSString * const UpdateBundleFileName = @"app.jsbundle";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return [packageFolder stringByAppendingPathComponent:UpdateBundleFileName];
|
||||
NSDictionary *currentPackage = [self getCurrentPackage:error];
|
||||
|
||||
if(*error) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSString *relativeBundlePath = [currentPackage objectForKey:RelativeBundlePathKey];
|
||||
if (relativeBundlePath) {
|
||||
return [packageFolder stringByAppendingPathComponent:relativeBundlePath];
|
||||
} else {
|
||||
return [packageFolder stringByAppendingPathComponent:UpdateBundleFileName];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)getCurrentPackageHash:(NSError **)error
|
||||
@@ -161,15 +189,15 @@ NSString * const UpdateBundleFileName = @"app.jsbundle";
|
||||
}
|
||||
|
||||
+ (void)downloadPackage:(NSDictionary *)updatePackage
|
||||
progressCallback:(void (^)(long, long))progressCallback
|
||||
progressCallback:(void (^)(long long, long long))progressCallback
|
||||
doneCallback:(void (^)())doneCallback
|
||||
failCallback:(void (^)(NSError *err))failCallback
|
||||
{
|
||||
NSString *packageFolderPath = [self getPackageFolderPath:updatePackage[@"packageHash"]];
|
||||
NSString *newPackageFolderPath = [self getPackageFolderPath:updatePackage[@"packageHash"]];
|
||||
NSError *error = nil;
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:packageFolderPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:packageFolderPath
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:newPackageFolderPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:newPackageFolderPath
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&error];
|
||||
@@ -179,21 +207,122 @@ NSString * const UpdateBundleFileName = @"app.jsbundle";
|
||||
return failCallback(error);
|
||||
}
|
||||
|
||||
NSString *downloadFilePath = [packageFolderPath stringByAppendingPathComponent:UpdateBundleFileName];
|
||||
NSString * downloadFilePath = [self getDownloadFilePath];
|
||||
NSString * bundleFilePath = [newPackageFolderPath stringByAppendingPathComponent:UpdateBundleFileName];
|
||||
|
||||
CodePushDownloadHandler *downloadHandler = [[CodePushDownloadHandler alloc]
|
||||
init:downloadFilePath
|
||||
progressCallback:progressCallback
|
||||
doneCallback:^{
|
||||
NSError *error;
|
||||
doneCallback:^(BOOL isZip) {
|
||||
NSError *error = nil;
|
||||
NSString * unzippedFolderPath = [CodePushPackage getUnzippedFolderPath];
|
||||
NSMutableDictionary * mutableUpdatePackage = [updatePackage mutableCopy];
|
||||
if (isZip) {
|
||||
NSError *nonFailingError = nil;
|
||||
|
||||
[SSZipArchive unzipFileAtPath:downloadFilePath
|
||||
toDestination:unzippedFolderPath];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:downloadFilePath
|
||||
error:&nonFailingError];
|
||||
if (nonFailingError) {
|
||||
NSLog(@"Error deleting downloaded file: %@", nonFailingError);
|
||||
nonFailingError = nil;
|
||||
}
|
||||
|
||||
NSString * diffManifestFilePath = [unzippedFolderPath
|
||||
stringByAppendingPathComponent:DiffManifestFileName];
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:diffManifestFilePath]) {
|
||||
// Copy the current package to the new package.
|
||||
NSString* currentPackageFolderPath = [self getCurrentPackageFolderPath:&error];
|
||||
if (error) {
|
||||
failCallback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
[CodePushPackage copyEntriesInFolder:currentPackageFolderPath
|
||||
destFolder:newPackageFolderPath
|
||||
error:&error];
|
||||
if (error) {
|
||||
failCallback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete files mentioned in the manifest.
|
||||
NSString *manifestContent = [NSString stringWithContentsOfFile:diffManifestFilePath
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&error];
|
||||
if (error) {
|
||||
failCallback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *data = [manifestContent dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary* manifestJSON = [NSJSONSerialization JSONObjectWithData:data
|
||||
options:kNilOptions
|
||||
error:&error];
|
||||
NSArray *deletedFiles = manifestJSON[@"deletedFiles"];
|
||||
for (NSString *deletedFileName in deletedFiles) {
|
||||
[[NSFileManager defaultManager]
|
||||
removeItemAtPath:[newPackageFolderPath
|
||||
stringByAppendingPathComponent:deletedFileName]
|
||||
error:&nonFailingError];
|
||||
|
||||
if (nonFailingError) {
|
||||
NSLog(@"Error deleting file from current package: %@", nonFailingError);
|
||||
nonFailingError = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CodePushPackage copyEntriesInFolder:unzippedFolderPath
|
||||
destFolder:newPackageFolderPath
|
||||
error:&error];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:unzippedFolderPath
|
||||
error:&nonFailingError];
|
||||
if (nonFailingError) {
|
||||
NSLog(@"Error deleting downloaded file: %@", nonFailingError);
|
||||
nonFailingError = nil;
|
||||
}
|
||||
|
||||
NSString *relativeBundlePath = [self findMainBundleInFolder:newPackageFolderPath
|
||||
error:&error];
|
||||
if (error) {
|
||||
failCallback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (relativeBundlePath) {
|
||||
[mutableUpdatePackage setValue:relativeBundlePath forKey:RelativeBundlePathKey];
|
||||
} else {
|
||||
error = [[NSError alloc] initWithDomain:CodePushErrorDomain
|
||||
code:CodePushErrorCode
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey:
|
||||
NSLocalizedString(@"Update is invalid - no files with extension .jsbundle or .bundle were found in the update package.", nil)
|
||||
}];
|
||||
failCallback(error);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
[[NSFileManager defaultManager] moveItemAtPath:downloadFilePath
|
||||
toPath:bundleFilePath
|
||||
error:&error];
|
||||
if (error) {
|
||||
failCallback(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSData *updateSerializedData = [NSJSONSerialization
|
||||
dataWithJSONObject:updatePackage
|
||||
options:0
|
||||
error:&error];
|
||||
dataWithJSONObject:mutableUpdatePackage
|
||||
options:0
|
||||
error:&error];
|
||||
NSString *packageJsonString = [[NSString alloc]
|
||||
initWithData:updateSerializedData encoding:NSUTF8StringEncoding];
|
||||
|
||||
[packageJsonString writeToFile:[packageFolderPath stringByAppendingPathComponent:@"app.json"]
|
||||
initWithData:updateSerializedData
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
[packageJsonString writeToFile:[newPackageFolderPath stringByAppendingPathComponent:@"app.json"]
|
||||
atomically:YES
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&error];
|
||||
@@ -203,15 +332,96 @@ NSString * const UpdateBundleFileName = @"app.jsbundle";
|
||||
doneCallback();
|
||||
}
|
||||
}
|
||||
|
||||
failCallback:failCallback];
|
||||
|
||||
[downloadHandler download:updatePackage[@"downloadUrl"]];
|
||||
}
|
||||
|
||||
+ (NSString *)findMainBundleInFolder:(NSString *)folderPath
|
||||
error:(NSError **)error
|
||||
{
|
||||
NSArray* folderFiles = [[NSFileManager defaultManager]
|
||||
contentsOfDirectoryAtPath:folderPath
|
||||
error:error];
|
||||
if (*error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (NSString *fileName in folderFiles) {
|
||||
NSString *fullFilePath = [folderPath stringByAppendingPathComponent:fileName];
|
||||
BOOL isDir = NO;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:fullFilePath
|
||||
isDirectory:&isDir] && isDir) {
|
||||
NSString *mainBundlePathInFolder = [self findMainBundleInFolder:fullFilePath error:error];
|
||||
if (*error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (mainBundlePathInFolder) {
|
||||
return [fileName stringByAppendingPathComponent:mainBundlePathInFolder];
|
||||
}
|
||||
} else if ([[fileName pathExtension] isEqualToString:@"bundle"] ||
|
||||
[[fileName pathExtension] isEqualToString:@"jsbundle"]) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
+ (void)copyEntriesInFolder:(NSString *)sourceFolder
|
||||
destFolder:(NSString *)destFolder
|
||||
error:(NSError **)error
|
||||
|
||||
{
|
||||
NSArray* files = [[NSFileManager defaultManager]
|
||||
contentsOfDirectoryAtPath:sourceFolder
|
||||
error:error];
|
||||
if (*error) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSString *fileName in files) {
|
||||
NSString * fullFilePath = [sourceFolder stringByAppendingPathComponent:fileName];
|
||||
BOOL isDir = NO;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:fullFilePath
|
||||
isDirectory:&isDir] && isDir) {
|
||||
NSString *nestedDestFolder = [destFolder stringByAppendingPathComponent:fileName];
|
||||
[self copyEntriesInFolder:fullFilePath
|
||||
destFolder:nestedDestFolder
|
||||
error:error];
|
||||
} else {
|
||||
NSString *destFileName = [destFolder stringByAppendingPathComponent:fileName];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:destFileName]) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:destFileName error:error];
|
||||
if (*error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:destFolder]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:destFolder
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:error];
|
||||
if (*error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[NSFileManager defaultManager] moveItemAtPath:fullFilePath toPath:destFileName error:error];
|
||||
if (*error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)installPackage:(NSDictionary *)updatePackage
|
||||
error:(NSError **)error
|
||||
{
|
||||
NSString *packageHash = updatePackage[@"packageHash" ];
|
||||
NSString *packageHash = updatePackage[@"packageHash"];
|
||||
NSMutableDictionary *info = [self getCurrentPackageInfo:error];
|
||||
|
||||
if (*error) {
|
||||
@@ -223,7 +433,8 @@ NSString * const UpdateBundleFileName = @"app.jsbundle";
|
||||
NSString *previousPackageFolderPath = [self getPackageFolderPath:previousPackageHash];
|
||||
// Error in deleting old package will not cause the entire operation to fail.
|
||||
NSError *deleteError;
|
||||
[[NSFileManager defaultManager] removeItemAtPath:previousPackageFolderPath error:&deleteError];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:previousPackageFolderPath
|
||||
error:&deleteError];
|
||||
if (deleteError) {
|
||||
NSLog(@"Error deleting old package: %@", deleteError);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user