@kesha-antonov/react-native-background-downloader 4.0.2 → 4.0.3

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/README.md CHANGED
@@ -49,6 +49,8 @@ The real challenge of using this method is making sure the app's UI is always up
49
49
 
50
50
  ## Getting started
51
51
 
52
+ ### Installation
53
+
52
54
  ```Terminal
53
55
  yarn add @kesha-antonov/react-native-background-downloader
54
56
  ```
@@ -64,7 +66,9 @@ Then:
64
66
  cd ios && pod install
65
67
  ```
66
68
 
67
- #### Manual Setup (Advanced)
69
+ <details>
70
+ <summary>Manual Setup (Advanced)</summary>
71
+
68
72
  If you need to manually configure the package for New Architecture:
69
73
 
70
74
  **iOS**: The library automatically detects New Architecture via compile-time flags.
@@ -82,16 +86,7 @@ protected List<ReactPackage> getPackages() {
82
86
  );
83
87
  }
84
88
  ```
85
-
86
- #### Known Issues with New Architecture
87
- When using larger files with the New Architecture, you may encounter `ERROR_CANNOT_RESUME` (error code 1008). This is a known limitation of Android's DownloadManager, not specific to this library or the New Architecture. The error includes enhanced messaging to help diagnose the issue.
88
-
89
- **Workaround:** If you encounter this error frequently with large files, consider:
90
- 1. Breaking large downloads into smaller chunks
91
- 2. Implementing retry logic in your app
92
- 3. Using alternative download strategies for very large files
93
-
94
- The library now provides enhanced error handling for this specific case with detailed logging and cleanup.
89
+ </details>
95
90
 
96
91
  ### Mostly automatic installation
97
92
  Any React Native version **`>= 0.60`** supports autolinking so nothing should be done.
@@ -255,16 +250,18 @@ let task = createDownloadTask({
255
250
  // starts download
256
251
  task.start()
257
252
 
253
+ // ...later
254
+
258
255
  // Pause the task (iOS only)
259
256
  // Note: On Android, pause/resume is not supported by DownloadManager
260
- task.pause()
257
+ await task.pause()
261
258
 
262
259
  // Resume after pause (iOS only)
263
260
  // Note: On Android, pause/resume is not supported by DownloadManager
264
- task.resume()
261
+ await task.resume()
265
262
 
266
263
  // Cancel the task
267
- task.stop()
264
+ await task.stop()
268
265
  ```
269
266
 
270
267
  ### Platform-Aware Pause/Resume
@@ -291,18 +288,18 @@ const task = createDownloadTask({
291
288
  task.start()
292
289
 
293
290
  // Platform-aware pause/resume handling
294
- function pauseDownloadTask() {
291
+ async function pauseDownloadTask() {
295
292
  if (Platform.OS === 'ios') {
296
- task.pause()
293
+ await task.pause()
297
294
  console.log('Download paused')
298
295
  } else {
299
296
  console.log('Pause not supported on Android. Consider using stop() instead.')
300
297
  }
301
298
  }
302
299
 
303
- function resumeDownloadTask() {
300
+ async function resumeDownloadTask() {
304
301
  if (Platform.OS === 'ios') {
305
- task.resume()
302
+ await task.resume()
306
303
  console.log('Download resumed')
307
304
  } else {
308
305
  console.log('Resume not supported on Android. You may need to restart the download.')
@@ -437,7 +434,6 @@ import {
437
434
  setConfig,
438
435
  createDownloadTask,
439
436
  getExistingDownloadTasks,
440
- ensureDownloadsAreRunning,
441
437
  completeHandler,
442
438
  directories
443
439
  } from '@kesha-antonov/react-native-background-downloader'
@@ -452,7 +448,6 @@ import RNBackgroundDownloader from '@kesha-antonov/react-native-background-downl
452
448
  RNBackgroundDownloader.setConfig
453
449
  RNBackgroundDownloader.createDownloadTask
454
450
  RNBackgroundDownloader.getExistingDownloadTasks
455
- RNBackgroundDownloader.ensureDownloadsAreRunning
456
451
  RNBackgroundDownloader.completeHandler
457
452
  RNBackgroundDownloader.directories
458
453
  ```
@@ -531,62 +526,6 @@ After finishing download in background you have some time to process your JS log
531
526
 
532
527
  **Note:** This should be called after processing your download in the `done` callback to properly signal completion to the OS.
533
528
 
534
- ### `ensureDownloadsAreRunning()` (iOS only)
535
-
536
- Pauses and resumes all downloads - this is fix for stuck downloads. Use it when your app loaded and is ready for handling downloads (all your logic loaded and ready to handle download callbacks).
537
-
538
- **returns**
539
-
540
- `Promise<void>` - A promise that resolves when all downloads have been paused and resumed
541
-
542
- Here's example of how you can use it:
543
-
544
- 1. When your app just loaded
545
-
546
- Either stop all tasks:
547
-
548
- ```javascript
549
- import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
550
-
551
- const tasks = await getExistingDownloadTasks()
552
- for (const task of tasks)
553
- task.stop()
554
- ```
555
-
556
- Or re-attach them:
557
-
558
- ```javascript
559
- import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
560
-
561
- const tasks = await getExistingDownloadTasks()
562
- for (const task of tasks) {
563
- task.pause()
564
- //
565
- // YOUR LOGIC OF RE-ATTACHING DOWLOADS TO YOUR STUFF
566
- // ...
567
- //
568
- }
569
- ```
570
-
571
- 2. Prepare your app to handle downloads... (load your state etc.)
572
-
573
- 3. Add listener to handle when your app goes foreground (be sure to do it only after you stopped all tasks or re-attached them!)
574
-
575
- ```javascript
576
- import { ensureDownloadsAreRunning } from '@kesha-antonov/react-native-background-downloader'
577
-
578
- function handleAppStateChange (appState) {
579
- if (appState !== 'active')
580
- return
581
-
582
- ensureDownloadsAreRunning()
583
- }
584
-
585
- const appStateChangeListener = AppState.addEventListener('change', handleAppStateChange)
586
- ```
587
-
588
- 4. Call `ensureDownloadsAreRunning()` after all was setup.
589
-
590
529
  ### `Callback Methods`
591
530
  Use these methods to stay updated on what's happening with the task.
592
531
 
@@ -599,18 +538,18 @@ All callback methods return the current instance of the `DownloadTask` for chain
599
538
  | `done` | `{ bytesDownloaded: number, bytesTotal: number }` | Called when the download is done, the file is at the destination you've set |
600
539
  | `error` | `{ error: string, errorCode: number }` | Called when the download stops due to an error |
601
540
 
602
- ### `pause()` (iOS only)
603
- Pauses the download
541
+ ### `pause(): Promise<void>` (iOS only)
542
+ Pauses the download. Returns a promise that resolves when the pause operation is complete.
604
543
 
605
544
  **Note:** This functionality is not supported on Android due to limitations in the DownloadManager API. On Android, calling this method will log a warning but will not crash the application.
606
545
 
607
- ### `resume()` (iOS only)
608
- Resumes a paused download
546
+ ### `resume(): Promise<void>` (iOS only)
547
+ Resumes a paused download. Returns a promise that resolves when the resume operation is complete.
609
548
 
610
549
  **Note:** This functionality is not supported on Android due to limitations in the DownloadManager API. On Android, calling this method will log a warning but will not crash the application.
611
550
 
612
- ### `stop()`
613
- Stops the download for good and removes the file that was written so far
551
+ ### `stop(): Promise<void>`
552
+ Stops the download for good and removes the file that was written so far. Returns a promise that resolves when the stop operation is complete.
614
553
 
615
554
  ## Constants
616
555
 
@@ -661,11 +600,19 @@ In case of error `java.lang.IllegalStateException: TypeToken must be created wit
661
600
  -keepattributes AnnotationDefault,RuntimeVisibleAnnotations
662
601
  ```
663
602
 
603
+ ## Known Issues with New Architecture
604
+ When using larger files with the New Architecture, you may encounter `ERROR_CANNOT_RESUME` (error code 1008). This is a known limitation of Android's DownloadManager, not specific to this library or the New Architecture. The error includes enhanced messaging to help diagnose the issue.
605
+
606
+ **Workaround:** If you encounter this error frequently with large files, consider:
607
+ 1. Breaking large downloads into smaller chunks
608
+ 2. Implementing retry logic in your app
609
+ 3. Using alternative download strategies for very large files
610
+
611
+ The library now provides enhanced error handling for this specific case with detailed logging and cleanup.
612
+
664
613
  ## TODO
665
614
 
666
- - [ ] Write better examples - current kinda old and shallow
667
615
  - [ ] Write better API for downloads - current kinda boilerplate
668
- - [ ] Add more tests
669
616
 
670
617
  ## Authors
671
618
 
@@ -29,16 +29,19 @@ class RNBackgroundDownloaderModule(reactContext: ReactApplicationContext) :
29
29
  impl.download(options)
30
30
  }
31
31
 
32
- override fun pauseTask(id: String) {
32
+ override fun pauseTask(id: String, promise: com.facebook.react.bridge.Promise) {
33
33
  impl.pauseTask(id)
34
+ promise.resolve(null)
34
35
  }
35
36
 
36
- override fun resumeTask(id: String) {
37
+ override fun resumeTask(id: String, promise: com.facebook.react.bridge.Promise) {
37
38
  impl.resumeTask(id)
39
+ promise.resolve(null)
38
40
  }
39
41
 
40
- override fun stopTask(id: String) {
42
+ override fun stopTask(id: String, promise: com.facebook.react.bridge.Promise) {
41
43
  impl.stopTask(id)
44
+ promise.resolve(null)
42
45
  }
43
46
 
44
47
  override fun completeHandler(jobId: String, promise: com.facebook.react.bridge.Promise) {
@@ -33,18 +33,21 @@ class RNBackgroundDownloaderModule(reactContext: ReactApplicationContext) :
33
33
  }
34
34
 
35
35
  @ReactMethod
36
- fun pauseTask(id: String) {
36
+ fun pauseTask(id: String, promise: Promise) {
37
37
  impl.pauseTask(id)
38
+ promise.resolve(null)
38
39
  }
39
40
 
40
41
  @ReactMethod
41
- fun resumeTask(id: String) {
42
+ fun resumeTask(id: String, promise: Promise) {
42
43
  impl.resumeTask(id)
44
+ promise.resolve(null)
43
45
  }
44
46
 
45
47
  @ReactMethod
46
- fun stopTask(id: String) {
48
+ fun stopTask(id: String, promise: Promise) {
47
49
  impl.stopTask(id)
50
+ promise.resolve(null)
48
51
  }
49
52
 
50
53
  @ReactMethod
@@ -373,7 +373,7 @@ RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
373
373
  }
374
374
  }
375
375
 
376
- - (void)pauseTaskInternal:(NSString *)identifier {
376
+ - (void)pauseTaskInternal:(NSString *)identifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
377
377
  DLog(identifier, @"[RNBackgroundDownloader] - [pauseTask]");
378
378
  @synchronized (sharedLock) {
379
379
  NSURLSessionDownloadTask *task = self->idToTaskMap[identifier];
@@ -392,22 +392,25 @@ RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
392
392
  }
393
393
  // Keep the identifier in idsToPauseSet until resume or stop is called
394
394
  }
395
+ resolve(nil);
395
396
  }];
397
+ } else {
398
+ resolve(nil);
396
399
  }
397
400
  }
398
401
  }
399
402
 
400
- #ifdef RCT_NEW_ARCH_ENABLED
401
- - (void)pauseTask:(NSString *)identifier {
402
- [self pauseTaskInternal:identifier];
403
+ - (void)pauseTask:(NSString *)identifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
404
+ [self pauseTaskInternal:identifier resolve:resolve reject:reject];
403
405
  }
404
- #else
405
- RCT_EXPORT_METHOD(pauseTask: (NSString *)id) {
406
- [self pauseTaskInternal:id];
406
+
407
+ #ifndef RCT_NEW_ARCH_ENABLED
408
+ RCT_EXPORT_METHOD(pauseTask:(NSString *)id resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
409
+ [self pauseTaskInternal:id resolve:resolve reject:reject];
407
410
  }
408
411
  #endif
409
412
 
410
- - (void)resumeTaskInternal:(NSString *)identifier {
413
+ - (void)resumeTaskInternal:(NSString *)identifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
411
414
  DLog(identifier, @"[RNBackgroundDownloader] - [resumeTask]");
412
415
  @synchronized (sharedLock) {
413
416
  [self lazyRegisterSession];
@@ -456,19 +459,20 @@ RCT_EXPORT_METHOD(pauseTask: (NSString *)id) {
456
459
  DLog(identifier, @"[RNBackgroundDownloader] - [resumeTask] no task or resume data found for %@", identifier);
457
460
  }
458
461
  }
462
+ resolve(nil);
459
463
  }
460
464
 
461
- #ifdef RCT_NEW_ARCH_ENABLED
462
- - (void)resumeTask:(NSString *)identifier {
463
- [self resumeTaskInternal:identifier];
465
+ - (void)resumeTask:(NSString *)identifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
466
+ [self resumeTaskInternal:identifier resolve:resolve reject:reject];
464
467
  }
465
- #else
466
- RCT_EXPORT_METHOD(resumeTask: (NSString *)id) {
467
- [self resumeTaskInternal:id];
468
+
469
+ #ifndef RCT_NEW_ARCH_ENABLED
470
+ RCT_EXPORT_METHOD(resumeTask:(NSString *)id resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
471
+ [self resumeTaskInternal:id resolve:resolve reject:reject];
468
472
  }
469
473
  #endif
470
474
 
471
- - (void)stopTaskInternal:(NSString *)identifier {
475
+ - (void)stopTaskInternal:(NSString *)identifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
472
476
  DLog(identifier, @"[RNBackgroundDownloader] - [stopTask]");
473
477
  @synchronized (sharedLock) {
474
478
  NSURLSessionDownloadTask *task = self->idToTaskMap[identifier];
@@ -479,15 +483,16 @@ RCT_EXPORT_METHOD(resumeTask: (NSString *)id) {
479
483
  [self->idToResumeDataMap removeObjectForKey:identifier];
480
484
  [idsToPauseSet removeObject:identifier];
481
485
  }
486
+ resolve(nil);
482
487
  }
483
488
 
484
- #ifdef RCT_NEW_ARCH_ENABLED
485
- - (void)stopTask:(NSString *)identifier {
486
- [self stopTaskInternal:identifier];
489
+ - (void)stopTask:(NSString *)identifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
490
+ [self stopTaskInternal:identifier resolve:resolve reject:reject];
487
491
  }
488
- #else
489
- RCT_EXPORT_METHOD(stopTask: (NSString *)id) {
490
- [self stopTaskInternal:id];
492
+
493
+ #ifndef RCT_NEW_ARCH_ENABLED
494
+ RCT_EXPORT_METHOD(stopTask:(NSString *)id resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
495
+ [self stopTaskInternal:id resolve:resolve reject:reject];
491
496
  }
492
497
  #endif
493
498
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kesha-antonov/react-native-background-downloader",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
4
4
  "description": "A library for React-Native to help you download large files on iOS and Android both in the foreground and most importantly in the background.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -129,17 +129,17 @@ export default class DownloadTask {
129
129
  this.downloadParams = downloadParams
130
130
  }
131
131
 
132
- pause () {
132
+ async pause (): Promise<void> {
133
133
  log('DownloadTask: pause', this.id)
134
134
  this.state = 'PAUSED'
135
- RNBackgroundDownloader.pauseTask(this.id)
135
+ await RNBackgroundDownloader.pauseTask(this.id)
136
136
  }
137
137
 
138
- resume () {
138
+ async resume (): Promise<void> {
139
139
  log('DownloadTask: resume', this.id)
140
140
  this.state = 'DOWNLOADING'
141
141
  this.errorCode = 0
142
- RNBackgroundDownloader.resumeTask(this.id)
142
+ await RNBackgroundDownloader.resumeTask(this.id)
143
143
  }
144
144
 
145
145
  start () {
@@ -163,11 +163,11 @@ export default class DownloadTask {
163
163
  })
164
164
  }
165
165
 
166
- stop () {
166
+ async stop (): Promise<void> {
167
167
  log('DownloadTask: stop', this.id)
168
168
 
169
169
  this.state = 'STOPPED'
170
- RNBackgroundDownloader.stopTask(this.id)
170
+ await RNBackgroundDownloader.stopTask(this.id)
171
171
  }
172
172
 
173
173
  tryParseJson (metadata?: string | Metadata): Metadata | null {
@@ -58,9 +58,9 @@ export interface Spec extends TurboModule {
58
58
  maxRedirects?: number
59
59
  }): void
60
60
 
61
- pauseTask(id: string): void
62
- resumeTask(id: string): void
63
- stopTask(id: string): void
61
+ pauseTask(id: string): Promise<void>
62
+ resumeTask(id: string): Promise<void>
63
+ stopTask(id: string): Promise<void>
64
64
 
65
65
  completeHandler(jobId: string): Promise<void>
66
66
 
package/src/index.ts CHANGED
@@ -255,16 +255,6 @@ export const getExistingDownloadTasks = async (): Promise<DownloadTask[]> => {
255
255
  return downloadTasks
256
256
  }
257
257
 
258
- export const ensureDownloadsAreRunning = async (): Promise<void> => {
259
- const tasks: DownloadTask[] = await getExistingDownloadTasks()
260
- const tasksDownloading = tasks.filter(task => task.state === 'DOWNLOADING')
261
-
262
- for (const task of tasksDownloading) {
263
- task.pause()
264
- task.resume()
265
- }
266
- }
267
-
268
258
  export const completeHandler = (jobId: string) => {
269
259
  if (jobId == null) {
270
260
  log('completeHandler: jobId is empty')
@@ -319,7 +309,6 @@ export default {
319
309
  setConfig,
320
310
  createDownloadTask,
321
311
  getExistingDownloadTasks,
322
- ensureDownloadsAreRunning,
323
312
  completeHandler,
324
313
 
325
314
  directories,
package/src/types.ts CHANGED
@@ -94,15 +94,14 @@ export interface DownloadTask {
94
94
 
95
95
  setDownloadParams: (params: DownloadParams) => void
96
96
  start: () => void
97
- pause: () => void
98
- resume: () => void
99
- stop: () => void
97
+ pause: () => Promise<void>
98
+ resume: () => Promise<void>
99
+ stop: () => Promise<void>
100
100
 
101
101
  tryParseJson: (metadata?: string | Metadata) => Metadata | null
102
102
  }
103
103
 
104
104
  export type getExistingDownloadTasks = () => Promise<DownloadTask[]>
105
- export type EnsureDownloadsAreRunning = () => Promise<void>
106
105
 
107
106
  export interface DownloadOption {
108
107
  id: string