From 9ab4ebb72e648156b2af60578de112022f856fb4 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 3 Aug 2017 09:37:08 +0800 Subject: [PATCH 01/20] bump to 0.10.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d88ec178..a93dba81d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-fetch-blob", - "version": "0.10.7", + "version": "0.10.8", "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.", "main": "index.js", "scripts": { From 1336555d79014a181ab79350434e016ea1bc8324 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 3 Aug 2017 09:42:16 +0800 Subject: [PATCH 02/20] Update PULL_REQUEST_TEMPLATE --- .github/PULL_REQUEST_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index cef8ed2af..b03e3e8e3 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,5 +1,5 @@ Thank you for making a pull request ! Just a gentle reminder :) 1. If the PR is offering a feature please make the request to our "Feature Branch" 0.11.0 -2. Bug fix request to "Bug Fix Branch" 0.10.8 +2. Bug fix request to "Bug Fix Branch" 0.10.9 3. Correct README.md can directly to master From 2aea0b58e99581000d7336697e70b3bba7670a50 Mon Sep 17 00:00:00 2001 From: grylance Date: Wed, 9 Aug 2017 16:12:33 +0100 Subject: [PATCH 03/20] Fix path argument in iOS excludeFromBackupKey (#473) --- ios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios.js b/ios.js index 566b424e2..340ef04cf 100644 --- a/ios.js +++ b/ios.js @@ -43,7 +43,7 @@ function openDocument(path:string, scheme:string) { * @param {string} url URL of the resource, only file URL is supported * @return {Promise} */ -function excludeFromBackupKey(url:string) { +function excludeFromBackupKey(path:string) { return RNFetchBlob.excludeFromBackupKey('file://' + path); } From 821eeb0f9db1736009ee791c14298ef72144a825 Mon Sep 17 00:00:00 2001 From: Kota Furusawa Date: Thu, 24 Aug 2017 10:02:20 +0800 Subject: [PATCH 04/20] Fix README (#501) remove extra space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7680fab86..3c7d24a59 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ RNFetchBlob console.log('The file saved to ', res.path()) // Beware that when using a file path as Image source on Android, // you must prepend "file://"" before the file path - imageView = + imageView = }) ``` From 22fd32a863343c6378c62d42f769bb58d6598d5b Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 5 Dec 2017 15:26:07 +0100 Subject: [PATCH 05/20] Catch exceptions on sd card directories constants creation. Methods to get sd card directories. sd card directiories as constants deprecated warning --- android.js | 18 +++++++++++- .../java/com/RNFetchBlob/RNFetchBlob.java | 16 +++++++---- .../java/com/RNFetchBlob/RNFetchBlobFS.java | 28 ++++++++++++++++++- fs.js | 13 +++++++-- 4 files changed, 65 insertions(+), 10 deletions(-) diff --git a/android.js b/android.js index 0b5dbd84b..c9abd283a 100644 --- a/android.js +++ b/android.js @@ -38,9 +38,25 @@ function addCompleteDownload(config) { return Promise.reject('RNFetchBlob.android.addCompleteDownload only supports Android.') } +function getSDCardDir() { + if(Platform.OS === 'android') + return RNFetchBlob.getSDCardDir() + else + return Promise.reject('RNFetchBlob.android.getSDCardDir only supports Android.') +} + +function getSDCardApplicationDir() { + if(Platform.OS === 'android') + return RNFetchBlob.getSDCardApplicationDir() + else + return Promise.reject('RNFetchBlob.android.getSDCardApplicationDir only supports Android.') +} + export default { actionViewIntent, getContentIntent, - addCompleteDownload + addCompleteDownload, + getSDCardDir, + getSDCardApplicationDir, } diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java index 19e1be435..f5597d3ad 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java @@ -92,7 +92,6 @@ public void run() { RNFetchBlobFS.createFile(path, content, encode, callback); } }); - } @ReactMethod @@ -136,7 +135,6 @@ public void run() { RNFetchBlobFS.createFileASCII(path, dataArray, callback); } }); - } @ReactMethod @@ -167,7 +165,6 @@ public void run() { RNFetchBlobFS.cp(path, dest, callback); } }); - } @ReactMethod @@ -228,7 +225,6 @@ public void run() { RNFetchBlobFS.writeFile(path, encoding, data, append, promise); } }); - } @ReactMethod @@ -263,7 +259,6 @@ public void run() { new RNFetchBlobFS(ctx).scanFile(p, m, callback); } }); - } @ReactMethod @@ -324,7 +319,7 @@ public void enableUploadProgressReport(String taskId, int interval, int count) { @ReactMethod public void fetchBlob(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, final Callback callback) { new RNFetchBlobReq(options, taskId, method, url, headers, body, null, mClient, callback).run(); -} + } @ReactMethod public void fetchBlobForm(ReadableMap options, String taskId, String method, String url, ReadableMap headers, ReadableArray body, final Callback callback) { @@ -370,4 +365,13 @@ public void addCompleteDownload (ReadableMap config, Promise promise) { } + @ReactMethod + public void getSDCardDir(Promise promise) { + RNFetchBlobFS.getSDCardDir(promise); + } + + @ReactMethod + public void getSDCardApplicationDir(Promise promise) { + RNFetchBlobFS.getSDCardApplicationDir(this.getReactApplicationContext(), promise); + } } diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index 7a7910546..0959c4f9f 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -213,12 +213,38 @@ static public Map getSystemfolders(ReactApplicationContext ctx) state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { res.put("SDCardDir", Environment.getExternalStorageDirectory().getAbsolutePath()); - res.put("SDCardApplicationDir", ctx.getExternalFilesDir(null).getParentFile().getAbsolutePath()); + try { + res.put("SDCardApplicationDir", ctx.getExternalFilesDir(null).getParentFile().getAbsolutePath()); + } catch(Exception e) { + res.put("SDCardApplicationDir", ""); + } } res.put("MainBundleDir", ctx.getApplicationInfo().dataDir); return res; } + static public void getSDCardDir(Promise promise) { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + promise.resolve(Environment.getExternalStorageDirectory().getAbsolutePath()); + } else { + promise.reject("RNFetchBlob.getSDCardDir", "External storage not mounted"); + } + + } + + static public void getSDCardApplicationDir(ReactApplicationContext ctx, Promise promise) { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + try { + final String path = ctx.getExternalFilesDir(null).getParentFile().getAbsolutePath(); + promise.resolve(path); + } catch (Exception e) { + promise.reject("RNFetchBlob.getSDCardApplicationDir", e.getLocalizedMessage()); + } + } else { + promise.reject("RNFetchBlob.getSDCardApplicationDir", "External storage not mounted"); + } + } + /** * Static method that returns a temp file path * @param ctx React Native application context diff --git a/fs.js b/fs.js index 83e6bdceb..f50691e5e 100644 --- a/fs.js +++ b/fs.js @@ -28,8 +28,17 @@ const dirs = { MovieDir : RNFetchBlob.MovieDir, DownloadDir : RNFetchBlob.DownloadDir, DCIMDir : RNFetchBlob.DCIMDir, - SDCardDir : RNFetchBlob.SDCardDir, - SDCardApplicationDir : RNFetchBlob.SDCardApplicationDir, + get SDCardDir() { + console.warn('SDCardDir as a constant is deprecated and will be removed in feature release. ' + + 'Use RNFetchBlob.android.getSDCardDir():Promise instead.'); + return RNFetchBlob.SDCardDir; + }, + get SDCardApplicationDir() { + console.warn('SDCardApplicationDir as a constant is deprecated and will be removed in feature release. ' + + 'Use RNFetchBlob.android.getSDCardApplicationDir():Promise instead. ' + + 'This variable can be empty on error in native code.'); + return RNFetchBlob.SDCardApplicationDir; + }, MainBundleDir : RNFetchBlob.MainBundleDir, LibraryDir : RNFetchBlob.LibraryDir } From e46f94e2ce7f135f3e9088c95bbd53cd79481ab5 Mon Sep 17 00:00:00 2001 From: Mattia Barbon Date: Wed, 23 Aug 2017 06:32:52 +0200 Subject: [PATCH 06/20] Fix iOS initialization race condition (#499) We have a crash in our application, and based on our analysis the culprit is a race condition in RNFetchBlobNetwork initialization. Crashing thread: Crashed: com.apple.root.default-qos 0 libobjc.A.dylib 0x10416bacb objc_msgSend + 11 1 Foundation 0x103d3b644 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] + 153 2 Foundation 0x103c23f8c -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] + 61 3 Foundation 0x103c23f3a -[NSOperationQueue setMaxConcurrentOperationCount:] + 198 4 prymr 0x100d579aa -[RNFetchBlobNetwork init] (RNFetchBlobNetwork.m:112) 5 prymr 0x100d63a73 __65-[RNFetchBlob fetchBlob:taskId:method:url:headers:body:callback:]_block_invoke (RNFetchBlob.m:131) 6 prymr 0x100d6006b __85+[RNFetchBlobReqBuilder buildOctetRequest:taskId:method:url:headers:body:onComplete:]_block_invoke (RNFetchBlobReqBuilder.m:178) 7 libdispatch.dylib 0x107511585 _dispatch_call_block_and_release + 12 While a second thread is running: com.apple.root.default-qos 0 libsystem_kernel.dylib 0x107891c22 __psynch_mutexwait + 10 1 libsystem_pthread.dylib 0x1078c6dfa _pthread_mutex_lock_wait + 100 2 libsystem_pthread.dylib 0x1078c4519 _pthread_mutex_lock_slow + 285 3 Foundation 0x103d3b615 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] + 106 4 Foundation 0x103c23f8c -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] + 61 5 Foundation 0x103c23f3a -[NSOperationQueue setMaxConcurrentOperationCount:] + 198 6 prymr 0x100d579aa -[RNFetchBlobNetwork init] (RNFetchBlobNetwork.m:112) 7 prymr 0x100d63a73 __65-[RNFetchBlob fetchBlob:taskId:method:url:headers:body:callback:]_block_invoke (RNFetchBlob.m:131) 8 prymr 0x100d6006b __85+[RNFetchBlobReqBuilder buildOctetRequest:taskId:method:url:headers:body:onComplete:]_block_invoke (RNFetchBlobReqBuilder.m:178) 9 libdispatch.dylib 0x107511585 _dispatch_call_block_and_release + 12 The patch just adds a dumb double-synchronization to the initialization. --- ios/RNFetchBlobNetwork.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 7be57fc59..c2f6d1680 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -106,8 +106,12 @@ @implementation RNFetchBlobNetwork - (id)init { self = [super init]; if(taskQueue == nil) { - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.maxConcurrentOperationCount = 10; + @synchronized ([RNFetchBlobNetwork class]) { + if (taskQueue == nil) { + taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.maxConcurrentOperationCount = 10; + } + } } return self; } From 38f4d75eadb1cb7faed34ad613d190562d73c10f Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Fri, 12 Jan 2018 11:47:20 +0100 Subject: [PATCH 07/20] Synchronized dictionaries and tables operations --- ios/RNFetchBlobNetwork.m | 94 +++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 7be57fc59..621f48693 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -105,29 +105,35 @@ @implementation RNFetchBlobNetwork // constructor - (id)init { self = [super init]; - if(taskQueue == nil) { - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.maxConcurrentOperationCount = 10; + @synchronized ([RNFetchBlobNetwork class]) { + if (taskQueue == nil) { + taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.maxConcurrentOperationCount = 10; + } } return self; } + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config { - if(progressTable == nil) - { - progressTable = [[NSMutableDictionary alloc] init]; + @synchronized ([RNFetchBlobNetwork class]) { + if(progressTable == nil) + { + progressTable = [[NSMutableDictionary alloc] init]; + } + [progressTable setValue:config forKey:taskId]; } - [progressTable setValue:config forKey:taskId]; } + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config { - if(uploadProgressTable == nil) - { - uploadProgressTable = [[NSMutableDictionary alloc] init]; + @synchronized ([RNFetchBlobNetwork class]) { + if(uploadProgressTable == nil) + { + uploadProgressTable = [[NSMutableDictionary alloc] init]; + } + [uploadProgressTable setValue:config forKey:taskId]; } - [uploadProgressTable setValue:config forKey:taskId]; } // removing case from headers @@ -241,8 +247,10 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options } __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; - [taskTable setObject:task forKey:taskId]; - [task resume]; + @synchronized ([RNFetchBlobNetwork class]){ + [taskTable setObject:task forKey:taskId]; + [task resume]; + } // network status indicator if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) @@ -254,21 +262,22 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled + (void) emitExpiredTasks { - NSEnumerator * emu = [expirationTable keyEnumerator]; - NSString * key; - - while((key = [emu nextObject])) - { - RCTBridge * bridge = [RNFetchBlob getRCTBridge]; - NSData * args = @{ @"taskId": key }; - [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; + @synchronized ([RNFetchBlobNetwork class]){ + NSEnumerator * emu = [expirationTable keyEnumerator]; + NSString * key; - } + while((key = [emu nextObject])) + { + RCTBridge * bridge = [RNFetchBlob getRCTBridge]; + NSData * args = @{ @"taskId": key }; + [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; - // clear expired task entries - [expirationTable removeAllObjects]; - expirationTable = [[NSMapTable alloc] init]; + } + // clear expired task entries + [expirationTable removeAllObjects]; + expirationTable = [[NSMapTable alloc] init]; + } } //////////////////////////////////////// @@ -448,10 +457,18 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat { [writeStream write:[data bytes] maxLength:[data length]]; } - RNFetchBlobProgress * pconfig = [progressTable valueForKey:taskId]; + if(expectedBytes == 0) return; + + RNFetchBlobProgress * pconfig; + + @synchronized ([RNFetchBlobNetwork class]){ + pconfig = [progressTable valueForKey:taskId]; + } + NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; + if(pconfig != nil && [pconfig shouldReport:now]) { [self.bridge.eventDispatcher @@ -461,11 +478,9 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat @"written": [NSString stringWithFormat:@"%d", receivedBytes], @"total": [NSString stringWithFormat:@"%d", expectedBytes], @"chunk": chunkString - } + } ]; } - received = nil; - } - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error @@ -537,7 +552,7 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom callback(@[ errMsg, rnfbRespType, respStr]); - @synchronized(taskTable, uploadProgressTable, progressTable) + @synchronized ([RNFetchBlobNetwork class]) { if([taskTable objectForKey:taskId] == nil) NSLog(@"object released by ARC."); @@ -556,17 +571,23 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom // upload progress handler - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite { - RNFetchBlobProgress * pconfig = [uploadProgressTable valueForKey:taskId]; if(totalBytesExpectedToWrite == 0) return; + + RNFetchBlobProgress * pconfig; + + @synchronized ([RNFetchBlobNetwork class]) { + pconfig = [uploadProgressTable valueForKey:taskId]; + } + NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; if(pconfig != nil && [pconfig shouldReport:now]) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD body:@{ @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%d", totalBytesWritten], - @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite] + @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten], + @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite] } ]; } @@ -574,7 +595,12 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen + (void) cancelRequest:(NSString *)taskId { - NSURLSessionDataTask * task = [taskTable objectForKey:taskId]; + NSURLSessionDataTask * task; + + @synchronized ([RNFetchBlobNetwork class]) { + task = [taskTable objectForKey:taskId]; + } + if(task != nil && task.state == NSURLSessionTaskStateRunning) [task cancel]; } From 17cc8b0f7031888692a9743338f7bf95c323b079 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Fri, 12 Jan 2018 11:47:20 +0100 Subject: [PATCH 08/20] Synchronized dictionaries and tables operations --- ios/RNFetchBlobNetwork.m | 96 ++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index c2f6d1680..621f48693 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -105,12 +105,10 @@ @implementation RNFetchBlobNetwork // constructor - (id)init { self = [super init]; - if(taskQueue == nil) { - @synchronized ([RNFetchBlobNetwork class]) { - if (taskQueue == nil) { - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.maxConcurrentOperationCount = 10; - } + @synchronized ([RNFetchBlobNetwork class]) { + if (taskQueue == nil) { + taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.maxConcurrentOperationCount = 10; } } return self; @@ -118,20 +116,24 @@ - (id)init { + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config { - if(progressTable == nil) - { - progressTable = [[NSMutableDictionary alloc] init]; + @synchronized ([RNFetchBlobNetwork class]) { + if(progressTable == nil) + { + progressTable = [[NSMutableDictionary alloc] init]; + } + [progressTable setValue:config forKey:taskId]; } - [progressTable setValue:config forKey:taskId]; } + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config { - if(uploadProgressTable == nil) - { - uploadProgressTable = [[NSMutableDictionary alloc] init]; + @synchronized ([RNFetchBlobNetwork class]) { + if(uploadProgressTable == nil) + { + uploadProgressTable = [[NSMutableDictionary alloc] init]; + } + [uploadProgressTable setValue:config forKey:taskId]; } - [uploadProgressTable setValue:config forKey:taskId]; } // removing case from headers @@ -245,8 +247,10 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options } __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; - [taskTable setObject:task forKey:taskId]; - [task resume]; + @synchronized ([RNFetchBlobNetwork class]){ + [taskTable setObject:task forKey:taskId]; + [task resume]; + } // network status indicator if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) @@ -258,21 +262,22 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled + (void) emitExpiredTasks { - NSEnumerator * emu = [expirationTable keyEnumerator]; - NSString * key; - - while((key = [emu nextObject])) - { - RCTBridge * bridge = [RNFetchBlob getRCTBridge]; - NSData * args = @{ @"taskId": key }; - [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; + @synchronized ([RNFetchBlobNetwork class]){ + NSEnumerator * emu = [expirationTable keyEnumerator]; + NSString * key; - } + while((key = [emu nextObject])) + { + RCTBridge * bridge = [RNFetchBlob getRCTBridge]; + NSData * args = @{ @"taskId": key }; + [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; - // clear expired task entries - [expirationTable removeAllObjects]; - expirationTable = [[NSMapTable alloc] init]; + } + // clear expired task entries + [expirationTable removeAllObjects]; + expirationTable = [[NSMapTable alloc] init]; + } } //////////////////////////////////////// @@ -452,10 +457,18 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat { [writeStream write:[data bytes] maxLength:[data length]]; } - RNFetchBlobProgress * pconfig = [progressTable valueForKey:taskId]; + if(expectedBytes == 0) return; + + RNFetchBlobProgress * pconfig; + + @synchronized ([RNFetchBlobNetwork class]){ + pconfig = [progressTable valueForKey:taskId]; + } + NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; + if(pconfig != nil && [pconfig shouldReport:now]) { [self.bridge.eventDispatcher @@ -465,11 +478,9 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat @"written": [NSString stringWithFormat:@"%d", receivedBytes], @"total": [NSString stringWithFormat:@"%d", expectedBytes], @"chunk": chunkString - } + } ]; } - received = nil; - } - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error @@ -541,7 +552,7 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom callback(@[ errMsg, rnfbRespType, respStr]); - @synchronized(taskTable, uploadProgressTable, progressTable) + @synchronized ([RNFetchBlobNetwork class]) { if([taskTable objectForKey:taskId] == nil) NSLog(@"object released by ARC."); @@ -560,17 +571,23 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom // upload progress handler - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite { - RNFetchBlobProgress * pconfig = [uploadProgressTable valueForKey:taskId]; if(totalBytesExpectedToWrite == 0) return; + + RNFetchBlobProgress * pconfig; + + @synchronized ([RNFetchBlobNetwork class]) { + pconfig = [uploadProgressTable valueForKey:taskId]; + } + NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; if(pconfig != nil && [pconfig shouldReport:now]) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD body:@{ @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%d", totalBytesWritten], - @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite] + @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten], + @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite] } ]; } @@ -578,7 +595,12 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen + (void) cancelRequest:(NSString *)taskId { - NSURLSessionDataTask * task = [taskTable objectForKey:taskId]; + NSURLSessionDataTask * task; + + @synchronized ([RNFetchBlobNetwork class]) { + task = [taskTable objectForKey:taskId]; + } + if(task != nil && task.state == NSURLSessionTaskStateRunning) [task cancel]; } From 905ab1c2da7f138e6b37f52ac23d4a2bc77851fa Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 14:16:19 +0100 Subject: [PATCH 09/20] Mostly RNFetchBlobNetwork cleanup --- ios/RNFetchBlob/RNFetchBlob.h | 1 - ios/RNFetchBlob/RNFetchBlob.m | 4 +- ios/RNFetchBlobConst.m | 56 +++++++++++++------------- ios/RNFetchBlobFS.h | 4 +- ios/RNFetchBlobNetwork.h | 20 +++------- ios/RNFetchBlobNetwork.m | 74 +++++++++++++++-------------------- ios/RNFetchBlobReqBuilder.h | 2 +- ios/RNFetchBlobReqBuilder.m | 2 +- 8 files changed, 72 insertions(+), 91 deletions(-) diff --git a/ios/RNFetchBlob/RNFetchBlob.h b/ios/RNFetchBlob/RNFetchBlob.h index e6385114f..669a093b5 100644 --- a/ios/RNFetchBlob/RNFetchBlob.h +++ b/ios/RNFetchBlob/RNFetchBlob.h @@ -39,7 +39,6 @@ @property (retain) UIDocumentInteractionController * documentController; + (RCTBridge *)getRCTBridge; -+ (void) checkExpiredSessions; @end diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 246d6707c..40fc4c502 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -38,7 +38,7 @@ - (dispatch_queue_t) methodQueue { + (RCTBridge *)getRCTBridge { - RCTRootView * rootView = [[UIApplication sharedApplication] keyWindow].rootViewController.view; + RCTRootView * rootView = (RCTRootView*) [[UIApplication sharedApplication] keyWindow].rootViewController.view; return rootView.bridge; } @@ -128,7 +128,7 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - __block RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; + RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback]; } }]; diff --git a/ios/RNFetchBlobConst.m b/ios/RNFetchBlobConst.m index 6f7fef4b2..bc9b793a5 100644 --- a/ios/RNFetchBlobConst.m +++ b/ios/RNFetchBlobConst.m @@ -7,38 +7,38 @@ // #import "RNFetchBlobConst.h" -extern NSString *const FILE_PREFIX = @"RNFetchBlob-file://"; -extern NSString *const ASSET_PREFIX = @"bundle-assets://"; -extern NSString *const AL_PREFIX = @"assets-library://"; +NSString *const FILE_PREFIX = @"RNFetchBlob-file://"; +NSString *const ASSET_PREFIX = @"bundle-assets://"; +NSString *const AL_PREFIX = @"assets-library://"; // fetch configs -extern NSString *const CONFIG_USE_TEMP = @"fileCache"; -extern NSString *const CONFIG_FILE_PATH = @"path"; -extern NSString *const CONFIG_FILE_EXT = @"appendExt"; -extern NSString *const CONFIG_TRUSTY = @"trusty"; -extern NSString *const CONFIG_INDICATOR = @"indicator"; -extern NSString *const CONFIG_KEY = @"key"; -extern NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes"; +NSString *const CONFIG_USE_TEMP = @"fileCache"; +NSString *const CONFIG_FILE_PATH = @"path"; +NSString *const CONFIG_FILE_EXT = @"appendExt"; +NSString *const CONFIG_TRUSTY = @"trusty"; +NSString *const CONFIG_INDICATOR = @"indicator"; +NSString *const CONFIG_KEY = @"key"; +NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes"; -extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState"; -extern NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush"; -extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress"; -extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload"; -extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire"; +NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState"; +NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush"; +NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress"; +NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload"; +NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire"; -extern NSString *const MSG_EVENT = @"RNFetchBlobMessage"; -extern NSString *const MSG_EVENT_LOG = @"log"; -extern NSString *const MSG_EVENT_WARN = @"warn"; -extern NSString *const MSG_EVENT_ERROR = @"error"; -extern NSString *const FS_EVENT_DATA = @"data"; -extern NSString *const FS_EVENT_END = @"end"; -extern NSString *const FS_EVENT_WARN = @"warn"; -extern NSString *const FS_EVENT_ERROR = @"error"; +NSString *const MSG_EVENT = @"RNFetchBlobMessage"; +NSString *const MSG_EVENT_LOG = @"log"; +NSString *const MSG_EVENT_WARN = @"warn"; +NSString *const MSG_EVENT_ERROR = @"error"; +NSString *const FS_EVENT_DATA = @"data"; +NSString *const FS_EVENT_END = @"end"; +NSString *const FS_EVENT_WARN = @"warn"; +NSString *const FS_EVENT_ERROR = @"error"; -extern NSString *const KEY_REPORT_PROGRESS = @"reportProgress"; -extern NSString *const KEY_REPORT_UPLOAD_PROGRESS = @"reportUploadProgress"; +NSString *const KEY_REPORT_PROGRESS = @"reportProgress"; +NSString *const KEY_REPORT_UPLOAD_PROGRESS = @"reportUploadProgress"; // response type -extern NSString *const RESP_TYPE_BASE64 = @"base64"; -extern NSString *const RESP_TYPE_UTF8 = @"utf8"; -extern NSString *const RESP_TYPE_PATH = @"path"; +NSString *const RESP_TYPE_BASE64 = @"base64"; +NSString *const RESP_TYPE_UTF8 = @"utf8"; +NSString *const RESP_TYPE_PATH = @"path"; diff --git a/ios/RNFetchBlobFS.h b/ios/RNFetchBlobFS.h index 97386ef9d..c714fa66c 100644 --- a/ios/RNFetchBlobFS.h +++ b/ios/RNFetchBlobFS.h @@ -34,8 +34,8 @@ NSString * streamId; } -@property (nonatomic) NSOutputStream * outStream; -@property (nonatomic) NSInputStream * inStream; +@property (nonatomic) NSOutputStream * _Nullable outStream; +@property (nonatomic) NSInputStream * _Nullable inStream; @property (strong, nonatomic) RCTResponseSenderBlock callback; @property (nonatomic) RCTBridge * bridge; @property (nonatomic) NSString * encoding; diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index d3b4654a5..ef9e85ea7 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -20,39 +20,31 @@ #define RNFetchBlobNetwork_h - typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error); typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error); @interface RNFetchBlobNetwork : NSObject @property (nullable, nonatomic) NSString * taskId; -@property (nonatomic) int expectedBytes; -@property (nonatomic) int receivedBytes; +@property (nonatomic) long long expectedBytes; +@property (nonatomic) long long receivedBytes; @property (nonatomic) BOOL isServerPush; @property (nullable, nonatomic) NSMutableData * respData; -@property (strong, nonatomic) RCTResponseSenderBlock callback; +@property (nullable, strong, nonatomic) RCTResponseSenderBlock callback; @property (nullable, nonatomic) RCTBridge * bridge; @property (nullable, nonatomic) NSDictionary * options; @property (nullable, nonatomic) RNFetchBlobFS * fileStream; -@property (strong, nonatomic) CompletionHander fileTaskCompletionHandler; -@property (strong, nonatomic) DataTaskCompletionHander dataTaskCompletionHandler; @property (nullable, nonatomic) NSError * error; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; -+ (void) cancelRequest:(NSString *)taskId; -+ (void) enableProgressReport:(NSString *) taskId; -+ (void) enableUploadProgress:(NSString *) taskId; ++ (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; ++ (void) enableProgressReport:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; ++ (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; - (nullable id) init; -- (void) sendRequest; - (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; -+ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config; -+ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config; - - @end diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 621f48693..88fde5752 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -70,18 +70,17 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) { @interface RNFetchBlobNetwork () { - BOOL * respFile; + BOOL respFile; BOOL isNewPart; - BOOL * isIncrement; + BOOL isIncrement; NSMutableData * partBuffer; NSString * destPath; NSOutputStream * writeStream; long bodyLength; - NSMutableDictionary * respInfo; NSInteger respStatus; NSMutableArray * redirects; ResponseFormat responseFormat; - BOOL * followRedirect; + BOOL followRedirect; BOOL backgroundTask; } @@ -97,8 +96,6 @@ @implementation RNFetchBlobNetwork @synthesize callback; @synthesize bridge; @synthesize options; -@synthesize fileTaskCompletionHandler; -@synthesize dataTaskCompletionHandler; @synthesize error; @@ -121,7 +118,7 @@ + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *) { progressTable = [[NSMutableDictionary alloc] init]; } - [progressTable setValue:config forKey:taskId]; + if (config) [progressTable setValue:config forKey:taskId]; } } @@ -132,7 +129,7 @@ + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *) { uploadProgressTable = [[NSMutableDictionary alloc] init]; } - [uploadProgressTable setValue:config forKey:taskId]; + if (config) [uploadProgressTable setValue:config forKey:taskId]; } } @@ -193,9 +190,8 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options responseFormat = AUTO; NSString * path = [self.options valueForKey:CONFIG_FILE_PATH]; - NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT]; NSString * key = [self.options valueForKey:CONFIG_KEY]; - __block NSURLSession * session; + NSURLSession * session; bodyLength = contentLength; @@ -246,16 +242,15 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options respFile = NO; } - __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; + NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; @synchronized ([RNFetchBlobNetwork class]){ [taskTable setObject:task forKey:taskId]; - [task resume]; } + [task resume]; // network status indicator if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - __block UIApplication * app = [UIApplication sharedApplication]; } @@ -269,7 +264,7 @@ + (void) emitExpiredTasks while((key = [emu nextObject])) { RCTBridge * bridge = [RNFetchBlob getRCTBridge]; - NSData * args = @{ @"taskId": key }; + id args = @{ @"taskId": key }; [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; } @@ -352,23 +347,14 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat { respType = @"blob"; // for XMLHttpRequest, switch response data handling strategy automatically - if([options valueForKey:@"auto"] == YES) { + if([options valueForKey:@"auto"]) { respFile = YES; destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; } } - } - else + } else { respType = @"text"; - respInfo = @{ - @"taskId": taskId, - @"state": @"2", - @"headers": headers, - @"redirects": redirects, - @"respType" : respType, - @"timeout" : @NO, - @"status": [NSNumber numberWithInteger:statusCode] - }; + } #pragma mark - handling cookies // # 153 get cookies @@ -383,11 +369,16 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat [self.bridge.eventDispatcher sendDeviceEventWithName: EVENT_STATE_CHANGE - body:respInfo + body:@{ + @"taskId": taskId, + @"state": @"2", + @"headers": headers, + @"redirects": redirects, + @"respType" : respType, + @"timeout" : @NO, + @"status": [NSNumber numberWithInteger:statusCode] + } ]; - headers = nil; - respInfo = nil; - } else NSLog(@"oops"); @@ -475,8 +466,8 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat sendDeviceEventWithName:EVENT_PROGRESS body:@{ @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%d", receivedBytes], - @"total": [NSString stringWithFormat:@"%d", expectedBytes], + @"written": [NSString stringWithFormat:@"%ld", (long) receivedBytes], + @"total": [NSString stringWithFormat:@"%ld", (long) expectedBytes], @"chunk": chunkString } ]; @@ -494,17 +485,12 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom { self.error = error; - NSString * errMsg = [NSNull null]; - NSString * respStr = [NSNull null]; - NSString * rnfbRespType = @""; + NSString * errMsg; + NSString * respStr; + NSString * rnfbRespType; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - if(respInfo == nil) - { - respInfo = [NSNull null]; - } - if(error != nil) { errMsg = [error localizedDescription]; @@ -550,7 +536,11 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom } - callback(@[ errMsg, rnfbRespType, respStr]); + callback(@[ + errMsg ?: [NSNull null], + rnfbRespType ?: @"", + respStr ?: [NSNull null] + ]); @synchronized ([RNFetchBlobNetwork class]) { @@ -608,7 +598,7 @@ + (void) cancelRequest:(NSString *)taskId - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler { - BOOL trusty = [options valueForKey:CONFIG_TRUSTY]; + BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; if(!trusty) { completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); diff --git a/ios/RNFetchBlobReqBuilder.h b/ios/RNFetchBlobReqBuilder.h index e7abeb9c7..1edc3ff50 100644 --- a/ios/RNFetchBlobReqBuilder.h +++ b/ios/RNFetchBlobReqBuilder.h @@ -29,7 +29,7 @@ body:(NSString *)body onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete; -+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSMutableArray *) headers; ++(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers; @end diff --git a/ios/RNFetchBlobReqBuilder.m b/ios/RNFetchBlobReqBuilder.m index 15465a1ae..e9eced6c4 100644 --- a/ios/RNFetchBlobReqBuilder.m +++ b/ios/RNFetchBlobReqBuilder.m @@ -277,7 +277,7 @@ void __block (^getFieldData)(id field) = ^(id field) } } -+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSMutableDictionary *) headers { ++(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers { NSString * normalCase = [headers valueForKey:field]; NSString * ignoredCase = [headers valueForKey:[field lowercaseString]]; From 11f674c88b1c2b6d0d93141a2d5011f104e2d7e1 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 15:13:44 +0100 Subject: [PATCH 10/20] QOS level for task queue --- ios/RNFetchBlobNetwork.m | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 88fde5752..37490fdc7 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -105,6 +105,7 @@ - (id)init { @synchronized ([RNFetchBlobNetwork class]) { if (taskQueue == nil) { taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.qualityOfService = NSQualityOfServiceUtility; taskQueue.maxConcurrentOperationCount = 10; } } From f0301db36ce6b54e2a9f942173523ad1a94e33ae Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 15:46:31 +0100 Subject: [PATCH 11/20] Unused completion handlers leftovers removed --- ios/RNFetchBlobNetwork.h | 3 --- ios/RNFetchBlobNetwork.m | 1 - 2 files changed, 4 deletions(-) diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index ef9e85ea7..dcc5b99c5 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -20,9 +20,6 @@ #define RNFetchBlobNetwork_h -typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error); -typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error); - @interface RNFetchBlobNetwork : NSObject @property (nullable, nonatomic) NSString * taskId; diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 37490fdc7..ae22b37b8 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -137,7 +137,6 @@ + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *) // removing case from headers + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers { - NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init]; for(NSString * key in headers) { [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]]; From 8dd310d9f62979b77b39ddfb11ef6287fcf71bae Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 17:16:46 +0100 Subject: [PATCH 12/20] Separate requests from network logic --- ios/RNFetchBlob.xcodeproj/project.pbxproj | 8 +- ios/RNFetchBlob/RNFetchBlob.m | 16 +- ios/RNFetchBlobNetwork.h | 14 +- ios/RNFetchBlobNetwork.m | 580 ++-------------------- ios/RNFetchBlobRequest.h | 47 ++ ios/RNFetchBlobRequest.m | 495 ++++++++++++++++++ 6 files changed, 616 insertions(+), 544 deletions(-) create mode 100644 ios/RNFetchBlobRequest.h create mode 100644 ios/RNFetchBlobRequest.m diff --git a/ios/RNFetchBlob.xcodeproj/project.pbxproj b/ios/RNFetchBlob.xcodeproj/project.pbxproj index acd524413..070fcabb1 100644 --- a/ios/RNFetchBlob.xcodeproj/project.pbxproj +++ b/ios/RNFetchBlob.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8C4801A6200CF71700FED7ED /* RNFetchBlobRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */; }; A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */; }; A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */; }; A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; }; @@ -29,6 +30,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8C4801A4200CF71700FED7ED /* RNFetchBlobRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobRequest.h; sourceTree = ""; }; + 8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobRequest.m; sourceTree = ""; }; A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobFS.m; sourceTree = ""; }; A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobFS.h; sourceTree = ""; }; A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobConst.h; sourceTree = ""; }; @@ -71,8 +74,10 @@ A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */, A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */, A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */, - A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */, A158F42E1D0539CE006FFD38 /* RNFetchBlobNetwork.h */, + A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */, + 8C4801A4200CF71700FED7ED /* RNFetchBlobRequest.h */, + 8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */, A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */, A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */, A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */, @@ -149,6 +154,7 @@ buildActionMask = 2147483647; files = ( A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */, + 8C4801A6200CF71700FED7ED /* RNFetchBlobRequest.m in Sources */, A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */, A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */, A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */, diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 40fc4c502..6e8141f9e 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -96,8 +96,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; - [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback]; + [RNFetchBlobNetwork sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; @@ -128,8 +132,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; - [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback]; + [RNFetchBlobNetwork sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; } diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index dcc5b99c5..b93541267 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -22,18 +22,8 @@ @interface RNFetchBlobNetwork : NSObject -@property (nullable, nonatomic) NSString * taskId; -@property (nonatomic) long long expectedBytes; -@property (nonatomic) long long receivedBytes; -@property (nonatomic) BOOL isServerPush; -@property (nullable, nonatomic) NSMutableData * respData; -@property (nullable, strong, nonatomic) RCTResponseSenderBlock callback; -@property (nullable, nonatomic) RCTBridge * bridge; -@property (nullable, nonatomic) NSDictionary * options; -@property (nullable, nonatomic) RNFetchBlobFS * fileStream; -@property (nullable, nonatomic) NSError * error; - ++ (_Nullable instancetype)sharedInstance; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; + (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; @@ -41,7 +31,7 @@ + (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; - (nullable id) init; -- (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; ++ (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; @end diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index ae22b37b8..5b3a32966 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -8,14 +8,12 @@ #import -#import "RNFetchBlob.h" -#import "RNFetchBlobFS.h" #import "RNFetchBlobNetwork.h" + +#import "RNFetchBlob.h" #import "RNFetchBlobConst.h" -#import "RNFetchBlobReqBuilder.h" -#import "IOS7Polyfill.h" -#import #import "RNFetchBlobProgress.h" +#import "RNFetchBlobRequest.h" #if __has_include() #import @@ -35,10 +33,7 @@ // //////////////////////////////////////// -NSMapTable * taskTable; NSMapTable * expirationTable; -NSMutableDictionary * progressTable; -NSMutableDictionary * uploadProgressTable; __attribute__((constructor)) static void initialize_tables() { @@ -46,91 +41,73 @@ static void initialize_tables() { { expirationTable = [[NSMapTable alloc] init]; } - if(taskTable == nil) - { - taskTable = [[NSMapTable alloc] init]; - } - if(progressTable == nil) - { - progressTable = [[NSMutableDictionary alloc] init]; - } - if(uploadProgressTable == nil) - { - uploadProgressTable = [[NSMutableDictionary alloc] init]; - } } -typedef NS_ENUM(NSUInteger, ResponseFormat) { - UTF8, - BASE64, - AUTO -}; - - -@interface RNFetchBlobNetwork () -{ - BOOL respFile; - BOOL isNewPart; - BOOL isIncrement; - NSMutableData * partBuffer; - NSString * destPath; - NSOutputStream * writeStream; - long bodyLength; - NSInteger respStatus; - NSMutableArray * redirects; - ResponseFormat responseFormat; - BOOL followRedirect; - BOOL backgroundTask; -} - -@end - @implementation RNFetchBlobNetwork NSOperationQueue *taskQueue; -@synthesize taskId; -@synthesize expectedBytes; -@synthesize receivedBytes; -@synthesize respData; -@synthesize callback; -@synthesize bridge; -@synthesize options; -@synthesize error; +NSMapTable * requestsTable; - -// constructor - (id)init { self = [super init]; - @synchronized ([RNFetchBlobNetwork class]) { - if (taskQueue == nil) { - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.qualityOfService = NSQualityOfServiceUtility; - taskQueue.maxConcurrentOperationCount = 10; - } + if (self) { + requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory]; + + taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.qualityOfService = NSQualityOfServiceUtility; + taskQueue.maxConcurrentOperationCount = 10; } + return self; } ++ (instancetype)sharedInstance { + static id _sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedInstance = [[self alloc] init]; + }); + + return _sharedInstance; +} + ++ (void) sendRequest:(__weak NSDictionary * _Nullable )options + contentLength:(long) contentLength + bridge:(RCTBridge * _Nullable)bridgeRef + taskId:(NSString * _Nullable)taskId + withRequest:(__weak NSURLRequest * _Nullable)req + callback:(_Nullable RCTResponseSenderBlock) callback +{ + RNFetchBlobRequest *request = [[RNFetchBlobRequest alloc] init]; + [request sendRequest:options + contentLength:contentLength + bridge:bridgeRef + taskId:taskId + withRequest:req + taskOperationQueue:taskQueue + callback:callback]; + + @synchronized([RNFetchBlobNetwork class]) { + [requestsTable setObject:request forKey:taskId]; + } +} + + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config { - @synchronized ([RNFetchBlobNetwork class]) { - if(progressTable == nil) - { - progressTable = [[NSMutableDictionary alloc] init]; + if (config) { + @synchronized ([RNFetchBlobNetwork class]) { + [requestsTable objectForKey:taskId].progressConfig = config; } - if (config) [progressTable setValue:config forKey:taskId]; } } + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config { - @synchronized ([RNFetchBlobNetwork class]) { - if(uploadProgressTable == nil) - { - uploadProgressTable = [[NSMutableDictionary alloc] init]; + if (config) { + @synchronized ([RNFetchBlobNetwork class]) { + [requestsTable objectForKey:taskId].uploadProgressConfig = config; } - if (config) [uploadProgressTable setValue:config forKey:taskId]; } } @@ -145,488 +122,37 @@ + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers return mheaders; } -- (NSString *)md5:(NSString *)input { - const char* str = [input UTF8String]; - unsigned char result[CC_MD5_DIGEST_LENGTH]; - CC_MD5(str, (CC_LONG)strlen(str), result); - - NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2]; - for(int i = 0; i 0) - { - defaultConfigObject.timeoutIntervalForRequest = timeout/1000; - } - defaultConfigObject.HTTPMaximumConnectionsPerHost = 10; - session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue]; - if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil) - { - respFile = YES; - - NSString* cacheKey = taskId; - if (key != nil) { - cacheKey = [self md5:key]; - if (cacheKey == nil) { - cacheKey = taskId; - } - - destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; - if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) { - callback(@[[NSNull null], RESP_TYPE_PATH, destPath]); - return; - } - } - - if(path != nil) - destPath = path; - else - destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; - } - else - { - respData = [[NSMutableData alloc] init]; - respFile = NO; - } - - NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; - @synchronized ([RNFetchBlobNetwork class]){ - [taskTable setObject:task forKey:taskId]; - } - [task resume]; - - // network status indicator - if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - -} - // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled + (void) emitExpiredTasks { @synchronized ([RNFetchBlobNetwork class]){ NSEnumerator * emu = [expirationTable keyEnumerator]; NSString * key; - + while((key = [emu nextObject])) { RCTBridge * bridge = [RNFetchBlob getRCTBridge]; id args = @{ @"taskId": key }; [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; - + } - + // clear expired task entries [expirationTable removeAllObjects]; expirationTable = [[NSMapTable alloc] init]; } } -//////////////////////////////////////// -// -// NSURLSession delegates -// -//////////////////////////////////////// - - -#pragma mark NSURLSession delegate methods - - -#pragma mark - Received Response -// set expected content length on response received -- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler -{ - expectedBytes = [response expectedContentLength]; - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; - NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; - NSString * respType = @""; - respStatus = statusCode; - if ([response respondsToSelector:@selector(allHeaderFields)]) - { - NSDictionary *headers = [httpResponse allHeaderFields]; - NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString]; - if(self.isServerPush == NO) - { - self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; - } - if(self.isServerPush) - { - if(partBuffer != nil) - { - [self.bridge.eventDispatcher - sendDeviceEventWithName:EVENT_SERVER_PUSH - body:@{ - @"taskId": taskId, - @"chunk": [partBuffer base64EncodedStringWithOptions:0], - } - ]; - } - partBuffer = [[NSMutableData alloc] init]; - completionHandler(NSURLSessionResponseAllow); - return; - } - if(respCType != nil) - { - NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE]; - if([respCType RNFBContainsString:@"text/"]) - { - respType = @"text"; - } - else if([respCType RNFBContainsString:@"application/json"]) - { - respType = @"json"; - } - // If extra blob content type is not empty, check if response type matches - else if( extraBlobCTypes != nil) { - for(NSString * substr in extraBlobCTypes) - { - if([respCType RNFBContainsString:[substr lowercaseString]]) - { - respType = @"blob"; - respFile = YES; - destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil]; - break; - } - } - } - else - { - respType = @"blob"; - // for XMLHttpRequest, switch response data handling strategy automatically - if([options valueForKey:@"auto"]) { - respFile = YES; - destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; - } - } - } else { - respType = @"text"; - } - -#pragma mark - handling cookies - // # 153 get cookies - if(response.URL != nil) - { - NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL]; - if(cookies != nil && [cookies count] > 0) { - [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil]; - } - } - - [self.bridge.eventDispatcher - sendDeviceEventWithName: EVENT_STATE_CHANGE - body:@{ - @"taskId": taskId, - @"state": @"2", - @"headers": headers, - @"redirects": redirects, - @"respType" : respType, - @"timeout" : @NO, - @"status": [NSNumber numberWithInteger:statusCode] - } - ]; - } - else - NSLog(@"oops"); - - if(respFile == YES) - { - @try{ - NSFileManager * fm = [NSFileManager defaultManager]; - NSString * folder = [destPath stringByDeletingLastPathComponent]; - if(![fm fileExistsAtPath:folder]) - { - [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil]; - } - BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue]; - BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"]; - - appendToExistingFile = !overwrite; - - // For solving #141 append response data if the file already exists - // base on PR#139 @kejinliang - if(appendToExistingFile) - { - destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""]; - } - if (![fm fileExistsAtPath:destPath]) - { - [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil]; - } - writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile]; - [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - [writeStream open]; - } - @catch(NSException * ex) - { - NSLog(@"write file error"); - } - } - - completionHandler(NSURLSessionResponseAllow); -} - - -// download progress handler -- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data -{ - // For #143 handling multipart/x-mixed-replace response - if(self.isServerPush) - { - [partBuffer appendData:data]; - return ; - } - - NSNumber * received = [NSNumber numberWithLong:[data length]]; - receivedBytes += [received longValue]; - NSString * chunkString = @""; - - if(isIncrement == YES) - { - chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - } - - if(respFile == NO) - { - [respData appendData:data]; - } - else - { - [writeStream write:[data bytes] maxLength:[data length]]; - } - - if(expectedBytes == 0) - return; - - RNFetchBlobProgress * pconfig; - - @synchronized ([RNFetchBlobNetwork class]){ - pconfig = [progressTable valueForKey:taskId]; - } - - NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; - - if(pconfig != nil && [pconfig shouldReport:now]) - { - [self.bridge.eventDispatcher - sendDeviceEventWithName:EVENT_PROGRESS - body:@{ - @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%ld", (long) receivedBytes], - @"total": [NSString stringWithFormat:@"%ld", (long) expectedBytes], - @"chunk": chunkString - } - ]; - } -} - -- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error -{ - if([session isEqual:session]) - session = nil; -} - - -- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error -{ - - self.error = error; - NSString * errMsg; - NSString * respStr; - NSString * rnfbRespType; - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - if(error != nil) - { - errMsg = [error localizedDescription]; - } - - if(respFile == YES) - { - [writeStream close]; - rnfbRespType = RESP_TYPE_PATH; - respStr = destPath; - } - // base64 response - else { - // #73 fix unicode data encoding issue : - // when response type is BASE64, we should first try to encode the response data to UTF8 format - // if it turns out not to be `nil` that means the response data contains valid UTF8 string, - // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding. - NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]; - - if(responseFormat == BASE64) - { - rnfbRespType = RESP_TYPE_BASE64; - respStr = [respData base64EncodedStringWithOptions:0]; - } - else if (responseFormat == UTF8) - { - rnfbRespType = RESP_TYPE_UTF8; - respStr = utf8; - } - else - { - if(utf8 != nil) - { - rnfbRespType = RESP_TYPE_UTF8; - respStr = utf8; - } - else - { - rnfbRespType = RESP_TYPE_BASE64; - respStr = [respData base64EncodedStringWithOptions:0]; - } - } - } - - - callback(@[ - errMsg ?: [NSNull null], - rnfbRespType ?: @"", - respStr ?: [NSNull null] - ]); - - @synchronized ([RNFetchBlobNetwork class]) - { - if([taskTable objectForKey:taskId] == nil) - NSLog(@"object released by ARC."); - else - [taskTable removeObjectForKey:taskId]; - [uploadProgressTable removeObjectForKey:taskId]; - [progressTable removeObjectForKey:taskId]; - } - - respData = nil; - receivedBytes = 0; - [session finishTasksAndInvalidate]; - -} - -// upload progress handler -- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite -{ - if(totalBytesExpectedToWrite == 0) - return; - - RNFetchBlobProgress * pconfig; - - @synchronized ([RNFetchBlobNetwork class]) { - pconfig = [uploadProgressTable valueForKey:taskId]; - } - - NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; - if(pconfig != nil && [pconfig shouldReport:now]) { - [self.bridge.eventDispatcher - sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD - body:@{ - @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten], - @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite] - } - ]; - } -} - + (void) cancelRequest:(NSString *)taskId { NSURLSessionDataTask * task; @synchronized ([RNFetchBlobNetwork class]) { - task = [taskTable objectForKey:taskId]; + task = [requestsTable objectForKey:taskId].task; } - if(task != nil && task.state == NSURLSessionTaskStateRunning) + if(task && task.state == NSURLSessionTaskStateRunning) { [task cancel]; -} - - -- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler -{ - BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; - if(!trusty) - { - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } - else - { - completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } -} - - -- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session -{ - NSLog(@"sess done in background"); -} - -- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler -{ - - if(followRedirect) - { - if(request.URL != nil) - [redirects addObject:[request.URL absoluteString]]; - completionHandler(request); - } - else - { - completionHandler(nil); } } diff --git a/ios/RNFetchBlobRequest.h b/ios/RNFetchBlobRequest.h new file mode 100644 index 000000000..a88ff05f6 --- /dev/null +++ b/ios/RNFetchBlobRequest.h @@ -0,0 +1,47 @@ +// +// RNFetchBlobRequest.h +// RNFetchBlob +// +// Created by Artur Chrusciel on 15.01.18. +// Copyright © 2018 wkh237.github.io. All rights reserved. +// + +#import + +#import "RNFetchBlobProgress.h" + +#if __has_include() +#import +#else +#import "RCTBridgeModule.h" +#endif + +#ifndef RNFetchBlobRequest_h +#define RNFetchBlobRequest_h + +@interface RNFetchBlobRequest : NSObject + +@property (nullable, nonatomic) NSString * taskId; +@property (nonatomic) long long expectedBytes; +@property (nonatomic) long long receivedBytes; +@property (nonatomic) BOOL isServerPush; +@property (nullable, nonatomic) NSMutableData * respData; +@property (nullable, strong, nonatomic) RCTResponseSenderBlock callback; +@property (nullable, nonatomic) RCTBridge * bridge; +@property (nullable, nonatomic) NSDictionary * options; +@property (nullable, nonatomic) NSError * error; +@property (nullable, nonatomic) RNFetchBlobProgress *progressConfig; +@property (nullable, nonatomic) RNFetchBlobProgress *uploadProgressConfig; +@property (nullable, nonatomic, weak) NSURLSessionDataTask *task; + +- (void) sendRequest:(NSDictionary * _Nullable )options + contentLength:(long)contentLength + bridge:(RCTBridge * _Nullable)bridgeRef + taskId:(NSString * _Nullable)taskId + withRequest:(NSURLRequest * _Nullable)req + taskOperationQueue:(NSOperationQueue * _Nonnull)operationQueue + callback:(_Nullable RCTResponseSenderBlock) callback; + +@end + +#endif /* RNFetchBlobRequest_h */ diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m new file mode 100644 index 000000000..73b562cc7 --- /dev/null +++ b/ios/RNFetchBlobRequest.m @@ -0,0 +1,495 @@ +// +// RNFetchBlobRequest.m +// RNFetchBlob +// +// Created by Artur Chrusciel on 15.01.18. +// Copyright © 2018 wkh237.github.io. All rights reserved. +// + +#import "RNFetchBlobRequest.h" + +#import "RNFetchBlobFS.h" +#import "RNFetchBlobConst.h" +#import "RNFetchBlobReqBuilder.h" + +#import "IOS7Polyfill.h" +#import + + +typedef NS_ENUM(NSUInteger, ResponseFormat) { + UTF8, + BASE64, + AUTO +}; + +@interface RNFetchBlobRequest () +{ + BOOL respFile; + BOOL isNewPart; + BOOL isIncrement; + NSMutableData * partBuffer; + NSString * destPath; + NSOutputStream * writeStream; + long bodyLength; + NSInteger respStatus; + NSMutableArray * redirects; + ResponseFormat responseFormat; + BOOL followRedirect; + BOOL backgroundTask; +} + +@end + +@implementation RNFetchBlobRequest + +@synthesize taskId; +@synthesize expectedBytes; +@synthesize receivedBytes; +@synthesize respData; +@synthesize callback; +@synthesize bridge; +@synthesize options; +@synthesize error; + + +- (NSString *)md5:(NSString *)input { + const char* str = [input UTF8String]; + unsigned char result[CC_MD5_DIGEST_LENGTH]; + CC_MD5(str, (CC_LONG)strlen(str), result); + + NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2]; + for(int i = 0; i 0) + { + defaultConfigObject.timeoutIntervalForRequest = timeout/1000; + } + defaultConfigObject.HTTPMaximumConnectionsPerHost = 10; + session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue]; + if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil) + { + respFile = YES; + + NSString* cacheKey = taskId; + if (key != nil) { + cacheKey = [self md5:key]; + if (cacheKey == nil) { + cacheKey = taskId; + } + + destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) { + callback(@[[NSNull null], RESP_TYPE_PATH, destPath]); + return; + } + } + + if(path != nil) + destPath = path; + else + destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; + } + else + { + respData = [[NSMutableData alloc] init]; + respFile = NO; + } + + self.task = [session dataTaskWithRequest:req]; + [self.task resume]; + + // network status indicator + if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + +} + +//////////////////////////////////////// +// +// NSURLSession delegates +// +//////////////////////////////////////// + + +#pragma mark NSURLSession delegate methods + + +#pragma mark - Received Response +// set expected content length on response received +- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler +{ + expectedBytes = [response expectedContentLength]; + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + NSString * respType = @""; + respStatus = statusCode; + if ([response respondsToSelector:@selector(allHeaderFields)]) + { + NSDictionary *headers = [httpResponse allHeaderFields]; + NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString]; + if(self.isServerPush == NO) + { + self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; + } + if(self.isServerPush) + { + if(partBuffer != nil) + { + [self.bridge.eventDispatcher + sendDeviceEventWithName:EVENT_SERVER_PUSH + body:@{ + @"taskId": taskId, + @"chunk": [partBuffer base64EncodedStringWithOptions:0], + } + ]; + } + partBuffer = [[NSMutableData alloc] init]; + completionHandler(NSURLSessionResponseAllow); + return; + } + if(respCType != nil) + { + NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE]; + if([respCType RNFBContainsString:@"text/"]) + { + respType = @"text"; + } + else if([respCType RNFBContainsString:@"application/json"]) + { + respType = @"json"; + } + // If extra blob content type is not empty, check if response type matches + else if( extraBlobCTypes != nil) { + for(NSString * substr in extraBlobCTypes) + { + if([respCType RNFBContainsString:[substr lowercaseString]]) + { + respType = @"blob"; + respFile = YES; + destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil]; + break; + } + } + } + else + { + respType = @"blob"; + // for XMLHttpRequest, switch response data handling strategy automatically + if([options valueForKey:@"auto"]) { + respFile = YES; + destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; + } + } + } else { + respType = @"text"; + } + +#pragma mark - handling cookies + // # 153 get cookies + if(response.URL != nil) + { + NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL]; + if(cookies != nil && [cookies count] > 0) { + [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil]; + } + } + + [self.bridge.eventDispatcher + sendDeviceEventWithName: EVENT_STATE_CHANGE + body:@{ + @"taskId": taskId, + @"state": @"2", + @"headers": headers, + @"redirects": redirects, + @"respType" : respType, + @"timeout" : @NO, + @"status": [NSNumber numberWithInteger:statusCode] + } + ]; + } + else + NSLog(@"oops"); + + if(respFile == YES) + { + @try{ + NSFileManager * fm = [NSFileManager defaultManager]; + NSString * folder = [destPath stringByDeletingLastPathComponent]; + if(![fm fileExistsAtPath:folder]) + { + [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil]; + } + BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue]; + BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"]; + + appendToExistingFile = !overwrite; + + // For solving #141 append response data if the file already exists + // base on PR#139 @kejinliang + if(appendToExistingFile) + { + destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""]; + } + if (![fm fileExistsAtPath:destPath]) + { + [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil]; + } + writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile]; + [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [writeStream open]; + } + @catch(NSException * ex) + { + NSLog(@"write file error"); + } + } + + completionHandler(NSURLSessionResponseAllow); +} + + +// download progress handler +- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data +{ + // For #143 handling multipart/x-mixed-replace response + if(self.isServerPush) + { + [partBuffer appendData:data]; + return ; + } + + NSNumber * received = [NSNumber numberWithLong:[data length]]; + receivedBytes += [received longValue]; + NSString * chunkString = @""; + + if(isIncrement == YES) + { + chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } + + if(respFile == NO) + { + [respData appendData:data]; + } + else + { + [writeStream write:[data bytes] maxLength:[data length]]; + } + + if(expectedBytes == 0) + return; + + NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; + + if([self.progressConfig shouldReport:now]) + { + [self.bridge.eventDispatcher + sendDeviceEventWithName:EVENT_PROGRESS + body:@{ + @"taskId": taskId, + @"written": [NSString stringWithFormat:@"%ld", (long) receivedBytes], + @"total": [NSString stringWithFormat:@"%ld", (long) expectedBytes], + @"chunk": chunkString + } + ]; + } +} + +- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error +{ + if([session isEqual:session]) + session = nil; +} + + +- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error +{ + + self.error = error; + NSString * errMsg; + NSString * respStr; + NSString * rnfbRespType; + + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + + if(error != nil) + { + errMsg = [error localizedDescription]; + } + + if(respFile == YES) + { + [writeStream close]; + rnfbRespType = RESP_TYPE_PATH; + respStr = destPath; + } + // base64 response + else { + // #73 fix unicode data encoding issue : + // when response type is BASE64, we should first try to encode the response data to UTF8 format + // if it turns out not to be `nil` that means the response data contains valid UTF8 string, + // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding. + NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]; + + if(responseFormat == BASE64) + { + rnfbRespType = RESP_TYPE_BASE64; + respStr = [respData base64EncodedStringWithOptions:0]; + } + else if (responseFormat == UTF8) + { + rnfbRespType = RESP_TYPE_UTF8; + respStr = utf8; + } + else + { + if(utf8 != nil) + { + rnfbRespType = RESP_TYPE_UTF8; + respStr = utf8; + } + else + { + rnfbRespType = RESP_TYPE_BASE64; + respStr = [respData base64EncodedStringWithOptions:0]; + } + } + } + + + callback(@[ + errMsg ?: [NSNull null], + rnfbRespType ?: @"", + respStr ?: [NSNull null] + ]); + /* + @synchronized ([RNFetchBlobNetwork class]) + { + if([taskTable objectForKey:taskId] == nil) + NSLog(@"object released by ARC."); + else + [taskTable removeObjectForKey:taskId]; + [uploadProgressTable removeObjectForKey:taskId]; + [progressTable removeObjectForKey:taskId]; + }*/ + + respData = nil; + receivedBytes = 0; + [session finishTasksAndInvalidate]; + +} + +// upload progress handler +- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite +{ + if(totalBytesExpectedToWrite == 0) + return; + + NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; + + if([self.uploadProgressConfig shouldReport:now]) { + [self.bridge.eventDispatcher + sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD + body:@{ + @"taskId": taskId, + @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten], + @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite] + } + ]; + } +} + + +- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler +{ + BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; + if(!trusty) + { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + } + else + { + completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + } +} + + +- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session +{ + NSLog(@"sess done in background"); +} + +- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler +{ + + if(followRedirect) + { + if(request.URL != nil) + [redirects addObject:[request.URL absoluteString]]; + completionHandler(request); + } + else + { + completionHandler(nil); + } +} + + +@end From db688751468496ffb4592ec000787597f96184ae Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 08:22:00 +0100 Subject: [PATCH 13/20] Shared instance used --- ios/RNFetchBlobNetwork.h | 5 ++++- ios/RNFetchBlobNetwork.m | 23 ++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index b93541267..743d51e03 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -9,6 +9,7 @@ #import #import "RNFetchBlobProgress.h" #import "RNFetchBlobFS.h" +#import "RNFetchBlobRequest.h" #if __has_include() #import @@ -22,8 +23,10 @@ @interface RNFetchBlobNetwork : NSObject +@property(nonnull, nonatomic) NSOperationQueue *taskQueue; +@property(nonnull, nonatomic) NSMapTable * requestsTable; -+ (_Nullable instancetype)sharedInstance; ++ (RNFetchBlobNetwork* _Nullable)sharedInstance; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; + (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 5b3a32966..1b4181959 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -13,7 +13,6 @@ #import "RNFetchBlob.h" #import "RNFetchBlobConst.h" #import "RNFetchBlobProgress.h" -#import "RNFetchBlobRequest.h" #if __has_include() #import @@ -46,23 +45,21 @@ static void initialize_tables() { @implementation RNFetchBlobNetwork -NSOperationQueue *taskQueue; -NSMapTable * requestsTable; - (id)init { self = [super init]; if (self) { - requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory]; + self.requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory]; - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.qualityOfService = NSQualityOfServiceUtility; - taskQueue.maxConcurrentOperationCount = 10; + self.taskQueue = [[NSOperationQueue alloc] init]; + self.taskQueue.qualityOfService = NSQualityOfServiceUtility; + self.taskQueue.maxConcurrentOperationCount = 10; } return self; } -+ (instancetype)sharedInstance { ++ (RNFetchBlobNetwork* _Nullable)sharedInstance { static id _sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -85,11 +82,11 @@ + (void) sendRequest:(__weak NSDictionary * _Nullable )options bridge:bridgeRef taskId:taskId withRequest:req - taskOperationQueue:taskQueue + taskOperationQueue:[self sharedInstance].taskQueue callback:callback]; @synchronized([RNFetchBlobNetwork class]) { - [requestsTable setObject:request forKey:taskId]; + [[self sharedInstance].requestsTable setObject:request forKey:taskId]; } } @@ -97,7 +94,7 @@ + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *) { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [requestsTable objectForKey:taskId].progressConfig = config; + [[self sharedInstance].requestsTable objectForKey:taskId].progressConfig = config; } } } @@ -106,7 +103,7 @@ + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *) { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [requestsTable objectForKey:taskId].uploadProgressConfig = config; + [[self sharedInstance].requestsTable objectForKey:taskId].uploadProgressConfig = config; } } } @@ -148,7 +145,7 @@ + (void) cancelRequest:(NSString *)taskId NSURLSessionDataTask * task; @synchronized ([RNFetchBlobNetwork class]) { - task = [requestsTable objectForKey:taskId].task; + task = [[self sharedInstance].requestsTable objectForKey:taskId].task; } if(task && task.state == NSURLSessionTaskStateRunning) { From ad657634283173a7ae3ce19e3fceb5dfa0b85910 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 08:31:49 +0100 Subject: [PATCH 14/20] Proper usage of shared instance --- ios/RNFetchBlob/RNFetchBlob.m | 30 ++++++++++++------------- ios/RNFetchBlobNetwork.h | 14 ++++++++---- ios/RNFetchBlobNetwork.m | 42 +++++++++++++++++------------------ 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 6e8141f9e..8b92431e9 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -96,12 +96,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - [RNFetchBlobNetwork sendRequest:options - contentLength:bodyLength - bridge:self.bridge - taskId:taskId - withRequest:req - callback:callback]; + [[RNFetchBlobNetwork sharedInstance] sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; @@ -132,12 +132,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - [RNFetchBlobNetwork sendRequest:options - contentLength:bodyLength - bridge:self.bridge - taskId:taskId - withRequest:req - callback:callback]; + [[RNFetchBlobNetwork sharedInstance] sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; } @@ -496,7 +496,7 @@ - (NSDictionary *)constantsToExport #pragma mark - net.cancelRequest RCT_EXPORT_METHOD(cancelRequest:(NSString *)taskId callback:(RCTResponseSenderBlock)callback) { - [RNFetchBlobNetwork cancelRequest:taskId]; + [[RNFetchBlobNetwork sharedInstance] cancelRequest:taskId]; callback(@[[NSNull null], taskId]); } @@ -506,14 +506,14 @@ - (NSDictionary *)constantsToExport { RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Download interval:interval count:count]; - [RNFetchBlobNetwork enableProgressReport:taskId config:cfg]; + [[RNFetchBlobNetwork sharedInstance] enableProgressReport:taskId config:cfg]; } #pragma mark - net.enableUploadProgressReport RCT_EXPORT_METHOD(enableUploadProgressReport:(NSString *)taskId interval:(nonnull NSNumber*)interval count:(nonnull NSNumber*)count) { RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Upload interval:interval count:count]; - [RNFetchBlobNetwork enableUploadProgress:taskId config:cfg]; + [[RNFetchBlobNetwork sharedInstance] enableUploadProgress:taskId config:cfg]; } #pragma mark - fs.slice diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index 743d51e03..ecee93761 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -28,13 +28,19 @@ + (RNFetchBlobNetwork* _Nullable)sharedInstance; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; -+ (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; -+ (void) enableProgressReport:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; -+ (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; - (nullable id) init; -+ (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; +- (void) sendRequest:(NSDictionary * _Nullable )options + contentLength:(long)contentLength + bridge:(RCTBridge * _Nullable)bridgeRef + taskId:(NSString * _Nullable)taskId + withRequest:(NSURLRequest * _Nullable)req + callback:(_Nullable RCTResponseSenderBlock) callback; +- (void) cancelRequest:(NSString * _Nonnull)taskId; +- (void) enableProgressReport:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; +- (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; + @end diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 1b4181959..1cd9e93b3 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -69,7 +69,7 @@ + (RNFetchBlobNetwork* _Nullable)sharedInstance { return _sharedInstance; } -+ (void) sendRequest:(__weak NSDictionary * _Nullable )options +- (void) sendRequest:(__weak NSDictionary * _Nullable )options contentLength:(long) contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId @@ -82,32 +82,45 @@ + (void) sendRequest:(__weak NSDictionary * _Nullable )options bridge:bridgeRef taskId:taskId withRequest:req - taskOperationQueue:[self sharedInstance].taskQueue + taskOperationQueue:self.taskQueue callback:callback]; @synchronized([RNFetchBlobNetwork class]) { - [[self sharedInstance].requestsTable setObject:request forKey:taskId]; + [self.requestsTable setObject:request forKey:taskId]; } } -+ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config +- (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [[self sharedInstance].requestsTable objectForKey:taskId].progressConfig = config; + [self.requestsTable objectForKey:taskId].progressConfig = config; } } } -+ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config +- (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [[self sharedInstance].requestsTable objectForKey:taskId].uploadProgressConfig = config; + [self.requestsTable objectForKey:taskId].uploadProgressConfig = config; } } } +- (void) cancelRequest:(NSString *)taskId +{ + NSURLSessionDataTask * task; + + @synchronized ([RNFetchBlobNetwork class]) { + task = [self.requestsTable objectForKey:taskId].task; + } + + if(task && task.state == NSURLSessionTaskStateRunning) { + [task cancel]; + } +} + // removing case from headers + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers { @@ -115,7 +128,7 @@ + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers for(NSString * key in headers) { [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]]; } - + return mheaders; } @@ -140,17 +153,4 @@ + (void) emitExpiredTasks } } -+ (void) cancelRequest:(NSString *)taskId -{ - NSURLSessionDataTask * task; - - @synchronized ([RNFetchBlobNetwork class]) { - task = [[self sharedInstance].requestsTable objectForKey:taskId].task; - } - - if(task && task.state == NSURLSessionTaskStateRunning) { - [task cancel]; - } -} - @end From 1d19f984ea4464298334587e4b57eb499489f509 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 08:33:47 +0100 Subject: [PATCH 15/20] Commented code removed --- ios/RNFetchBlobRequest.m | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m index 73b562cc7..8ebdd8d19 100644 --- a/ios/RNFetchBlobRequest.m +++ b/ios/RNFetchBlobRequest.m @@ -419,16 +419,6 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom rnfbRespType ?: @"", respStr ?: [NSNull null] ]); - /* - @synchronized ([RNFetchBlobNetwork class]) - { - if([taskTable objectForKey:taskId] == nil) - NSLog(@"object released by ARC."); - else - [taskTable removeObjectForKey:taskId]; - [uploadProgressTable removeObjectForKey:taskId]; - [progressTable removeObjectForKey:taskId]; - }*/ respData = nil; receivedBytes = 0; From d4b465a366603a90b50a3a46323183e7e6795e7d Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 09:06:57 +0100 Subject: [PATCH 16/20] Fixed header import defines --- ios/RNFetchBlobNetwork.h | 6 +++--- ios/RNFetchBlobRequest.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index ecee93761..1512712af 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -6,6 +6,9 @@ // Copyright © 2016 wkh237. All rights reserved. // +#ifndef RNFetchBlobNetwork_h +#define RNFetchBlobNetwork_h + #import #import "RNFetchBlobProgress.h" #import "RNFetchBlobFS.h" @@ -17,9 +20,6 @@ #import "RCTBridgeModule.h" #endif -#ifndef RNFetchBlobNetwork_h -#define RNFetchBlobNetwork_h - @interface RNFetchBlobNetwork : NSObject diff --git a/ios/RNFetchBlobRequest.h b/ios/RNFetchBlobRequest.h index a88ff05f6..b550ac22e 100644 --- a/ios/RNFetchBlobRequest.h +++ b/ios/RNFetchBlobRequest.h @@ -6,6 +6,9 @@ // Copyright © 2018 wkh237.github.io. All rights reserved. // +#ifndef RNFetchBlobRequest_h +#define RNFetchBlobRequest_h + #import #import "RNFetchBlobProgress.h" @@ -16,9 +19,6 @@ #import "RCTBridgeModule.h" #endif -#ifndef RNFetchBlobRequest_h -#define RNFetchBlobRequest_h - @interface RNFetchBlobRequest : NSObject @property (nullable, nonatomic) NSString * taskId; From 57102f4d1e9b2e3c59739acadb7b870066eaa903 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 12:50:18 +0100 Subject: [PATCH 17/20] Some code guidelines for network classes --- ios/RNFetchBlobNetwork.m | 10 +- ios/RNFetchBlobRequest.m | 200 ++++++++++++++++++--------------------- 2 files changed, 97 insertions(+), 113 deletions(-) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 1cd9e93b3..4531bf35e 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -36,8 +36,7 @@ __attribute__((constructor)) static void initialize_tables() { - if(expirationTable == nil) - { + if (expirationTable == nil) { expirationTable = [[NSMapTable alloc] init]; } } @@ -62,6 +61,7 @@ - (id)init { + (RNFetchBlobNetwork* _Nullable)sharedInstance { static id _sharedInstance = nil; static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ _sharedInstance = [[self alloc] init]; }); @@ -116,7 +116,7 @@ - (void) cancelRequest:(NSString *)taskId task = [self.requestsTable objectForKey:taskId].task; } - if(task && task.state == NSURLSessionTaskStateRunning) { + if (task && task.state == NSURLSessionTaskStateRunning) { [task cancel]; } } @@ -125,7 +125,7 @@ - (void) cancelRequest:(NSString *)taskId + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers { NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init]; - for(NSString * key in headers) { + for (NSString * key in headers) { [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]]; } @@ -139,7 +139,7 @@ + (void) emitExpiredTasks NSEnumerator * emu = [expirationTable keyEnumerator]; NSString * key; - while((key = [emu nextObject])) + while ((key = [emu nextObject])) { RCTBridge * bridge = [RNFetchBlob getRCTBridge]; id args = @{ @"taskId": key }; diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m index 8ebdd8d19..10b5e660f 100644 --- a/ios/RNFetchBlobRequest.m +++ b/ios/RNFetchBlobRequest.m @@ -58,7 +58,7 @@ - (NSString *)md5:(NSString *)input { CC_MD5(str, (CC_LONG)strlen(str), result); NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2]; - for(int i = 0; i 0) - { + + if (timeout > 0) { defaultConfigObject.timeoutIntervalForRequest = timeout/1000; } + defaultConfigObject.HTTPMaximumConnectionsPerHost = 10; session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue]; - if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil) - { + + if (path || [self.options valueForKey:CONFIG_USE_TEMP]) { respFile = YES; NSString* cacheKey = taskId; - if (key != nil) { + if (key) { cacheKey = [self md5:key]; - if (cacheKey == nil) { + + if (!cacheKey) { cacheKey = taskId; } destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) { callback(@[[NSNull null], RESP_TYPE_PATH, destPath]); + return; } } - if(path != nil) + if (path) { destPath = path; - else + } else { destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; - } - else - { + } + } else { respData = [[NSMutableData alloc] init]; respFile = NO; } @@ -154,9 +161,9 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options [self.task resume]; // network status indicator - if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) + if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - + } } //////////////////////////////////////// @@ -179,18 +186,14 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; NSString * respType = @""; respStatus = statusCode; + if ([response respondsToSelector:@selector(allHeaderFields)]) { NSDictionary *headers = [httpResponse allHeaderFields]; NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString]; - if(self.isServerPush == NO) - { - self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; - } - if(self.isServerPush) - { - if(partBuffer != nil) - { + + if (self.isServerPush) { + if (partBuffer) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_SERVER_PUSH body:@{ @@ -199,39 +202,37 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat } ]; } + partBuffer = [[NSMutableData alloc] init]; completionHandler(NSURLSessionResponseAllow); + return; + } else { + self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; } - if(respCType != nil) + + if(respCType) { NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE]; - if([respCType RNFBContainsString:@"text/"]) - { + + if ([respCType RNFBContainsString:@"text/"]) { respType = @"text"; - } - else if([respCType RNFBContainsString:@"application/json"]) - { + } else if ([respCType RNFBContainsString:@"application/json"]) { respType = @"json"; - } - // If extra blob content type is not empty, check if response type matches - else if( extraBlobCTypes != nil) { - for(NSString * substr in extraBlobCTypes) - { - if([respCType RNFBContainsString:[substr lowercaseString]]) - { + } else if(extraBlobCTypes) { // If extra blob content type is not empty, check if response type matches + for (NSString * substr in extraBlobCTypes) { + if ([respCType RNFBContainsString:[substr lowercaseString]]) { respType = @"blob"; respFile = YES; destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil]; break; } } - } - else - { + } else { respType = @"blob"; + // for XMLHttpRequest, switch response data handling strategy automatically - if([options valueForKey:@"auto"]) { + if ([options valueForKey:@"auto"]) { respFile = YES; destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; } @@ -242,11 +243,10 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat #pragma mark - handling cookies // # 153 get cookies - if(response.URL != nil) - { + if (response.URL) { NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage]; NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL]; - if(cookies != nil && [cookies count] > 0) { + if (cookies.count) { [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil]; } } @@ -263,19 +263,21 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat @"status": [NSNumber numberWithInteger:statusCode] } ]; - } - else + } else { NSLog(@"oops"); + } - if(respFile == YES) + if (respFile) { @try{ NSFileManager * fm = [NSFileManager defaultManager]; NSString * folder = [destPath stringByDeletingLastPathComponent]; - if(![fm fileExistsAtPath:folder]) - { + + if (![fm fileExistsAtPath:folder]) { [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil]; } + + // if not set overwrite in options, defaults to TRUE BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue]; BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"]; @@ -283,14 +285,14 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat // For solving #141 append response data if the file already exists // base on PR#139 @kejinliang - if(appendToExistingFile) - { + if (appendToExistingFile) { destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""]; } - if (![fm fileExistsAtPath:destPath]) - { + + if (![fm fileExistsAtPath:destPath]) { [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil]; } + writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile]; [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [writeStream open]; @@ -309,9 +311,10 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { // For #143 handling multipart/x-mixed-replace response - if(self.isServerPush) + if (self.isServerPush) { [partBuffer appendData:data]; + return ; } @@ -319,27 +322,23 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat receivedBytes += [received longValue]; NSString * chunkString = @""; - if(isIncrement == YES) - { + if (isIncrement) { chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } - if(respFile == NO) - { - [respData appendData:data]; - } - else - { + if (respFile) { [writeStream write:[data bytes] maxLength:[data length]]; + } else { + [respData appendData:data]; } - if(expectedBytes == 0) + if (expectedBytes == 0) { return; + } NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; - if([self.progressConfig shouldReport:now]) - { + if ([self.progressConfig shouldReport:now]) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_PROGRESS body:@{ @@ -354,8 +353,9 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error { - if([session isEqual:session]) + if ([session isEqual:session]) { session = nil; + } } @@ -369,44 +369,32 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - if(error != nil) - { + if (error) { errMsg = [error localizedDescription]; } - if(respFile == YES) - { + if (respFile) { [writeStream close]; rnfbRespType = RESP_TYPE_PATH; respStr = destPath; - } - // base64 response - else { + } else { // base64 response // #73 fix unicode data encoding issue : // when response type is BASE64, we should first try to encode the response data to UTF8 format // if it turns out not to be `nil` that means the response data contains valid UTF8 string, // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding. NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]; - if(responseFormat == BASE64) - { + if (responseFormat == BASE64) { rnfbRespType = RESP_TYPE_BASE64; respStr = [respData base64EncodedStringWithOptions:0]; - } - else if (responseFormat == UTF8) - { + } else if (responseFormat == UTF8) { rnfbRespType = RESP_TYPE_UTF8; respStr = utf8; - } - else - { - if(utf8 != nil) - { + } else { + if (utf8) { rnfbRespType = RESP_TYPE_UTF8; respStr = utf8; - } - else - { + } else { rnfbRespType = RESP_TYPE_BASE64; respStr = [respData base64EncodedStringWithOptions:0]; } @@ -429,12 +417,13 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom // upload progress handler - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite { - if(totalBytesExpectedToWrite == 0) + if (totalBytesExpectedToWrite == 0) { return; + } NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; - if([self.uploadProgressConfig shouldReport:now]) { + if ([self.uploadProgressConfig shouldReport:now]) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD body:@{ @@ -449,14 +438,10 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler { - BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; - if(!trusty) - { - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } - else - { + if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) { completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + } else { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } } @@ -469,14 +454,13 @@ - (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)sessio - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler { - if(followRedirect) - { - if(request.URL != nil) + if (followRedirect) { + if (request.URL) { [redirects addObject:[request.URL absoluteString]]; + } + completionHandler(request); - } - else - { + } else { completionHandler(nil); } } From 89d9b7a5a7152b2ec19acb35d03461702fb2fc92 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 13:24:14 +0100 Subject: [PATCH 18/20] Network activity calls on main thread --- ios/RNFetchBlobRequest.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m index 10b5e660f..4b2ca9a1f 100644 --- a/ios/RNFetchBlobRequest.m +++ b/ios/RNFetchBlobRequest.m @@ -162,7 +162,9 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options // network status indicator if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) { - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + }); } } @@ -367,7 +369,9 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom NSString * respStr; NSString * rnfbRespType; - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + }); if (error) { errMsg = [error localizedDescription]; From a46bf8180d76131eafe84ab44e0436322073820c Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 13:36:09 +0100 Subject: [PATCH 19/20] task cancelled error message --- ios/RNFetchBlobRequest.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m index 4b2ca9a1f..c77668ce8 100644 --- a/ios/RNFetchBlobRequest.m +++ b/ios/RNFetchBlobRequest.m @@ -374,7 +374,11 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom }); if (error) { - errMsg = [error localizedDescription]; + if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled) { + errMsg = @"task cancelled"; + } else { + errMsg = [error localizedDescription]; + } } if (respFile) { From fb63db99e0beccda99a2ec416a0f8b0ee809d750 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 27 Mar 2018 10:41:22 +0200 Subject: [PATCH 20/20] createFile methods with promises --- .../java/com/RNFetchBlob/RNFetchBlob.java | 8 ++++---- .../java/com/RNFetchBlob/RNFetchBlobFS.java | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java index f5597d3ad..fae1f7d15 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java @@ -85,11 +85,11 @@ public Map getConstants() { } @ReactMethod - public void createFile(final String path, final String content, final String encode, final Callback callback) { + public void createFile(final String path, final String content, final String encode, final Promise promise) { threadPool.execute(new Runnable() { @Override public void run() { - RNFetchBlobFS.createFile(path, content, encode, callback); + RNFetchBlobFS.createFile(path, content, encode, promise); } }); } @@ -128,11 +128,11 @@ public void onHostDestroy() { } @ReactMethod - public void createFileASCII(final String path, final ReadableArray dataArray, final Callback callback) { + public void createFileASCII(final String path, final ReadableArray dataArray, final Promise promise) { threadPool.execute(new Runnable() { @Override public void run() { - RNFetchBlobFS.createFileASCII(path, dataArray, callback); + RNFetchBlobFS.createFileASCII(path, dataArray, promise); } }); } diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index 0959c4f9f..614aced0f 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -727,7 +727,7 @@ public void onScanCompleted(String s, Uri uri) { * @param encoding Encoding of initial data. * @param callback RCT bridge callback. */ - static void createFile(String path, String data, String encoding, Callback callback) { + static void createFile(String path, String data, String encoding, Promise promise) { try { File dest = new File(path); boolean created = dest.createNewFile(); @@ -735,7 +735,7 @@ static void createFile(String path, String data, String encoding, Callback callb String orgPath = data.replace(RNFetchBlobConst.FILE_PREFIX, ""); File src = new File(orgPath); if(!src.exists()) { - callback.invoke("RNfetchBlob writeFileError", "source file : " + data + "not exists"); + promise.reject("RNfetchBlob writeFileError", "source file : " + data + "not exists"); return ; } FileInputStream fin = new FileInputStream(src); @@ -751,15 +751,15 @@ static void createFile(String path, String data, String encoding, Callback callb } else { if (!created) { - callback.invoke("create file error: failed to create file at path `" + path + "` for its parent path may not exists, or the file already exists. If you intended to overwrite the existing file use fs.writeFile instead."); + Promise.reject("create file error: failed to create file at path `" + path + "` for its parent path may not exists, or the file already exists. If you intended to overwrite the existing file use fs.writeFile instead."); return; } OutputStream ostream = new FileOutputStream(dest); ostream.write(RNFetchBlobFS.stringToBytes(data, encoding)); } - callback.invoke(null, path); + promise.resolve(path); } catch(Exception err) { - callback.invoke(err.getLocalizedMessage()); + promise.reject(err.getLocalizedMessage()); } } @@ -769,16 +769,16 @@ static void createFile(String path, String data, String encoding, Callback callb * @param data Content of new file * @param callback JS context callback */ - static void createFileASCII(String path, ReadableArray data, Callback callback) { + static void createFileASCII(String path, ReadableArray data, Promise promise) { try { File dest = new File(path); if(dest.exists()) { - callback.invoke("create file error: failed to create file at path `" + path + "`, file already exists."); + promise.reject("create file error: failed to create file at path `" + path + "`, file already exists."); return; } boolean created = dest.createNewFile(); if(!created) { - callback.invoke("create file error: failed to create file at path `" + path + "` for its parent path may not exists"); + promise.reject("create file error: failed to create file at path `" + path + "` for its parent path may not exists"); return; } OutputStream ostream = new FileOutputStream(dest); @@ -788,9 +788,9 @@ static void createFileASCII(String path, ReadableArray data, Callback callback) } ostream.write(chunk); chunk = null; - callback.invoke(null, path); + promise.resolve(path); } catch(Exception err) { - callback.invoke(err.getLocalizedMessage()); + promise.reject(err.getLocalizedMessage()); } }