From 4fe4629a9e4eaa4b18cb78d6258daf767dfa40af Mon Sep 17 00:00:00 2001 From: david Date: Mon, 9 Nov 2015 20:35:53 -0500 Subject: [PATCH] Refactored to use centralized downloader. Implemented using NSURLSession which can be configured with headers. (In a future PR). Removed now unused network related files. --- .gitignore | 1 + plugin.xml | 15 -- src/ios/HCPPlugin.m | 2 - .../Network/HCPApplicationConfig+Downloader.h | 42 ----- .../Network/HCPApplicationConfig+Downloader.m | 35 ---- .../Network/HCPContentManifest+Downloader.h | 42 ----- .../Network/HCPContentManifest+Downloader.m | 35 ---- src/ios/Network/HCPFileDownloader.h | 39 +---- src/ios/Network/HCPFileDownloader.m | 127 +++++++-------- src/ios/Network/HCPJsonDownloader.h | 53 ------- src/ios/Network/HCPJsonDownloader.m | 53 ------- src/ios/Updater/HCPUpdateLoader.h | 6 - src/ios/Updater/HCPUpdateLoader.m | 24 +-- src/ios/Updater/HCPUpdateLoaderWorker.m | 149 ++++++++++-------- 14 files changed, 150 insertions(+), 473 deletions(-) create mode 100644 .gitignore delete mode 100644 src/ios/Network/HCPApplicationConfig+Downloader.h delete mode 100644 src/ios/Network/HCPApplicationConfig+Downloader.m delete mode 100644 src/ios/Network/HCPContentManifest+Downloader.h delete mode 100644 src/ios/Network/HCPContentManifest+Downloader.m delete mode 100644 src/ios/Network/HCPJsonDownloader.h delete mode 100644 src/ios/Network/HCPJsonDownloader.m diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e43b0f98 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/plugin.xml b/plugin.xml index 3dcd619e..91c53a9a 100644 --- a/plugin.xml +++ b/plugin.xml @@ -120,26 +120,11 @@ target-dir="Model/" /> - - - - - - - - - diff --git a/src/ios/HCPPlugin.m b/src/ios/HCPPlugin.m index e460c6b5..f6db2cb6 100644 --- a/src/ios/HCPPlugin.m +++ b/src/ios/HCPPlugin.m @@ -7,8 +7,6 @@ #import #import "HCPPlugin.h" -#import "HCPApplicationConfig+Downloader.h" -#import "HCPContentManifest+Downloader.h" #import "HCPFileDownloader.h" #import "HCPFilesStructure.h" #import "HCPFilesStructureImpl.h" diff --git a/src/ios/Network/HCPApplicationConfig+Downloader.h b/src/ios/Network/HCPApplicationConfig+Downloader.h deleted file mode 100644 index 1f1ad056..00000000 --- a/src/ios/Network/HCPApplicationConfig+Downloader.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// HCPApplicationConfig+Downloader.h -// -// Created by Nikolay Demyankov on 10.08.15. -// - -#import "HCPApplicationConfig.h" -#import "HCPApplicationConfig.h" - -/** - * Application config download complition block. - * - * @param error holds information about occured error; nil if no error happened. - * @param config loaded application config - */ -typedef void (^HCPApplicationConfigDownloadComplitionBlock)(NSError *error, HCPApplicationConfig *config); - -/** - * Category for HCPApplicationConfig. - * Adds methods to download application config from the server. - */ -@interface HCPApplicationConfig (Downloader) - -/** - * Download application config asynchronously from the certain url. - * - * @param url url from which application config is loaded - * @param block download complition block - */ -+ (void)downloadFromURL:(NSURL *)url withComplitionBlock:(HCPApplicationConfigDownloadComplitionBlock)block; - -/** - * Download application config synchronously from the certain url. - * - * @param url url from which application config is loaded - * @param error holds information about occured error; nil if no error happened. - * - * @return loaded application config - */ -+ (HCPApplicationConfig *)downloadSyncFromURL:(NSURL *)url error:(NSError **)error; - -@end diff --git a/src/ios/Network/HCPApplicationConfig+Downloader.m b/src/ios/Network/HCPApplicationConfig+Downloader.m deleted file mode 100644 index b4fc2455..00000000 --- a/src/ios/Network/HCPApplicationConfig+Downloader.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// HCPApplicationConfig+Downloader.m -// -// Created by Nikolay Demyankov on 10.08.15. -// - -#import "HCPApplicationConfig+Downloader.h" -#import "HCPJsonDownloader.h" - -@implementation HCPApplicationConfig (Downloader) - -+ (void)downloadFromURL:(NSURL *)url withComplitionBlock:(HCPApplicationConfigDownloadComplitionBlock)block { - if (block == nil) { - return; - } - - HCPJsonDownloader *jsonDownloader = [[HCPJsonDownloader alloc] initWithUrl:url]; - [jsonDownloader downloadWithComplitionBlock:^(NSError *error, id json) { - HCPApplicationConfig *config = nil; - if (error == nil) { - config = [HCPApplicationConfig instanceFromJsonObject:json]; - } - - block(error, config); - }]; -} - -+ (HCPApplicationConfig *)downloadSyncFromURL:(NSURL *)url error:(NSError **)error { - HCPJsonDownloader *jsonDownloader = [[HCPJsonDownloader alloc] initWithUrl:url]; - id json = [jsonDownloader downloadSync:error]; - - return [HCPApplicationConfig instanceFromJsonObject:json]; -} - -@end diff --git a/src/ios/Network/HCPContentManifest+Downloader.h b/src/ios/Network/HCPContentManifest+Downloader.h deleted file mode 100644 index 5edcaf61..00000000 --- a/src/ios/Network/HCPContentManifest+Downloader.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// HCPContentManifest+Downloader.h -// -// Created by Nikolay Demyankov on 10.08.15. -// - -#import "HCPContentManifest.h" - -/** - * Complition block for manifest download process. - * - * @param error holds information about the occured error; nil if everything is fine - * @param manifest loaded manifest file - * @see HCPContentManifest - */ -typedef void (^HCPContentManifestDownloadComplitionBlock)(NSError *error, HCPContentManifest *manifest); - -/** - * Category for HCPContentManifest. - * Adds methods to download manifest from server. - */ -@interface HCPContentManifest (Downloader) - -/** - * Download manifest asynchronously from the certain url. - * - * @param url url from which manifest should be loaded - * @param block download complition block - */ -+ (void)downloadFromURL:(NSURL *)url withComplitionBlock:(HCPContentManifestDownloadComplitionBlock)block; - -/** - * Download manifest file synchronously. - * - * @param url url from which manifest file is loaded - * @param error holds information about occured error; nil on success - * - * @return loaded manifest instance; nil on error - */ -+ (HCPContentManifest *)downloadSyncFromURL:(NSURL *)url error:(NSError **)error; - -@end diff --git a/src/ios/Network/HCPContentManifest+Downloader.m b/src/ios/Network/HCPContentManifest+Downloader.m deleted file mode 100644 index 353ba7ae..00000000 --- a/src/ios/Network/HCPContentManifest+Downloader.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// HCPContentManifest+Downloader.m -// -// Created by Nikolay Demyankov on 10.08.15. -// - -#import "HCPContentManifest+Downloader.h" -#import "HCPJsonDownloader.h" - -@implementation HCPContentManifest (Downloader) - -+ (void)downloadFromURL:(NSURL *)url withComplitionBlock:(HCPContentManifestDownloadComplitionBlock)block { - if (block == nil) { - return; - } - - HCPJsonDownloader *jsonDownloader = [[HCPJsonDownloader alloc] initWithUrl:url]; - [jsonDownloader downloadWithComplitionBlock:^(NSError *error, id json) { - HCPContentManifest *manifest = nil; - if (error == nil) { - manifest = [HCPContentManifest instanceFromJsonObject:json]; - } - - block(error, manifest); - }]; -} - -+ (HCPContentManifest *)downloadSyncFromURL:(NSURL *)url error:(NSError **)error { - HCPJsonDownloader *jsonDownloader = [[HCPJsonDownloader alloc] initWithUrl:url]; - id json = [jsonDownloader downloadSync:error]; - - return [HCPContentManifest instanceFromJsonObject:json]; -} - -@end diff --git a/src/ios/Network/HCPFileDownloader.h b/src/ios/Network/HCPFileDownloader.h index 49cd58f5..610dbf5c 100644 --- a/src/ios/Network/HCPFileDownloader.h +++ b/src/ios/Network/HCPFileDownloader.h @@ -11,7 +11,8 @@ * * @param holds information about occured error; nil if everything is fine */ -typedef void (^HCPFileDownloadComplitionBlock)(NSError *error); +typedef void (^HCPFileDownloadCompletionBlock)(NSError *); +typedef void (^HCPDataDownloadCompletionBlock)(NSData*, NSError *); /** * Helper class to download files from the server. @@ -19,14 +20,12 @@ typedef void (^HCPFileDownloadComplitionBlock)(NSError *error); @interface HCPFileDownloader : NSObject /** - * Download file asynchronously. + * Download data asynchronously. * * @param url url to the downloaded file - * @param filePath url in local file system where to put loaded file - * @param checksum hash of the file to check if it's not corrupted - * @param block download complition block + * @param block data download completion block, called with the data when it is available. */ -- (void)downloadFileFromUrl:(NSURL *)url saveToFile:(NSURL *)filePath checksum:(NSString *)checksum complitionBlock:(HCPFileDownloadComplitionBlock)block; +- (void) downloadDataFromUrl:(NSURL*) url completionBlock:(HCPDataDownloadCompletionBlock) block; /** * Download list of files asynchronously. @@ -34,34 +33,10 @@ typedef void (^HCPFileDownloadComplitionBlock)(NSError *error); * @param filesList list of files to download. Files are instances of HCPManifestFile class. * @param contentURL url on the server where all files are located. Full URL to the file is constructed from this one and the files mame. * @param folderURL url to the directory in local file system where to put all loaded files - * @param block download complition block + * @param block download completion block * * @see HCPManifestFile */ -- (void)downloadFiles:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL complitionBlock:(HCPFileDownloadComplitionBlock)block; - -/** - * Download list of files synchronously. - * - * @param filesList list of files to download. Files are instances of HCPManifestFile class. - * @param contentURL url on the server where all files are located. Full URL to the file is constructed from this one and the files mame. - * @param folderURL url to the directory in local file system where to put all loaded files - * @param error holds information about occured error; nil if everything is fine - * @return YES when all files are loaded; NO on download error - * @see HCPManifestFile - */ -- (BOOL)downloadFilesSync:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL error:(NSError **)error; - -/** - * Download file synchronously. - * - * @param url url to the downloaded file - * @param filePath url in local file system where to put loaded file - * @param checksum hash of the file to check if it's not corrupted - * @param error holds information about occured error; nil if everything is fine - * - * @return YES when file is loaded; NO on download error - */ -- (BOOL)downloadFileSyncFromUrl:(NSURL *)url saveToFile:(NSURL *)filePath checksum:(NSString *)checksum error:(NSError **)error; +- (void) downloadFiles:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL completionBlock:(HCPFileDownloadCompletionBlock)block; @end diff --git a/src/ios/Network/HCPFileDownloader.m b/src/ios/Network/HCPFileDownloader.m index 24801d46..c0d8b95e 100644 --- a/src/ios/Network/HCPFileDownloader.m +++ b/src/ios/Network/HCPFileDownloader.m @@ -13,83 +13,74 @@ @implementation HCPFileDownloader #pragma mark Public API -- (void)downloadFileFromUrl:(NSURL *)url saveToFile:(NSURL *)filePath checksum:(NSString *)checksum complitionBlock:(HCPFileDownloadComplitionBlock)block { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error = nil; - [self executeFileDownloadFromURL:url saveToFile:filePath checksum:checksum error:&error]; - block(error); - }); -} - -- (void)downloadFiles:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL complitionBlock:(HCPFileDownloadComplitionBlock)block { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error = nil; - [self executeDownloadOfFiles:filesList fromURL:contentURL toFolder:folderURL error:&error]; - block(error); - }); -} -- (BOOL)downloadFilesSync:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL error:(NSError **)error { - [self executeDownloadOfFiles:filesList fromURL:contentURL toFolder:folderURL error:error]; +- (void) downloadDataFromUrl:(NSURL*) url completionBlock:(HCPDataDownloadCompletionBlock) block { - return (*error == nil); -} - -- (BOOL)downloadFileSyncFromUrl:(NSURL *)url saveToFile:(NSURL *)filePath checksum:(NSString *)checksum error:(NSError **)error { - [self executeFileDownloadFromURL:url saveToFile:filePath checksum:checksum error:error]; + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; + + NSURLSessionDataTask* dowloadTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + block(data, error); + }]; - return (*error == nil); + [dowloadTask resume]; } -#pragma mark Private API - -/** - * Perform download of the list of files - * - * @param filesList list of files to download - * @param contentURL base url for all the loaded files - * @param folderURL where to put loaded files on the file system - * @param error error information if any occure; nil if all files are loaded - */ -- (void)executeDownloadOfFiles:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL error:(NSError **)error { - for (HCPManifestFile *file in filesList) { - NSURL *filePathOnFileSystem = [folderURL URLByAppendingPathComponent:file.name isDirectory:NO]; - NSURL *fileUrlOnServer = [contentURL URLByAppendingPathComponent:file.name isDirectory:NO]; - BOOL isDownloaded = [self executeFileDownloadFromURL:fileUrlOnServer saveToFile:filePathOnFileSystem checksum:file.md5Hash error:error]; - if (!isDownloaded) { - break; - } +- (void) downloadFiles:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL completionBlock:(HCPFileDownloadCompletionBlock)block { + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; + + __block NSMutableSet* startedTasks = [NSMutableSet set]; + __block BOOL canceled = NO; + for (HCPManifestFile *file in filesList) + { + NSURL *url = [contentURL URLByAppendingPathComponent:file.name]; + __block NSURLSessionDataTask *downloadTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + NSError* operationError = nil; + if (error) { + operationError = error; + } + + if (!error) { + if (![self isDataCorrupted:data checksum:file.md5Hash error:&error]) { + NSURL *finalPath = [folderURL URLByAppendingPathComponent:file.name]; + [self prepareFileForSaving:finalPath]; + + BOOL success = [data writeToURL:finalPath options:kNilOptions error:&error]; + if (success) { + NSLog(@"Loaded file %@", file.name); + [startedTasks removeObject:downloadTask]; + } else { + operationError = error; + } + } else { + NSString *message = [NSString stringWithFormat:@"Failed to load file: %@", url]; + operationError = [NSError errorWithCode:0 description:message]; + } + } + + if (operationError) { + [session invalidateAndCancel]; + [startedTasks removeAllObjects]; + } + // operations finishes + if (!canceled && (startedTasks.count == 0 || operationError)) { + if (operationError) { + canceled = YES; // do not dispatch any other error + } + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + block(operationError); + }); + } + + }]; - NSLog(@"Loaded file %@", file.name); + [startedTasks addObject:downloadTask]; + [downloadTask resume]; } } -/** - * Perform download of the file from the provided url - * - * @param url url from which to downlaod the file - * @param fileURL where to save file on the external storage - * @param checksum file checksum to validate it after the download - * @param error error information if any occure; nil on download success - * - * @return YES if file is downloaded; NO if we failed to download - */ -- (BOOL)executeFileDownloadFromURL:(NSURL *)url saveToFile:(NSURL *)fileURL checksum:(NSString *)checksum error:(NSError **)error { - *error = nil; - NSData *downloadedContent = [NSData dataWithContentsOfURL:url]; - if (downloadedContent == nil) { - NSString *message = [NSString stringWithFormat:@"Failed to load file: %@", url]; - *error = [NSError errorWithCode:0 description:message]; - return NO; - } - - if (![self isDataCorrupted:downloadedContent checksum:checksum error:error]) { - [self prepareFileForSaving:fileURL]; - [downloadedContent writeToURL:fileURL options:kNilOptions error:error]; - } - - return (*error == nil); -} + /** * Check if data was corrupted during the download. diff --git a/src/ios/Network/HCPJsonDownloader.h b/src/ios/Network/HCPJsonDownloader.h deleted file mode 100644 index 09eef104..00000000 --- a/src/ios/Network/HCPJsonDownloader.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// HCPJsonDownloader.h -// -// Created by Nikolay Demyankov on 11.08.15. -// - -#import - -/** - * Complition block for JSON download process. - * - * @param error object is not nil if some error has happened during the download. - * @param json loaded json object - */ -typedef void (^HCPJsonDownloadComplitionBlock)(NSError *error, id json); - -/** - * Helper class to download JSON and convert it into appropriate object. - */ -@interface HCPJsonDownloader : NSObject - -/** - * URL from which we will download JSON. - */ -@property (nonatomic, strong, readonly) NSURL *url; - -/** - * Initialize object. - * - * @param url URL from which we should download JSON - * - * @return instance of the object - */ -- (instancetype)initWithUrl:(NSURL *)url; - -/** - * Perform download and call the provided block when finished. - * Download performed asynchronously. - * - * @param block complition block - */ -- (void)downloadWithComplitionBlock:(HCPJsonDownloadComplitionBlock)block; - -/** - * Download JSON synchronously. - * - * @param error object will hold error information if any occur - * - * @return loaded JSON object - */ -- (id)downloadSync:(NSError **)error; - -@end diff --git a/src/ios/Network/HCPJsonDownloader.m b/src/ios/Network/HCPJsonDownloader.m deleted file mode 100644 index a6fed837..00000000 --- a/src/ios/Network/HCPJsonDownloader.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// HCPJsonDownloader.m -// -// Created by Nikolay Demyankov on 11.08.15. -// - -#import "HCPJsonDownloader.h" -#import "NSError+HCPExtension.h" - -@implementation HCPJsonDownloader - -- (instancetype)initWithUrl:(NSURL *)url { - self = [super init]; - if (self) { - _url = url; - } - - return self; -} - -- (void)downloadWithComplitionBlock:(HCPJsonDownloadComplitionBlock)block { - if (block == nil) { - return; - } - - NSURLRequest *request = [NSURLRequest requestWithURL:self.url]; - [NSURLConnection sendAsynchronousRequest:request - queue:[NSOperationQueue currentQueue] - completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { - if (connectionError) { - block(connectionError, nil); - return; - } - - NSError *jsonError = nil; - id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError]; - block(jsonError, jsonObject); - }]; -} - -- (id)downloadSync:(NSError **)error { - *error = nil; - NSData *data = [NSData dataWithContentsOfURL:self.url]; - if (data == nil) { - NSString *message = [NSString stringWithFormat:@"Failed to download config file from: %@", self.url]; - *error = [NSError errorWithCode:0 description:message]; - return nil; - } - - return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:error]; -} - -@end diff --git a/src/ios/Updater/HCPUpdateLoader.h b/src/ios/Updater/HCPUpdateLoader.h index 93f244cc..7f1cec7c 100644 --- a/src/ios/Updater/HCPUpdateLoader.h +++ b/src/ios/Updater/HCPUpdateLoader.h @@ -44,11 +44,5 @@ */ - (void)setup:(id)filesStructure; -/** - * Flag to check if we are doing any downloads at the moment. - * - * @return YES if download is running, NO otherwise. - */ -@property (nonatomic, readonly, getter=isDownloadInProgress) BOOL isDownloadInProgress; @end diff --git a/src/ios/Updater/HCPUpdateLoader.m b/src/ios/Updater/HCPUpdateLoader.m index 194912e5..a5b177e2 100644 --- a/src/ios/Updater/HCPUpdateLoader.m +++ b/src/ios/Updater/HCPUpdateLoader.m @@ -29,38 +29,16 @@ + (HCPUpdateLoader *)sharedInstance { return sharedInstance; } -- (BOOL)isDownloadInProgress { - return _isExecuting; -} - - (void)setup:(id)filesStructure { _filesStructure = filesStructure; } - (NSString *)addUpdateTaskToQueueWithConfigUrl:(NSURL *)configUrl { id task = [[HCPUpdateLoaderWorker alloc] initWithConfigUrl:configUrl filesStructure:_filesStructure]; - if (_isExecuting) { - _scheduledTask = task; - } else { - [self executeTask:task]; - } + [task run]; return task.workerId; } -#pragma mark Private API - -- (void)executeTask:(id)task { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - _isExecuting = YES; - [task run]; - if (_scheduledTask) { - [self executeTask:_scheduledTask]; - _scheduledTask = nil; - } else { - _isExecuting = NO; - } - }); -} @end diff --git a/src/ios/Updater/HCPUpdateLoaderWorker.m b/src/ios/Updater/HCPUpdateLoaderWorker.m index cd408171..d21e85ee 100644 --- a/src/ios/Updater/HCPUpdateLoaderWorker.m +++ b/src/ios/Updater/HCPUpdateLoaderWorker.m @@ -5,8 +5,6 @@ // #import "HCPUpdateLoaderWorker.h" -#import "HCPContentManifest+Downloader.h" -#import "HCPApplicationConfig+Downloader.h" #import "NSJSONSerialization+HCPExtension.h" #import "NSBundle+HCPExtension.h" #import "HCPManifestDiff.h" @@ -17,6 +15,7 @@ #import "HCPEvents.h" #import "NSError+HCPExtension.h" #import "HCPUpdateInstaller.h" +#import "HCPContentManifest.h" @interface HCPUpdateLoaderWorker() { NSURL *_configURL; @@ -57,73 +56,88 @@ - (void)run { return; } - // download new application config - HCPApplicationConfig *newAppConfig = [HCPApplicationConfig downloadSyncFromURL:_configURL error:&error]; - if (error) { - [self notifyWithError:[NSError errorWithCode:kHCPFailedToDownloadApplicationConfigErrorCode descriptionFromError:error] - applicationConfig:nil]; - return; - } - - // check if there is anything new on the server - if ([newAppConfig.contentConfig.releaseVersion isEqualToString:_oldAppConfig.contentConfig.releaseVersion]) { - [self notifyNothingToUpdate:newAppConfig]; - return; - } - - // check if current native version supports new content - if (newAppConfig.contentConfig.minimumNativeVersion > [NSBundle applicationBuildVersion]) { - [self notifyWithError:[NSError errorWithCode:kHCPApplicationBuildVersionTooLowErrorCode - description:@"Application build version is too low for this update"] - applicationConfig:newAppConfig]; - return; - } - - // download new content manifest - NSURL *manifestFileURL = [newAppConfig.contentConfig.contentURL URLByAppendingPathComponent:_pluginFiles.manifestFileName]; - HCPContentManifest *newManifest = [HCPContentManifest downloadSyncFromURL:manifestFileURL error:&error]; - if (error) { - [self notifyWithError:[NSError errorWithCode:kHCPFailedToDownloadContentManifestErrorCode - descriptionFromError:error] - applicationConfig:newAppConfig]; - return; - } + HCPFileDownloader *configDownload = [[HCPFileDownloader alloc] init]; - // find files that were updated - NSArray *updatedFiles = [_oldManifest calculateDifference:newManifest].updateFileList; - if (updatedFiles.count == 0) { - [_manifestStorage store:newManifest inFolder:_pluginFiles.wwwFolder]; - [_appConfigStorage store:newAppConfig inFolder:_pluginFiles.wwwFolder]; - [self notifyNothingToUpdate:newAppConfig]; + // download new application config + [configDownload downloadDataFromUrl:_configURL completionBlock:^(NSData *data, NSError *error) { + if (error) { + [self notifyWithError:[NSError errorWithCode:kHCPFailedToDownloadApplicationConfigErrorCode descriptionFromError:error] + applicationConfig:nil]; + return; + } + NSError* jsonError = nil; + NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError]; + HCPApplicationConfig* newAppConfig = [HCPApplicationConfig instanceFromJsonObject:json]; - return; - } - - [self recreateDownloadFolder:_pluginFiles.downloadFolder]; - - // download files - HCPFileDownloader *downloader = [[HCPFileDownloader alloc] init]; - BOOL isDataLoaded = [downloader downloadFilesSync:updatedFiles - fromURL:newAppConfig.contentConfig.contentURL - toFolder:_pluginFiles.downloadFolder - error:&error]; - if (!isDataLoaded) { - [[NSFileManager defaultManager] removeItemAtURL:_pluginFiles.downloadFolder error:&error]; - [self notifyWithError:[NSError errorWithCode:kHCPFailedToDownloadUpdateFilesErrorCode - descriptionFromError:error] - applicationConfig:newAppConfig]; - return; - } - - // store configs - [_manifestStorage store:newManifest inFolder:_pluginFiles.downloadFolder]; - [_appConfigStorage store:newAppConfig inFolder:_pluginFiles.downloadFolder]; - - // move download folder to installation folder - [self moveDownloadedContentToInstallationFolder]; - - // notify that we are done - [self notifyUpdateDownloadSuccess:newAppConfig]; + if ([newAppConfig.contentConfig.releaseVersion isEqualToString:_oldAppConfig.contentConfig.releaseVersion]) { + [self notifyNothingToUpdate:newAppConfig]; + return; + } + + // check if current native version supports new content + if (newAppConfig.contentConfig.minimumNativeVersion > [NSBundle applicationBuildVersion]) { + [self notifyWithError:[NSError errorWithCode:kHCPApplicationBuildVersionTooLowErrorCode + description:@"Application build version is too low for this update"] + applicationConfig:newAppConfig]; + return; + } + + // download new content manifest + NSURL *manifestFileURL = [newAppConfig.contentConfig.contentURL URLByAppendingPathComponent:_pluginFiles.manifestFileName]; + [configDownload downloadDataFromUrl:manifestFileURL completionBlock:^(NSData *data, NSError *error) { + if (error) { + [self notifyWithError:[NSError errorWithCode:kHCPFailedToDownloadContentManifestErrorCode + descriptionFromError:error] + applicationConfig:newAppConfig]; + return; + } + + NSError* jsonError = nil; + NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError]; + HCPContentManifest* newManifest = [HCPContentManifest instanceFromJsonObject:json]; + + // find files that were updated + NSArray *updatedFiles = [_oldManifest calculateDifference:newManifest].updateFileList; + if (updatedFiles.count == 0) { + [_manifestStorage store:newManifest inFolder:_pluginFiles.wwwFolder]; + [_appConfigStorage store:newAppConfig inFolder:_pluginFiles.wwwFolder]; + [self notifyNothingToUpdate:newAppConfig]; + + return; + } + + [self recreateDownloadFolder:_pluginFiles.downloadFolder]; + + // download files + HCPFileDownloader *downloader = [[HCPFileDownloader alloc] init]; + // todo set credentials on downloader + + [downloader downloadFiles:updatedFiles + fromURL:newAppConfig.contentConfig.contentURL + toFolder:_pluginFiles.downloadFolder + completionBlock:^(NSError * error) { + + if (error) { + [[NSFileManager defaultManager] removeItemAtURL:_pluginFiles.downloadFolder error:&error]; + + [self notifyWithError:[NSError errorWithCode:kHCPFailedToDownloadUpdateFilesErrorCode + descriptionFromError:error] + applicationConfig:newAppConfig]; + } else { + // store configs + [_manifestStorage store:newManifest inFolder:_pluginFiles.downloadFolder]; + [_appConfigStorage store:newAppConfig inFolder:_pluginFiles.downloadFolder]; + + // move download folder to installation folder + [self moveDownloadedContentToInstallationFolder]; + + // notify that we are done + [self notifyUpdateDownloadSuccess:newAppConfig]; + } + }]; + + }]; + }]; } #pragma mark Private API @@ -170,6 +184,7 @@ - (void)moveDownloadedContentToInstallationFolder { */ - (void)waitForInstallationToComplete { while ([HCPUpdateInstaller sharedInstance].isInstallationInProgress) { + [NSThread sleepForTimeInterval:0.1]; // avoid busy loop } }