diff --git a/README.md b/README.md index e3c2e355..b6afb6e3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ When you publish your application on the store - you pack in it all your web con 1. Publish new version of the app on the store. But it takes time, especially with the App Store. 2. Sacrifice the offline feature and load all the pages online. But as soon as Internet connection goes down - application won't work. -This plugin is intended to fix all that. When user starts the app for the first time - it copies all the web files onto the external storage. From this moment all pages are loaded from the external folder and not from the packed bundle. On every launch plugin connects to your server and checks if the new version of web project is available for download. If so - it loads it on the device and installs on the next launch. +This plugin is intended to fix all that. When user starts the app for the first time - it copies all the web files onto the external storage. From this moment all pages are loaded from the external folder and not from the packed bundle. On every launch plugin connects to your server (with optional authentication, see fetchUpdate() below) and checks if the new version of web project is available for download. If so - it loads it on the device and installs on the next launch. As a result, your application receives updates of the web content as soon as possible, and still can work in offline mode. Also, plugin allows you to specify dependency between the web release and the native version to make sure, that new release will work on the older versions of the application. @@ -307,6 +307,8 @@ To disable updates auto downloads add to `config.xml`: ``` By default preference is set to `true`. +Note that in order to authenticate to the server, this should typically be set to false, and a manual download should be initiated with credentials in the [fetchUpdate()](#fetch-update) call. + ##### auto-install Defines if plugin is allowed to install updates. Originally update installation is performed automatically, but you can disable it and do that manually through the JavaScript module. @@ -539,6 +541,7 @@ By default, all update checking->downloading->installation cycle is performed au It allows you to: - subscribe for update related events; - check and download new releases from the server; +- override authentication and other request headers to the server; - install loaded updates; - change plugin preferences; - request user to download new version of the app from the store. @@ -656,13 +659,15 @@ From now on we will know, when update is loaded and ready for installation. By u In order to force update check you can call from your web page: ```js -chcp.fetchUpdate(updateCallback); +chcp.fetchUpdate(updateCallback, headers /* optional */); function updateCallback(error, data) { // do some work } ``` +For authorization and other headers see [Change plugin preferences at runtime](#change-plugin-preferences-at-runtime) section. + Callback function gets called with two parameters: - `error` - error if any happened during the update check; `null` if everything went fine; - `data` - additional data, sent from the native side. For now it can be ignored. @@ -781,7 +786,7 @@ app.initialize(); **Be advised:** even if you call `installUpdate` method with a callback function - installation related events are still broadcasted. -#### Change plugin preferences on runtime +#### Change plugin preferences at runtime Normally all plugin preferences are set through the Cordova's `config.xml`. But you can change some of them through the JavaScript module. @@ -801,7 +806,7 @@ Supported options: Those options must be set on `deviceready` event. You should do that on every page load, because if application gets updated through the store - those options will be overridden with the corresponding values from the `config.xml`. -`auto-download` and `auto-install` can be used when you want to perform update download and installation manually. Let us extend previous example with `configure` method: +`auto-download` and `auto-install` can be used when you want to perform update download and installation manually. You will need to do this if you want to provide authentication credentials or other headers. Let us extend previous example with `configure` method: ```js var app = { @@ -847,8 +852,15 @@ var app = { } }, + // This function will typically handle authorization with a provider. + makeAuthHeaders: function() { + var auth = "Basic " + btoa("usename:password"); + + return { Authorization : auth }; + }, + checkForUpdate: function() { - chcp.fetchUpdate(this.fetchUpdateCallback); + chcp.fetchUpdate(this.fetchUpdateCallback, this.makeAuthHeaders()); }, fetchUpdateCallback: function(error, data) { diff --git a/src/ios/HCPPlugin.h b/src/ios/HCPPlugin.h index 50f540ad..49d6eab0 100644 --- a/src/ios/HCPPlugin.h +++ b/src/ios/HCPPlugin.h @@ -50,4 +50,6 @@ */ - (void)jsRequestAppUpdate:(CDVInvokedUrlCommand *)command; +@property (nonatomic, retain) NSDictionary* headers; + @end diff --git a/src/ios/HCPPlugin.m b/src/ios/HCPPlugin.m index e4bf5c30..af869f29 100644 --- a/src/ios/HCPPlugin.m +++ b/src/ios/HCPPlugin.m @@ -149,7 +149,7 @@ - (BOOL)_fetchUpdate:(NSString *)callbackId { return NO; } - NSString *taskId = [_updatesLoader addUpdateTaskToQueueWithConfigUrl:_pluginXmllConfig.configUrl]; + NSString *taskId = [_updatesLoader addUpdateTaskToQueueWithConfigUrl:_pluginXmllConfig.configUrl headers: self.headers]; [self storeCallback:callbackId forFetchTask:taskId]; return taskId != nil; @@ -626,6 +626,11 @@ - (void)jsFetchUpdate:(CDVInvokedUrlCommand *)command { if (!_isPluginReadyForWork) { return; } + + // headers may be passed as first argument, as a dict + if (command.arguments.count == 1) { + self.headers = command.arguments[0]; + } [self _fetchUpdate:command.callbackId]; } diff --git a/src/ios/Network/HCPFileDownloader.h b/src/ios/Network/HCPFileDownloader.h index 6b745962..56e22746 100644 --- a/src/ios/Network/HCPFileDownloader.h +++ b/src/ios/Network/HCPFileDownloader.h @@ -46,4 +46,7 @@ typedef void (^HCPDataDownloadCompletionBlock)(NSData *data, NSError *error); */ - (void) downloadFiles:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL completionBlock:(HCPFileDownloadCompletionBlock)block; +// headers to add to the session +@property (nonatomic, retain) NSDictionary* headers; + @end diff --git a/src/ios/Network/HCPFileDownloader.m b/src/ios/Network/HCPFileDownloader.m index d5726b71..18261c08 100644 --- a/src/ios/Network/HCPFileDownloader.m +++ b/src/ios/Network/HCPFileDownloader.m @@ -16,6 +16,9 @@ @implementation HCPFileDownloader - (void) downloadDataFromUrl:(NSURL*) url completionBlock:(HCPDataDownloadCompletionBlock) block { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + if (self.headers) { + [configuration setHTTPAdditionalHeaders:self.headers]; + } NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; NSURLSessionDataTask* dowloadTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { @@ -28,6 +31,9 @@ - (void) downloadDataFromUrl:(NSURL*) url completionBlock:(HCPDataDownloadComple - (void) downloadFiles:(NSArray *)filesList fromURL:(NSURL *)contentURL toFolder:(NSURL *)folderURL completionBlock:(HCPFileDownloadCompletionBlock)block { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + if (self.headers) { + [configuration setHTTPAdditionalHeaders:self.headers]; + } NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; __block NSMutableSet* startedTasks = [NSMutableSet set]; diff --git a/src/ios/Updater/HCPUpdateLoader.h b/src/ios/Updater/HCPUpdateLoader.h index 93f244cc..46a50d0d 100644 --- a/src/ios/Updater/HCPUpdateLoader.h +++ b/src/ios/Updater/HCPUpdateLoader.h @@ -31,10 +31,11 @@ * Add update download task to queue. It will be executed as fast as possible. * * @param configUrl url to the application config on the server + * @param optional authorization header * * @return id of the created worker */ -- (NSString *)addUpdateTaskToQueueWithConfigUrl:(NSURL *)configUrl; +- (NSString *)addUpdateTaskToQueueWithConfigUrl:(NSURL *)configUrl headers: (NSDictionary*) headers; /** * Setup loader. Should be called on application startup before any real work is performed. diff --git a/src/ios/Updater/HCPUpdateLoader.m b/src/ios/Updater/HCPUpdateLoader.m index 9e10eff4..7f0e7df7 100644 --- a/src/ios/Updater/HCPUpdateLoader.m +++ b/src/ios/Updater/HCPUpdateLoader.m @@ -37,14 +37,15 @@ - (void)setup:(id)filesStructure { _filesStructure = filesStructure; } -- (NSString *)addUpdateTaskToQueueWithConfigUrl:(NSURL *)configUrl { +- (NSString *)addUpdateTaskToQueueWithConfigUrl:(NSURL *)configUrl headers: (NSDictionary*) headers { // TODO: add better communication between installer and loader. // For now - skip update load request if installation or download is in progress. if ([HCPUpdateInstaller sharedInstance].isInstallationInProgress || _isExecuting) { return nil; } - - id task = [[HCPUpdateLoaderWorker alloc] initWithConfigUrl:configUrl filesStructure:_filesStructure]; + HCPUpdateLoaderWorker* task = [[HCPUpdateLoaderWorker alloc] initWithConfigUrl:configUrl filesStructure:_filesStructure]; + + task.headers = headers; [self executeTask:task]; return task.workerId; diff --git a/src/ios/Updater/HCPUpdateLoaderWorker.h b/src/ios/Updater/HCPUpdateLoaderWorker.h index e5aaf7e7..30a084dc 100644 --- a/src/ios/Updater/HCPUpdateLoaderWorker.h +++ b/src/ios/Updater/HCPUpdateLoaderWorker.h @@ -26,4 +26,5 @@ */ - (instancetype)initWithConfigUrl:(NSURL *)configURL filesStructure:(id)fileStructure; +@property (nonatomic, retain) NSDictionary* headers; @end diff --git a/src/ios/Updater/HCPUpdateLoaderWorker.m b/src/ios/Updater/HCPUpdateLoaderWorker.m index 25eba607..f01611ff 100644 --- a/src/ios/Updater/HCPUpdateLoaderWorker.m +++ b/src/ios/Updater/HCPUpdateLoaderWorker.m @@ -62,6 +62,7 @@ - (void)runWithComplitionBlock:(void (^)(void))updateLoaderComplitionBlock { } HCPFileDownloader *configDownloader = [[HCPFileDownloader alloc] init]; + configDownloader.headers = self.headers; // download new application config [configDownloader downloadDataFromUrl:_configURL completionBlock:^(NSData *data, NSError *error) { @@ -125,7 +126,9 @@ - (void)downloadUpdatedFiles:(NSArray *)updatedFiles appConfig:(HCPApplicationCo // download files HCPFileDownloader *downloader = [[HCPFileDownloader alloc] init]; - // TODO: set credentials on downloader + + // pass headers (auth or other) + downloader.headers = self.headers; [downloader downloadFiles:updatedFiles fromURL:newAppConfig.contentConfig.contentURL diff --git a/www/chcp.js b/www/chcp.js index db731a7b..aa5f95f5 100644 --- a/www/chcp.js +++ b/www/chcp.js @@ -204,16 +204,22 @@ var chcp = { * Usually this is done automatically by the plugin, but can be triggered at any time from the web page. * * @param {Callback(error, data)} callback - called when native side finished update process + * @param headers - provide optional headers object. This will be used to configure server request */ - fetchUpdate: function(callback) { + fetchUpdate: function(callback, headers) { var innerCallback = function(msg) { var resultObj = processMessageFromNative(msg); if (callback !== undefined && callback != null) { callback(resultObj.error, resultObj.data); } }; + + var _headers = [] + if (headers !== undefined && headers != null) { + _headers = [headers]; + } - exec(innerCallback, null, PLUGIN_NAME, pluginNativeMethod.FETCH_UPDATE, []); + exec(innerCallback, null, PLUGIN_NAME, pluginNativeMethod.FETCH_UPDATE, _headers); }, /**