Skip to content
This repository was archived by the owner on Mar 16, 2019. It is now read-only.

Commit e1cb0e2

Browse files
committed
Add IOS background HTTP expiration handling #115
1 parent 301c8cf commit e1cb0e2

File tree

8 files changed

+99
-30
lines changed

8 files changed

+99
-30
lines changed

src/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob
4848

4949
// register message channel event handler.
5050
emitter.addListener("RNFetchBlobMessage", (e) => {
51+
5152
if(e.event === 'warn') {
5253
console.warn(e.detail)
5354
}
@@ -140,7 +141,11 @@ function fetchFile(options = {}, method, url, headers = {}, body):Promise {
140141
promise = fs.stat(url)
141142
.then((stat) => {
142143
total = stat.size
143-
return fs.readStream(url, headers.encoding || 'utf8', Math.floor(headers.bufferSize) || 4096)
144+
return fs.readStream(url,
145+
headers.encoding || 'utf8',
146+
Math.floor(headers.bufferSize) || 409600,
147+
Math.floor(headers.interval) || 100
148+
)
144149
})
145150
.then((stream) => new Promise((resolve, reject) => {
146151
stream.open()
@@ -232,6 +237,13 @@ function fetch(...args:any):Promise {
232237
}
233238
})
234239

240+
subscription = emitter.addListener('RNFetchBlobExpire', (e) => {
241+
console.log(e , 'EXPIRED!!')
242+
if(e.taskId === taskId && promise.onExpire) {
243+
promise.onExpire(e)
244+
}
245+
})
246+
235247
// When the request body comes from Blob polyfill, we should use special its ref
236248
// as the request body
237249
if( body instanceof Blob && body.isRNFetchBlobPolyfill) {
@@ -261,6 +273,7 @@ function fetch(...args:any):Promise {
261273
delete promise['uploadProgress']
262274
delete promise['stateChange']
263275
delete promise['cancel']
276+
// delete promise['expire']
264277
promise.cancel = () => {}
265278

266279
if(err)
@@ -296,6 +309,10 @@ function fetch(...args:any):Promise {
296309
promise.onStateChange = fn
297310
return promise
298311
}
312+
promise.expire = (fn) => {
313+
promise.onExpire = fn
314+
return promise
315+
}
299316
promise.cancel = (fn) => {
300317
fn = fn || function(){}
301318
subscription.remove()

src/ios/RNFetchBlob/RNFetchBlob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
}
1818

1919
@property (nonatomic) NSString * filePathPrefix;
20+
@property (nonatomic) UIDocumentInteractionController * documentController;
2021

2122
+ (RCTBridge *)getRCTBridge;
2223

src/ios/RNFetchBlob/RNFetchBlob.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#import "RNFetchBlob.h"
88
#import "RCTLog.h"
9+
#import "RCTRootView.h"
910
#import "RCTBridge.h"
1011
#import "RCTEventDispatcher.h"
1112
#import "RNFetchBlobFS.h"
@@ -14,7 +15,7 @@
1415
#import "RNFetchBlobReqBuilder.h"
1516

1617

17-
RCTBridge * bridgeRef;
18+
__strong RCTBridge * bridgeRef;
1819
dispatch_queue_t commonTaskQueue;
1920
dispatch_queue_t fsQueue;
2021

@@ -40,7 +41,8 @@ - (dispatch_queue_t) methodQueue {
4041

4142
+ (RCTBridge *)getRCTBridge
4243
{
43-
return bridgeRef;
44+
RCTRootView * rootView = [[UIApplication sharedApplication] keyWindow].rootViewController.view;
45+
return rootView.bridge;
4446
}
4547

4648
RCT_EXPORT_MODULE();
@@ -58,6 +60,7 @@ - (id) init {
5860
[[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
5961
}
6062
bridgeRef = _bridge;
63+
[RNFetchBlobNetwork getExpiredTasks];
6164
return self;
6265
}
6366

src/ios/RNFetchBlobConst.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern NSString *const MSG_EVENT_LOG;
1717
extern NSString *const MSG_EVENT_WARN;
1818
extern NSString *const MSG_EVENT_ERROR;
1919

20+
extern NSString *const EVENT_EXPIRE;
2021
extern NSString *const EVENT_PROGRESS;
2122
extern NSString *const EVENT_PROGRESS_UPLOAD;
2223
extern NSString *const EVENT_STATE_CHANGE;

src/ios/RNFetchBlobConst.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
extern NSString *const ASSET_PREFIX = @"bundle-assets://";
1212
extern NSString *const AL_PREFIX = @"assets-library://";
1313

14-
15-
1614
// fetch configs
1715
extern NSString *const CONFIG_USE_TEMP = @"fileCache";
1816
extern NSString *const CONFIG_FILE_PATH = @"path";
@@ -25,6 +23,7 @@
2523
extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
2624
extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
2725
extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
26+
extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
2827

2928
extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
3029
extern NSString *const MSG_EVENT_LOG = @"log";

src/ios/RNFetchBlobNetwork.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,18 @@ typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse
3131
@property (nullable, nonatomic) NSError * error;
3232

3333

34-
- (nullable id) init;
35-
- (void) sendRequest;
36-
3734
+ (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
3835
+ (void) cancelRequest:(NSString *)taskId;
3936
+ (void) enableProgressReport:(NSString *) taskId;
4037
+ (void) enableUploadProgress:(NSString *) taskId;
38+
+ (void) getExpiredTasks;
39+
40+
- (nullable id) init;
41+
- (void) sendRequest;
4142
- (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
4243

4344

45+
4446
@end
4547

4648

src/ios/RNFetchBlobNetwork.m

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#import "RCTLog.h"
1010
#import <Foundation/Foundation.h>
1111
#import "RCTBridge.h"
12+
#import "RNFetchBlob.h"
1213
#import "RCTEventDispatcher.h"
1314
#import "RNFetchBlobFS.h"
15+
#import "RCTRootView.h"
1416
#import "RNFetchBlobNetwork.h"
1517
#import "RNFetchBlobConst.h"
1618
#import "RNFetchBlobReqBuilder.h"
@@ -24,6 +26,7 @@
2426
////////////////////////////////////////
2527

2628
NSMapTable * taskTable;
29+
NSMapTable * expirationTable;
2730
NSMutableDictionary * progressTable;
2831
NSMutableDictionary * uploadProgressTable;
2932

@@ -37,6 +40,7 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) {
3740
@interface RNFetchBlobNetwork ()
3841
{
3942
BOOL * respFile;
43+
BOOL * isIncrement;
4044
NSString * destPath;
4145
NSOutputStream * writeStream;
4246
long bodyLength;
@@ -70,7 +74,12 @@ - (id)init {
7074
taskQueue = [[NSOperationQueue alloc] init];
7175
taskQueue.maxConcurrentOperationCount = 10;
7276
}
73-
if(taskTable == nil) {
77+
if(expirationTable == nil)
78+
{
79+
expirationTable = [[NSMapTable alloc] init];
80+
}
81+
if(taskTable == nil)
82+
{
7483
taskTable = [[NSMapTable alloc] init];
7584
}
7685
if(progressTable == nil)
@@ -133,6 +142,7 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
133142
self.expectedBytes = 0;
134143
self.receivedBytes = 0;
135144
self.options = options;
145+
isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
136146
redirects = [[NSMutableArray alloc] init];
137147
[redirects addObject:req.URL.absoluteString];
138148

@@ -153,7 +163,9 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
153163
bodyLength = contentLength;
154164

155165
// the session trust any SSL certification
156-
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
166+
// NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
167+
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
168+
157169
// set request timeout
158170
float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
159171
if(timeout > 0)
@@ -190,13 +202,39 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
190202
respData = [[NSMutableData alloc] init];
191203
respFile = NO;
192204
}
193-
NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
205+
206+
__block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
194207
[taskTable setObject:task forKey:taskId];
195208
[task resume];
196209

197210
// network status indicator
198211
if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
199212
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
213+
__block UIApplication * app = [UIApplication sharedApplication];
214+
215+
// #115 handling task expired when application entering backgound for a long time
216+
[app beginBackgroundTaskWithName:taskId expirationHandler:^{
217+
NSLog([NSString stringWithFormat:@"session %@ expired event emit", taskId ]);
218+
[expirationTable setObject:task forKey:taskId];
219+
[app endBackgroundTask:task];
220+
221+
}];
222+
223+
224+
}
225+
226+
// #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
227+
+ (void) getExpiredTasks
228+
{
229+
NSEnumerator * emu = [expirationTable keyEnumerator];
230+
NSString * key;
231+
232+
while((key = [emu nextObject]))
233+
{
234+
RCTBridge * bridge = [RNFetchBlob getRCTBridge];
235+
NSData * args = @{ @"taskId": key };
236+
[bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
237+
}
200238
}
201239

202240
////////////////////////////////////////
@@ -306,7 +344,7 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat
306344
receivedBytes += [received longValue];
307345
NSString * chunkString = @"";
308346

309-
if(isInrement == YES)
347+
if(isIncrement == YES)
310348
{
311349
chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
312350
}
@@ -480,6 +518,11 @@ - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthentica
480518
}
481519
}
482520

521+
- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
522+
{
523+
NSLog(@"sess done in background");
524+
}
525+
483526
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
484527
{
485528
[redirects addObject:[request.URL absoluteString]];

test/test-init.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ const { Assert, Comparer, Info, prop } = RNTest
1818
// test environment variables
1919

2020
prop('FILENAME', `${Platform.OS}-0.8.0-${Date.now()}.png`)
21-
prop('TEST_SERVER_URL', 'http://192.168.16.70:8123')
22-
prop('TEST_SERVER_URL_SSL', 'https://192.168.16.70:8124')
21+
prop('TEST_SERVER_URL', 'http://localhost:8123')
22+
prop('TEST_SERVER_URL_SSL', 'https://localhost:8124')
2323
prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
2424
prop('styles', {
2525
image : {
@@ -59,21 +59,24 @@ describe('GET image from server', (report, done) => {
5959
})
6060

6161

62-
require('./test-0.1.x-0.4.x')
63-
require('./test-0.5.1')
64-
require('./test-0.5.2')
65-
require('./test-0.6.0')
66-
require('./test-0.6.2')
67-
require('./test-0.7.0')
68-
require('./test-0.8.0')
69-
require('./test-0.9.0')
70-
require('./test-0.9.2')
71-
require('./test-0.9.4')
72-
require('./test-fetch')
73-
require('./test-fs')
74-
require('./test-xmlhttp')
75-
require('./test-blob')
76-
require('./test-firebase')
77-
require('./test-android')
62+
// require('./test-0.1.x-0.4.x')
63+
// require('./test-0.5.1')
64+
// require('./test-0.5.2')
65+
// require('./test-0.6.0')
66+
// require('./test-0.6.2')
67+
// require('./test-0.7.0')
68+
// require('./test-0.8.0')
69+
// require('./test-0.9.0')
70+
// require('./test-0.9.2')
71+
// require('./test-0.9.4')
72+
// require('./test-0.9.5')
73+
// require('./test-0.10.0')
74+
// require('./test-fetch')
75+
// require('./test-fs')
76+
// require('./test-xmlhttp')
77+
// require('./test-blob')
78+
// require('./test-firebase')
79+
require('./test-background')
80+
// require('./test-android')
7881
// require('./test-stress')
7982
// require('./benchmark')

0 commit comments

Comments
 (0)