@kesha-antonov/react-native-background-downloader 2.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/README.md +349 -0
- package/android/build.gradle +31 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/eko/RNBGDTaskConfig.java +17 -0
- package/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java +487 -0
- package/android/src/main/java/com/eko/RNBackgroundDownloaderPackage.java +22 -0
- package/index.js +126 -0
- package/ios/RNBGDTaskConfig.h +55 -0
- package/ios/RNBackgroundDownloader.h +24 -0
- package/ios/RNBackgroundDownloader.m +425 -0
- package/ios/RNBackgroundDownloader.xcodeproj/project.pbxproj +254 -0
- package/lib/downloadTask.js +105 -0
- package/package.json +86 -0
- package/react-native-background-downloader.podspec +17 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RNFileBackgroundDownload.h
|
|
3
|
+
// EkoApp
|
|
4
|
+
//
|
|
5
|
+
// Created by Elad Gil on 20/11/2017.
|
|
6
|
+
// Copyright © 2017 Eko. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
//
|
|
9
|
+
#import <Foundation/Foundation.h>
|
|
10
|
+
#if __has_include(<React/RCTBridgeModule.h>)
|
|
11
|
+
#import <React/RCTBridgeModule.h>
|
|
12
|
+
#import <React/RCTEventEmitter.h>
|
|
13
|
+
#elif __has_include("RCTBridgeModule.h")
|
|
14
|
+
#import "RCTBridgeModule.h"
|
|
15
|
+
#import "RCTEventEmitter.h"
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
typedef void (^CompletionHandler)();
|
|
19
|
+
|
|
20
|
+
@interface RNBackgroundDownloader : RCTEventEmitter <RCTBridgeModule, NSURLSessionDelegate, NSURLSessionDownloadDelegate>
|
|
21
|
+
|
|
22
|
+
+ (void)setCompletionHandlerWithIdentifier: (NSString *)identifier completionHandler: (CompletionHandler)completionHandler;
|
|
23
|
+
|
|
24
|
+
@end
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RNFileBackgroundDownload.m
|
|
3
|
+
// EkoApp
|
|
4
|
+
//
|
|
5
|
+
// Created by Elad Gil on 20/11/2017.
|
|
6
|
+
// Copyright © 2017 Eko. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
//
|
|
9
|
+
#import "RNBackgroundDownloader.h"
|
|
10
|
+
#import "RNBGDTaskConfig.h"
|
|
11
|
+
|
|
12
|
+
#define ID_TO_CONFIG_MAP_KEY @"com.eko.bgdownloadidmap"
|
|
13
|
+
|
|
14
|
+
static CompletionHandler storedCompletionHandler;
|
|
15
|
+
|
|
16
|
+
@implementation RNBackgroundDownloader {
|
|
17
|
+
NSURLSession *urlSession;
|
|
18
|
+
NSURLSessionConfiguration *sessionConfig;
|
|
19
|
+
NSMutableDictionary<NSNumber *, RNBGDTaskConfig *> *taskToConfigMap;
|
|
20
|
+
NSMutableDictionary<NSString *, NSURLSessionDownloadTask *> *idToTaskMap;
|
|
21
|
+
NSMutableDictionary<NSString *, NSData *> *idToResumeDataMap;
|
|
22
|
+
NSMutableDictionary<NSString *, NSNumber *> *idToPercentMap;
|
|
23
|
+
NSMutableDictionary<NSString *, NSDictionary *> *progressReports;
|
|
24
|
+
NSDate *lastProgressReport;
|
|
25
|
+
NSNumber *sharedLock;
|
|
26
|
+
BOOL isNotificationCenterInited;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
RCT_EXPORT_MODULE();
|
|
30
|
+
|
|
31
|
+
- (dispatch_queue_t)methodQueue
|
|
32
|
+
{
|
|
33
|
+
return dispatch_queue_create("com.eko.backgrounddownloader", DISPATCH_QUEUE_SERIAL);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
37
|
+
return YES;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
41
|
+
return @[@"downloadComplete", @"downloadProgress", @"downloadFailed", @"downloadBegin"];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (NSDictionary *)constantsToExport {
|
|
45
|
+
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
46
|
+
|
|
47
|
+
return @{
|
|
48
|
+
@"documents": [paths firstObject],
|
|
49
|
+
@"TaskRunning": @(NSURLSessionTaskStateRunning),
|
|
50
|
+
@"TaskSuspended": @(NSURLSessionTaskStateSuspended),
|
|
51
|
+
@"TaskCanceling": @(NSURLSessionTaskStateCanceling),
|
|
52
|
+
@"TaskCompleted": @(NSURLSessionTaskStateCompleted)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
- (id) init {
|
|
57
|
+
NSLog(@"[RNBackgroundDownloader] - [init]");
|
|
58
|
+
self = [super init];
|
|
59
|
+
if (self) {
|
|
60
|
+
taskToConfigMap = [self deserialize:[[NSUserDefaults standardUserDefaults] objectForKey:ID_TO_CONFIG_MAP_KEY]];
|
|
61
|
+
if (taskToConfigMap == nil) {
|
|
62
|
+
taskToConfigMap = [[NSMutableDictionary alloc] init];
|
|
63
|
+
}
|
|
64
|
+
idToTaskMap = [[NSMutableDictionary alloc] init];
|
|
65
|
+
idToResumeDataMap= [[NSMutableDictionary alloc] init];
|
|
66
|
+
idToPercentMap = [[NSMutableDictionary alloc] init];
|
|
67
|
+
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
|
|
68
|
+
NSString *sessonIdentifier = [bundleIdentifier stringByAppendingString:@".backgrounddownloadtask"];
|
|
69
|
+
sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessonIdentifier];
|
|
70
|
+
sessionConfig.HTTPMaximumConnectionsPerHost = 4;
|
|
71
|
+
sessionConfig.timeoutIntervalForRequest = 60 * 60; // MAX TIME TO GET NEW DATA IN REQUEST - 1 HOUR
|
|
72
|
+
sessionConfig.timeoutIntervalForResource = 60 * 60 * 24; // MAX TIME TO DOWNLOAD RESOURCE - 1 DAY
|
|
73
|
+
sessionConfig.discretionary = NO;
|
|
74
|
+
sessionConfig.sessionSendsLaunchEvents = YES;
|
|
75
|
+
if (@available(iOS 9.0, *)) {
|
|
76
|
+
sessionConfig.shouldUseExtendedBackgroundIdleMode = YES;
|
|
77
|
+
}
|
|
78
|
+
if (@available(iOS 13.0, *)) {
|
|
79
|
+
sessionConfig.allowsExpensiveNetworkAccess = YES;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
progressReports = [[NSMutableDictionary alloc] init];
|
|
83
|
+
lastProgressReport = [[NSDate alloc] init];
|
|
84
|
+
sharedLock = [NSNumber numberWithInt:1];
|
|
85
|
+
}
|
|
86
|
+
return self;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (void)lazyInitSession {
|
|
90
|
+
NSLog(@"[RNBackgroundDownloader] - [lazyInitSession]");
|
|
91
|
+
@synchronized (sharedLock) {
|
|
92
|
+
if (urlSession == nil) {
|
|
93
|
+
urlSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
|
|
94
|
+
}
|
|
95
|
+
if (isNotificationCenterInited != YES) {
|
|
96
|
+
isNotificationCenterInited = YES;
|
|
97
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
98
|
+
selector:@selector(resumeTasks:)
|
|
99
|
+
name:UIApplicationWillEnterForegroundNotification
|
|
100
|
+
object:nil];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (void) dealloc {
|
|
106
|
+
NSLog(@"[RNBackgroundDownloader] - [dealloc]");
|
|
107
|
+
[urlSession invalidateAndCancel];
|
|
108
|
+
urlSession = nil;
|
|
109
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// NOTE: FIXES HANGING DOWNLOADS WHEN GOING TO BG
|
|
113
|
+
- (void) resumeTasks:(NSNotification *) note {
|
|
114
|
+
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 1");
|
|
115
|
+
@synchronized (self->sharedLock) {
|
|
116
|
+
[urlSession getTasksWithCompletionHandler:^(NSArray<NSURLSessionDataTask *> * _Nonnull dataTasks, NSArray<NSURLSessionUploadTask *> * _Nonnull uploadTasks, NSArray<NSURLSessionDownloadTask *> * _Nonnull downloadTasks) {
|
|
117
|
+
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 2");
|
|
118
|
+
for (NSURLSessionDownloadTask *task in downloadTasks) {
|
|
119
|
+
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 3 - state - %@", [NSNumber numberWithInt:task.state]);
|
|
120
|
+
// running - 0
|
|
121
|
+
// suspended - 1
|
|
122
|
+
// canceling - 2
|
|
123
|
+
// completed - 3
|
|
124
|
+
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 4 - totalBytes - %@", [NSNumber numberWithInt:task.countOfBytesExpectedToReceive]);
|
|
125
|
+
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 5 - bytesWritten - %@", [NSNumber numberWithInt:task.countOfBytesReceived]);
|
|
126
|
+
|
|
127
|
+
if (task.state == NSURLSessionTaskStateRunning) {
|
|
128
|
+
[task suspend]; // PAUSE
|
|
129
|
+
[task resume];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// TODO: MAYBE ADD FOR OTHER TASKS TYPES
|
|
133
|
+
// for (NSURLSessionDataTask *task in dataTasks) {
|
|
134
|
+
// NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 5");
|
|
135
|
+
// [task resume];
|
|
136
|
+
// }
|
|
137
|
+
// for (NSURLSessionUploadTask *task in dataTasks) {
|
|
138
|
+
// NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 6");
|
|
139
|
+
// [task resume];
|
|
140
|
+
// }
|
|
141
|
+
}];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
- (void)removeTaskFromMap: (NSURLSessionTask *)task {
|
|
146
|
+
NSLog(@"[RNBackgroundDownloader] - [removeTaskFromMap]");
|
|
147
|
+
@synchronized (sharedLock) {
|
|
148
|
+
NSNumber *taskId = @(task.taskIdentifier);
|
|
149
|
+
RNBGDTaskConfig *taskConfig = taskToConfigMap[taskId];
|
|
150
|
+
|
|
151
|
+
[taskToConfigMap removeObjectForKey:taskId];
|
|
152
|
+
[[NSUserDefaults standardUserDefaults] setObject:[self serialize: taskToConfigMap] forKey:ID_TO_CONFIG_MAP_KEY];
|
|
153
|
+
|
|
154
|
+
if (taskConfig) {
|
|
155
|
+
[idToTaskMap removeObjectForKey:taskConfig.id];
|
|
156
|
+
[idToPercentMap removeObjectForKey:taskConfig.id];
|
|
157
|
+
}
|
|
158
|
+
// TOREMOVE - GIVES ERROR IN JS ON HOT RELOAD
|
|
159
|
+
// if (taskToConfigMap.count == 0) {
|
|
160
|
+
// [urlSession invalidateAndCancel];
|
|
161
|
+
// urlSession = nil;
|
|
162
|
+
// }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
+ (void)setCompletionHandlerWithIdentifier: (NSString *)identifier completionHandler: (CompletionHandler)completionHandler {
|
|
167
|
+
NSLog(@"[RNBackgroundDownloader] - [setCompletionHandlerWithIdentifier]");
|
|
168
|
+
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
|
|
169
|
+
NSString *sessonIdentifier = [bundleIdentifier stringByAppendingString:@".backgrounddownloadtask"];
|
|
170
|
+
if ([sessonIdentifier isEqualToString:identifier]) {
|
|
171
|
+
storedCompletionHandler = completionHandler;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
- (NSError *)getServerError: (nonnull NSURLSessionDownloadTask *)downloadTask {
|
|
176
|
+
NSLog(@"[RNBackgroundDownloader] - [getServerError]");
|
|
177
|
+
NSError *serverError;
|
|
178
|
+
NSInteger httpStatusCode = [((NSHTTPURLResponse *)downloadTask.response) statusCode];
|
|
179
|
+
if(httpStatusCode != 200) {
|
|
180
|
+
serverError = [NSError errorWithDomain:NSURLErrorDomain
|
|
181
|
+
code:httpStatusCode
|
|
182
|
+
userInfo:@{NSLocalizedDescriptionKey: [NSHTTPURLResponse localizedStringForStatusCode: httpStatusCode]}];
|
|
183
|
+
}
|
|
184
|
+
return serverError;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
- (BOOL)saveDownloadedFile: (nonnull RNBGDTaskConfig *) taskConfig downloadURL:(nonnull NSURL *)location error:(NSError **)saveError {
|
|
188
|
+
NSLog(@"[RNBackgroundDownloader] - [saveDownloadedFile]");
|
|
189
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
190
|
+
NSURL *destURL = [NSURL fileURLWithPath:taskConfig.destination];
|
|
191
|
+
[fileManager createDirectoryAtURL:[destURL URLByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
|
|
192
|
+
[fileManager removeItemAtURL:destURL error:nil];
|
|
193
|
+
|
|
194
|
+
return [fileManager moveItemAtURL:location toURL:destURL error:saveError];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
#pragma mark - JS exported methods
|
|
198
|
+
RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
|
|
199
|
+
NSLog(@"[RNBackgroundDownloader] - [download]");
|
|
200
|
+
NSString *identifier = options[@"id"];
|
|
201
|
+
NSString *url = options[@"url"];
|
|
202
|
+
NSString *destination = options[@"destination"];
|
|
203
|
+
NSString *metadata = options[@"metadata"];
|
|
204
|
+
NSDictionary *headers = options[@"headers"];
|
|
205
|
+
if (identifier == nil || url == nil || destination == nil) {
|
|
206
|
+
NSLog(@"[RNBackgroundDownloader] - [Error] id, url and destination must be set");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
|
|
211
|
+
if (headers != nil) {
|
|
212
|
+
for (NSString *headerKey in headers) {
|
|
213
|
+
[request setValue:[headers valueForKey:headerKey] forHTTPHeaderField:headerKey];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@synchronized (sharedLock) {
|
|
218
|
+
[self lazyInitSession];
|
|
219
|
+
NSURLSessionDownloadTask __strong *task = [urlSession downloadTaskWithRequest:request];
|
|
220
|
+
if (task == nil) {
|
|
221
|
+
NSLog(@"[RNBackgroundDownloader] - [Error] failed to create download task");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
RNBGDTaskConfig *taskConfig = [[RNBGDTaskConfig alloc] initWithDictionary: @{@"id": identifier, @"destination": destination, @"metadata": metadata}];
|
|
226
|
+
|
|
227
|
+
taskToConfigMap[@(task.taskIdentifier)] = taskConfig;
|
|
228
|
+
[[NSUserDefaults standardUserDefaults] setObject:[self serialize: taskToConfigMap] forKey:ID_TO_CONFIG_MAP_KEY];
|
|
229
|
+
|
|
230
|
+
idToTaskMap[identifier] = task;
|
|
231
|
+
idToPercentMap[identifier] = @0.0;
|
|
232
|
+
|
|
233
|
+
[task resume];
|
|
234
|
+
lastProgressReport = [[NSDate alloc] init];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
RCT_EXPORT_METHOD(pauseTask: (NSString *)identifier) {
|
|
239
|
+
NSLog(@"[RNBackgroundDownloader] - [pauseTask]");
|
|
240
|
+
@synchronized (sharedLock) {
|
|
241
|
+
NSURLSessionDownloadTask *task = idToTaskMap[identifier];
|
|
242
|
+
if (task != nil && task.state == NSURLSessionTaskStateRunning) {
|
|
243
|
+
[task suspend];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
RCT_EXPORT_METHOD(resumeTask: (NSString *)identifier) {
|
|
249
|
+
NSLog(@"[RNBackgroundDownloader] - [resumeTask]");
|
|
250
|
+
@synchronized (sharedLock) {
|
|
251
|
+
NSURLSessionDownloadTask *task = idToTaskMap[identifier];
|
|
252
|
+
if (task != nil && task.state == NSURLSessionTaskStateSuspended) {
|
|
253
|
+
[task resume];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
RCT_EXPORT_METHOD(stopTask: (NSString *)identifier) {
|
|
259
|
+
NSLog(@"[RNBackgroundDownloader] - [stopTask]");
|
|
260
|
+
@synchronized (sharedLock) {
|
|
261
|
+
NSURLSessionDownloadTask *task = idToTaskMap[identifier];
|
|
262
|
+
if (task != nil) {
|
|
263
|
+
[task cancel];
|
|
264
|
+
[self removeTaskFromMap:task];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
RCT_EXPORT_METHOD(checkForExistingDownloads: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
270
|
+
NSLog(@"[RNBackgroundDownloader] - [checkForExistingDownloads]");
|
|
271
|
+
[self lazyInitSession];
|
|
272
|
+
[urlSession getTasksWithCompletionHandler:^(NSArray<NSURLSessionDataTask *> * _Nonnull dataTasks, NSArray<NSURLSessionUploadTask *> * _Nonnull uploadTasks, NSArray<NSURLSessionDownloadTask *> * _Nonnull downloadTasks) {
|
|
273
|
+
NSMutableArray *idsFound = [[NSMutableArray alloc] init];
|
|
274
|
+
@synchronized (sharedLock) {
|
|
275
|
+
for (NSURLSessionDownloadTask *foundTask in downloadTasks) {
|
|
276
|
+
NSURLSessionDownloadTask __strong *task = foundTask;
|
|
277
|
+
RNBGDTaskConfig *taskConfig = taskToConfigMap[@(task.taskIdentifier)];
|
|
278
|
+
if (taskConfig) {
|
|
279
|
+
if (task.state == NSURLSessionTaskStateCompleted && task.countOfBytesReceived < task.countOfBytesExpectedToReceive) {
|
|
280
|
+
if (task.error && task.error.code == -999 && task.error.userInfo[NSURLSessionDownloadTaskResumeData] != nil) {
|
|
281
|
+
task = [urlSession downloadTaskWithResumeData:task.error.userInfo[NSURLSessionDownloadTaskResumeData]];
|
|
282
|
+
} else {
|
|
283
|
+
task = [urlSession downloadTaskWithURL:foundTask.currentRequest.URL];
|
|
284
|
+
}
|
|
285
|
+
[task resume];
|
|
286
|
+
}
|
|
287
|
+
NSNumber *percent = foundTask.countOfBytesExpectedToReceive > 0 ? [NSNumber numberWithFloat:(float)task.countOfBytesReceived/(float)foundTask.countOfBytesExpectedToReceive] : @0.0;
|
|
288
|
+
[idsFound addObject:@{
|
|
289
|
+
@"id": taskConfig.id,
|
|
290
|
+
@"metadata": taskConfig.metadata,
|
|
291
|
+
@"state": [NSNumber numberWithInt: task.state],
|
|
292
|
+
@"bytesWritten": [NSNumber numberWithLongLong:task.countOfBytesReceived],
|
|
293
|
+
@"totalBytes": [NSNumber numberWithLongLong:foundTask.countOfBytesExpectedToReceive],
|
|
294
|
+
@"percent": percent
|
|
295
|
+
}];
|
|
296
|
+
taskConfig.reportedBegin = YES;
|
|
297
|
+
taskToConfigMap[@(task.taskIdentifier)] = taskConfig;
|
|
298
|
+
idToTaskMap[taskConfig.id] = task;
|
|
299
|
+
idToPercentMap[taskConfig.id] = percent;
|
|
300
|
+
} else {
|
|
301
|
+
[task cancel];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
resolve(idsFound);
|
|
305
|
+
}
|
|
306
|
+
}];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
RCT_EXPORT_METHOD(completeHandler:(nonnull NSString *)jobId
|
|
310
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
311
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
312
|
+
{
|
|
313
|
+
NSLog(@"[RNBackgroundDownloader] - [completeHandlerIOS]");
|
|
314
|
+
if (storedCompletionHandler) {
|
|
315
|
+
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
|
316
|
+
storedCompletionHandler();
|
|
317
|
+
storedCompletionHandler = nil;
|
|
318
|
+
}];
|
|
319
|
+
}
|
|
320
|
+
resolve(nil);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
#pragma mark - NSURLSessionDownloadDelegate methods
|
|
325
|
+
- (void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location {
|
|
326
|
+
NSLog(@"[RNBackgroundDownloader] - [didFinishDownloadingToURL]");
|
|
327
|
+
@synchronized (sharedLock) {
|
|
328
|
+
RNBGDTaskConfig *taskConfig = taskToConfigMap[@(downloadTask.taskIdentifier)];
|
|
329
|
+
if (taskConfig != nil) {
|
|
330
|
+
NSError *error = [self getServerError:downloadTask];
|
|
331
|
+
if (error == nil) {
|
|
332
|
+
[self saveDownloadedFile:taskConfig downloadURL:location error:&error];
|
|
333
|
+
}
|
|
334
|
+
if (self.bridge) {
|
|
335
|
+
if (error == nil) {
|
|
336
|
+
NSDictionary *responseHeaders = ((NSHTTPURLResponse *)downloadTask.response).allHeaderFields;
|
|
337
|
+
[self sendEventWithName:@"downloadComplete" body:@{@"id": taskConfig.id, @"headers": responseHeaders, @"location": taskConfig.destination}];
|
|
338
|
+
} else {
|
|
339
|
+
[self sendEventWithName:@"downloadFailed" body:@{@"id": taskConfig.id, @"error": [error localizedDescription]}];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
[self removeTaskFromMap:downloadTask];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
|
|
348
|
+
NSLog(@"[RNBackgroundDownloader] - [didResumeAtOffset]");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
|
352
|
+
NSLog(@"[RNBackgroundDownloader] - [didWriteData]");
|
|
353
|
+
@synchronized (sharedLock) {
|
|
354
|
+
RNBGDTaskConfig *taskCofig = taskToConfigMap[@(downloadTask.taskIdentifier)];
|
|
355
|
+
if (taskCofig != nil) {
|
|
356
|
+
if (!taskCofig.reportedBegin) {
|
|
357
|
+
NSDictionary *responseHeaders = ((NSHTTPURLResponse *)downloadTask.response).allHeaderFields;
|
|
358
|
+
if (self.bridge) {
|
|
359
|
+
[self sendEventWithName:@"downloadBegin" body:@{
|
|
360
|
+
@"id": taskCofig.id,
|
|
361
|
+
@"expectedBytes": [NSNumber numberWithLongLong: totalBytesExpectedToWrite],
|
|
362
|
+
@"headers": responseHeaders
|
|
363
|
+
}];
|
|
364
|
+
}
|
|
365
|
+
taskCofig.reportedBegin = YES;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
NSNumber *prevPercent = idToPercentMap[taskCofig.id];
|
|
369
|
+
NSNumber *percent = [NSNumber numberWithFloat:(float)totalBytesWritten/(float)totalBytesExpectedToWrite];
|
|
370
|
+
if ([percent floatValue] - [prevPercent floatValue] > 0.01f) {
|
|
371
|
+
progressReports[taskCofig.id] = @{@"id": taskCofig.id, @"written": [NSNumber numberWithLongLong: totalBytesWritten], @"total": [NSNumber numberWithLongLong: totalBytesExpectedToWrite], @"percent": percent};
|
|
372
|
+
idToPercentMap[taskCofig.id] = percent;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
NSDate *now = [[NSDate alloc] init];
|
|
376
|
+
if ([now timeIntervalSinceDate:lastProgressReport] > 0.25 && progressReports.count > 0) {
|
|
377
|
+
if (self.bridge) {
|
|
378
|
+
[self sendEventWithName:@"downloadProgress" body:[progressReports allValues]];
|
|
379
|
+
}
|
|
380
|
+
lastProgressReport = now;
|
|
381
|
+
[progressReports removeAllObjects];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
|
388
|
+
NSLog(@"[RNBackgroundDownloader] - [didCompleteWithError]");
|
|
389
|
+
@synchronized (sharedLock) {
|
|
390
|
+
RNBGDTaskConfig *taskCofig = taskToConfigMap[@(task.taskIdentifier)];
|
|
391
|
+
if (error != nil && error.code != -999 && taskCofig != nil) {
|
|
392
|
+
if (self.bridge) {
|
|
393
|
+
[self sendEventWithName:@"downloadFailed" body:@{@"id": taskCofig.id, @"error": [error localizedDescription]}];
|
|
394
|
+
}
|
|
395
|
+
[self removeTaskFromMap:task];
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
|
|
401
|
+
NSLog(@"[RNBackgroundDownloader] - [URLSessionDidFinishEventsForBackgroundURLSession]");
|
|
402
|
+
// USE completionHandler FROM JS INSTEAD OF THIS
|
|
403
|
+
// TOREMOVE
|
|
404
|
+
// if (storedCompletionHandler) {
|
|
405
|
+
// [[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
|
406
|
+
// storedCompletionHandler();
|
|
407
|
+
// storedCompletionHandler = nil;
|
|
408
|
+
// }];
|
|
409
|
+
// }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#pragma mark - serialization
|
|
413
|
+
- (NSData *)serialize: (id)obj {
|
|
414
|
+
return [NSKeyedArchiver archivedDataWithRootObject:obj];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
- (id)deserialize: (NSData *)data {
|
|
418
|
+
if (data == nil) {
|
|
419
|
+
return nil;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@end
|