Skip to content

Commit 5c9c4ab

Browse files
committed
Revert "Revert "Merge pull request joltup#198 from capriza/master""
This reverts commit ca4bf4e.
1 parent 12fc43a commit 5c9c4ab

File tree

4 files changed

+109
-60
lines changed

4 files changed

+109
-60
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ A project committed to making file access and data transfer easier and more effi
2929
* [Multipart/form upload](#user-content-multipartform-data-example--post-form-data-with-file-and-data)
3030
* [Upload/Download progress](#user-content-uploaddownload-progress)
3131
* [Cancel HTTP request](#user-content-cancel-request)
32+
* [iOS Background Uploading](#user-content-ios-background-uploading)
3233
* [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support)
3334
* [Self-Signed SSL Server](#user-content-self-signed-ssl-server)
3435
* [Transfer Encoding](#user-content-transfer-encoding)
@@ -475,6 +476,34 @@ If you have existing code that uses `whatwg-fetch`(the official **fetch**), it's
475476

476477
[See document and examples](https://github.com/joltup/rn-fetch-blob/wiki/Fetch-API#fetch-replacement)
477478

479+
### iOS Background Uploading
480+
Normally, iOS interrupts network connections when an app is moved to the background, and will throw an error 'Lost connection to background transfer service' when the app resumes. To continue the upload of large files even when the app is in the background, you will need to enable IOSUploadTask options.
481+
482+
First add the following property to your AppDelegate.h:
483+
```
484+
@property (nonatomic, copy) void(^backgroundTransferCompletionHandler)();
485+
```
486+
Then add the following to your AppDelegate.m:
487+
```
488+
- (void)application:(UIApplication *)application
489+
handleEventsForBackgroundURLSession:(NSString *)identifier
490+
completionHandler:(void (^)(void))completionHandler {
491+
self.backgroundTransferCompletionHandler = completionHandler;
492+
}
493+
```
494+
The following example shows how to upload a file in the background:
495+
```js
496+
RNFetchBlob
497+
.config({
498+
IOSBackgroundTask: true, // required for both upload
499+
IOSUploadTask: true, // Use instead of IOSDownloadTask if uploading
500+
uploadFilePath : 'file://' + filePath
501+
})
502+
.fetch('PUT', url, {
503+
'Content-Type': mediaType
504+
}, RNFetchBlob.wrap(filePath));
505+
```
506+
478507
### Android Media Scanner, and Download Manager Support
479508

480509
If you want to make a file in `External Storage` becomes visible in Picture, Downloads, or other built-in apps, you will have to use `Media Scanner` or `Download Manager`.

ios/RNFetchBlobNetwork.m

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ - (id)init {
6363
+ (RNFetchBlobNetwork* _Nullable)sharedInstance {
6464
static id _sharedInstance = nil;
6565
static dispatch_once_t onceToken;
66-
66+
6767
dispatch_once(&onceToken, ^{
6868
_sharedInstance = [[self alloc] init];
6969
});
@@ -135,14 +135,8 @@ - (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)
135135

136136
- (void) cancelRequest:(NSString *)taskId
137137
{
138-
NSURLSessionDataTask * task;
139-
140138
@synchronized ([RNFetchBlobNetwork class]) {
141-
task = [self.requestsTable objectForKey:taskId].task;
142-
}
143-
144-
if (task && task.state == NSURLSessionTaskStateRunning) {
145-
[task cancel];
139+
[[self.requestsTable objectForKey:taskId] cancelRequest:taskId];
146140
}
147141
}
148142

ios/RNFetchBlobRequest.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
@property (nullable, nonatomic) NSError * error;
3434
@property (nullable, nonatomic) RNFetchBlobProgress *progressConfig;
3535
@property (nullable, nonatomic) RNFetchBlobProgress *uploadProgressConfig;
36-
@property (nullable, nonatomic, weak) NSURLSessionDataTask *task;
3736
@property (nonatomic, retain) KeyChainDataSource *keyChain;
37+
@property (nonatomic, strong) __block NSURLSession * session;
3838

3939
- (void) sendRequest:(NSDictionary * _Nullable )options
4040
contentLength:(long)contentLength
@@ -44,6 +44,8 @@
4444
taskOperationQueue:(NSOperationQueue * _Nonnull)operationQueue
4545
callback:(_Nullable RCTResponseSenderBlock) callback;
4646

47+
- (void) cancelRequest:(NSString *)taskId;
48+
4749
@end
4850

4951
#endif /* RNFetchBlobRequest_h */

ios/RNFetchBlobRequest.m

Lines changed: 75 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,24 @@
1111
#import "RNFetchBlobFS.h"
1212
#import "RNFetchBlobConst.h"
1313
#import "RNFetchBlobReqBuilder.h"
14+
#if __has_include(<React/RCTLog.h>)
15+
#import <React/RCTLog.h>
16+
#else
17+
#import "RCTLog.h"
18+
#endif
1419

1520
#import "IOS7Polyfill.h"
1621
#import <CommonCrypto/CommonDigest.h>
1722

23+
NSMapTable * taskTable;
24+
25+
__attribute__((constructor))
26+
static void initialize_tables() {
27+
if(taskTable == nil)
28+
{
29+
taskTable = [[NSMapTable alloc] init];
30+
}
31+
}
1832

1933
typedef NS_ENUM(NSUInteger, ResponseFormat) {
2034
UTF8,
@@ -36,6 +50,7 @@ @interface RNFetchBlobRequest ()
3650
ResponseFormat responseFormat;
3751
BOOL followRedirect;
3852
BOOL backgroundTask;
53+
BOOL uploadTask;
3954
}
4055

4156
@end
@@ -83,8 +98,18 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
8398
if (!self.keyChain) {
8499
self.keyChain = [[KeyChainDataSource alloc] initWithMode:KSM_Identities];
85100
}
86-
101+
87102
backgroundTask = [[options valueForKey:@"IOSBackgroundTask"] boolValue];
103+
uploadTask = [options valueForKey:@"IOSUploadTask"] == nil ? NO : [[options valueForKey:@"IOSUploadTask"] boolValue];
104+
105+
NSString * filepath = [options valueForKey:@"uploadFilePath"];
106+
107+
if (uploadTask && ![[NSFileManager defaultManager] fileExistsAtPath:[NSURL URLWithString:filepath].path]) {
108+
RCTLog(@"[RNFetchBlobRequest] sendRequest uploadTask file doesn't exist %@", filepath);
109+
callback(@[@"uploadTask file doesn't exist", @"", [NSNull null]]);
110+
return;
111+
}
112+
88113
// when followRedirect not set in options, defaults to TRUE
89114
followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue];
90115
isIncrement = [[options valueForKey:@"increment"] boolValue];
@@ -107,7 +132,6 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
107132

108133
NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
109134
NSString * key = [self.options valueForKey:CONFIG_KEY];
110-
NSURLSession * session;
111135

112136
bodyLength = contentLength;
113137

@@ -120,6 +144,7 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
120144
defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
121145
}
122146

147+
123148
// request timeout, -1 if not set in options
124149
float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
125150

@@ -135,8 +160,8 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
135160
defaultConfigObject.timeoutIntervalForResource = 60;
136161
}
137162
}
138-
session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue];
139-
163+
_session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue];
164+
140165
if (path || [self.options valueForKey:CONFIG_USE_TEMP]) {
141166
respFile = YES;
142167

@@ -167,8 +192,19 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
167192
respFile = NO;
168193
}
169194

170-
self.task = [session dataTaskWithRequest:req];
171-
[self.task resume];
195+
__block NSURLSessionTask * task;
196+
197+
if(uploadTask)
198+
{
199+
task = [_session uploadTaskWithRequest:req fromFile:[NSURL URLWithString:filepath]];
200+
}
201+
else
202+
{
203+
task = [_session dataTaskWithRequest:req];
204+
}
205+
206+
[taskTable setObject:task forKey:taskId];
207+
[task resume];
172208

173209
// network status indicator
174210
if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
@@ -192,6 +228,7 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
192228
// set expected content length on response received
193229
- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
194230
{
231+
NSLog(@"sess didReceiveResponse");
195232
expectedBytes = [response expectedContentLength];
196233

197234
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
@@ -278,43 +315,7 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat
278315
} else {
279316
NSLog(@"oops");
280317
}
281-
282-
if (respFile)
283-
{
284-
@try{
285-
NSFileManager * fm = [NSFileManager defaultManager];
286-
NSString * folder = [destPath stringByDeletingLastPathComponent];
287-
288-
if (![fm fileExistsAtPath:folder]) {
289-
[fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
290-
}
291-
292-
// if not set overwrite in options, defaults to TRUE
293-
BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
294-
BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
295-
296-
appendToExistingFile = !overwrite;
297-
298-
// For solving #141 append response data if the file already exists
299-
// base on PR#139 @kejinliang
300-
if (appendToExistingFile) {
301-
destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
302-
}
303-
304-
if (![fm fileExistsAtPath:destPath]) {
305-
[fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
306-
}
307-
308-
writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
309-
[writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
310-
[writeStream open];
311-
}
312-
@catch(NSException * ex)
313-
{
314-
NSLog(@"write file error");
315-
}
316-
}
317-
318+
318319
completionHandler(NSURLSessionResponseAllow);
319320
}
320321

@@ -338,11 +339,7 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat
338339
chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
339340
}
340341

341-
if (respFile) {
342-
[writeStream write:[data bytes] maxLength:[data length]];
343-
} else {
344-
[respData appendData:data];
345-
}
342+
[respData appendData:data];
346343

347344
if (expectedBytes == 0) {
348345
return;
@@ -363,8 +360,16 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat
363360
}
364361
}
365362

363+
- (void) cancelRequest:(NSString *)taskId
364+
{
365+
NSURLSessionDataTask * task = [taskTable objectForKey:taskId];
366+
if(task != nil && task.state == NSURLSessionTaskStateRunning)
367+
[task cancel];
368+
}
369+
366370
- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
367371
{
372+
RCTLog(@"[RNFetchBlobRequest] session didBecomeInvalidWithError %@", [error description]);
368373
if ([session isEqual:session]) {
369374
session = nil;
370375
}
@@ -373,7 +378,7 @@ - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable
373378

374379
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
375380
{
376-
381+
RCTLog(@"[RNFetchBlobRequest] session didCompleteWithError %@", [error description]);
377382
self.error = error;
378383
NSString * errMsg;
379384
NSString * respStr;
@@ -426,10 +431,17 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom
426431
respStr ?: [NSNull null]
427432
]);
428433

434+
@synchronized(taskTable)
435+
{
436+
if([taskTable objectForKey:taskId] == nil)
437+
NSLog(@"object released by ARC.");
438+
else
439+
[taskTable removeObjectForKey:taskId];
440+
}
441+
429442
respData = nil;
430443
receivedBytes = 0;
431444
[session finishTasksAndInvalidate];
432-
433445
}
434446

435447
// upload progress handler
@@ -490,7 +502,19 @@ - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthentica
490502

491503
- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
492504
{
493-
NSLog(@"sess done in background");
505+
RCTLog(@"[RNFetchBlobRequest] session done in background");
506+
dispatch_async(dispatch_get_main_queue(), ^{
507+
id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
508+
SEL selector = NSSelectorFromString(@"backgroundTransferCompletionHandler");
509+
if ([appDelegate respondsToSelector:selector]) {
510+
void(^completionHandler)() = [appDelegate performSelector:selector];
511+
if (completionHandler != nil) {
512+
completionHandler();
513+
completionHandler = nil;
514+
}
515+
}
516+
517+
});
494518
}
495519

496520
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler

0 commit comments

Comments
 (0)