@kesha-antonov/react-native-background-downloader 3.2.6 → 4.0.0-alpha.1

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +354 -45
  3. package/android/build.gradle +48 -5
  4. package/android/src/main/java/com/eko/Downloader.kt +133 -0
  5. package/android/src/main/java/com/eko/RNBGDTaskConfig.kt +12 -0
  6. package/android/src/main/java/com/eko/RNBackgroundDownloaderModuleImpl.kt +863 -0
  7. package/android/src/main/java/com/eko/RNBackgroundDownloaderPackage.kt +39 -0
  8. package/android/src/main/java/com/eko/handlers/OnBegin.kt +92 -0
  9. package/android/src/main/java/com/eko/handlers/OnBeginState.kt +10 -0
  10. package/android/src/main/java/com/eko/handlers/OnProgress.kt +121 -0
  11. package/android/src/main/java/com/eko/handlers/OnProgressState.kt +9 -0
  12. package/android/src/main/java/com/eko/interfaces/BeginCallback.kt +7 -0
  13. package/android/src/main/java/com/eko/interfaces/ProgressCallback.kt +5 -0
  14. package/android/src/main/java/com/eko/utils/FileUtils.kt +62 -0
  15. package/android/src/newarch/java/com/eko/RNBackgroundDownloaderModule.kt +60 -0
  16. package/android/src/oldarch/java/com/eko/RNBackgroundDownloaderModule.kt +70 -0
  17. package/app.plugin.js +3 -0
  18. package/example/android/app/debug.keystore +0 -0
  19. package/ios/.DS_Store +0 -0
  20. package/ios/RNBGDTaskConfig.h +13 -54
  21. package/ios/RNBGDTaskConfig.mm +62 -0
  22. package/ios/RNBackgroundDownloader.h +15 -2
  23. package/ios/RNBackgroundDownloader.mm +1016 -0
  24. package/ios/RNBackgroundDownloader.xcodeproj/project.pbxproj +13 -21
  25. package/ios/RNBackgroundDownloader.xcodeproj/project.xcworkspace/xcuserdata/kesha.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  26. package/package.json +52 -32
  27. package/plugin/build/index.d.ts +3 -0
  28. package/plugin/build/index.js +169 -0
  29. package/react-native-background-downloader.podspec +22 -8
  30. package/src/DownloadTask.ts +184 -0
  31. package/src/NativeRNBackgroundDownloader.ts +83 -0
  32. package/src/index.ts +302 -0
  33. package/src/types.ts +124 -0
  34. package/android/src/main/java/com/eko/Downloader.java +0 -148
  35. package/android/src/main/java/com/eko/RNBGDTaskConfig.java +0 -21
  36. package/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java +0 -562
  37. package/android/src/main/java/com/eko/RNBackgroundDownloaderPackage.java +0 -22
  38. package/android/src/main/java/com/eko/handlers/OnBegin.java +0 -88
  39. package/android/src/main/java/com/eko/handlers/OnBeginState.java +0 -16
  40. package/android/src/main/java/com/eko/handlers/OnProgress.java +0 -123
  41. package/android/src/main/java/com/eko/handlers/OnProgressState.java +0 -14
  42. package/android/src/main/java/com/eko/interfaces/BeginCallback.java +0 -7
  43. package/android/src/main/java/com/eko/interfaces/ProgressCallback.java +0 -5
  44. package/android/src/main/java/com/eko/utils/FileUtils.java +0 -67
  45. package/example/vendor/bundle/ruby/2.7.0/gems/ffi-1.16.3/ext/ffi_c/libffi/.ci/compile +0 -351
  46. package/example/vendor/bundle/ruby/2.7.0/gems/ffi-1.16.3/ext/ffi_c/libffi/testsuite/libffi.bhaible/Makefile +0 -28
  47. package/index.d.ts +0 -142
  48. package/index.ts +0 -172
  49. package/ios/RNBackgroundDownloader.m +0 -630
  50. package/ios/RNBackgroundDownloader.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  51. package/lib/DownloadTask.ts +0 -116
package/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ ## v4.0.0-alpha.0
4
+
5
+ > 📖 **Upgrading from v3.x?** See the [Migration Guide](./MIGRATION.md) for detailed instructions.
6
+
7
+ ### ⚠️ Breaking Changes
8
+
9
+ - **API Renamed:** `checkForExistingDownloads()` → `getExistingDownloadTasks()` - Now returns a Promise with better naming
10
+ - **API Renamed:** `download()` → `createDownloadTask()` - Downloads now require explicit `.start()` call
11
+ - **Download Tasks Start Explicitly:** Tasks created with `createDownloadTask()` are now in `PENDING` state and must call `.start()` to begin downloading
12
+ - **New Config Option:** Added `progressMinBytes` to `setConfig()` - controls minimum bytes change before progress callback fires (default: 1MB)
13
+ - **Source Structure Changed:** Code moved from `lib/` to `src/` directory with proper TypeScript types
14
+
15
+ ### ✨ New Features
16
+
17
+ - **React Native New Architecture Support:** Full TurboModules support for both iOS and Android
18
+ - **Expo Config Plugin:** Added automatic iOS native code integration for Expo projects via `app.plugin.js`
19
+ - **Android Kotlin Migration:** All Java code converted to Kotlin
20
+ - **`maxRedirects` Option:** Configure maximum redirects for Android downloads (resolves #15)
21
+ - **`progressMinBytes` Option:** Hybrid progress reporting - callbacks fire based on time interval OR bytes downloaded
22
+ - **Android 15+ Support:** Added support for 16KB memory page sizes
23
+ - **Architecture Fallback:** Comprehensive x86/ARMv7 support with SharedPreferences fallback
24
+
25
+ ### 🐛 Bug Fixes
26
+
27
+ - **iOS Pause/Resume:** Fixed pause and resume functionality on iOS
28
+ - **RN 0.78+ Compatibility:** Fixed bridge checks with safe emitter checks
29
+ - **New Architecture Events:** Fixed `downloadBegin` and `downloadProgress` events emission
30
+ - **Android Background Downloads:** Fixed completed files not moving to destination
31
+ - **Progress Callback Unknown Total:** Fixed progress callback not firing when total bytes unknown
32
+ - **Android 12 MMKV Crash:** Added robust error handling
33
+ - **`checkForExistingDownloads` TypeError:** Fixed TypeError on Android with architecture fallback
34
+ - **Firebase Performance Compatibility:** Fixed `completeHandler` method compatibility on Android
35
+ - **Slow Connection Handling:** Better handling of slow-responding URLs with timeouts
36
+ - **Android OldArch Export:** Fixed module method export issue (#79)
37
+ - **MMKV Compatibility:** Support for react-native-mmkv 4+ with mmkv-shared dependency
38
+
39
+ ### 📦 Dependencies & Infrastructure
40
+
41
+ - **React Native:** Updated example app to RN 0.81.4
42
+ - **TypeScript:** Full TypeScript types in `src/types.ts`
43
+ - **iOS Native:** Converted from `.m` to `.mm` (Objective-C++)
44
+ - **Package Manager:** Switched to yarn as preferred package manager
45
+
46
+ ### 📚 Documentation
47
+
48
+ - Added documentation for `progressMinBytes` option
49
+ - Updated README for React Native 0.77+ instructions
50
+ - Improved Expo config plugin examples
package/README.md CHANGED
@@ -1,7 +1,32 @@
1
- ![react-native-background-downloader banner](https://d1w2zhnqcy4l8f.cloudfront.net/content/falcon/production/projects/V5EEOX_fast/RNBD-190702083358.png)
1
+
2
+ <p align="center">
3
+ <img width="300" src="https://github.com/user-attachments/assets/25e89808-9eb7-42b2-8031-b48d8c24796c" />
4
+ </p>
2
5
 
3
6
  [![npm version](https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader.svg)](https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader)
4
7
 
8
+ ## 🎉 Version 4.0.0-alpha.0 Released!
9
+
10
+ **v4.0.0-alpha.0** is now available with full **React Native New Architecture (TurboModules)** support!
11
+
12
+ > ⚠️ **Alpha Release:** This version is in alpha state. Please report any issues you encounter.
13
+
14
+ ### What's New
15
+ - ✅ Full TurboModules support for iOS and Android
16
+ - ✅ Expo Config Plugin for automatic iOS setup
17
+ - ✅ Android code converted to Kotlin
18
+ - ✅ Full TypeScript support
19
+ - ✅ New `progressMinBytes` option for hybrid progress reporting
20
+ - ✅ `maxRedirects` option for Android
21
+
22
+ ### Upgrading from v3.x?
23
+ 📖 See the [Migration Guide](./MIGRATION.md) for detailed upgrade instructions and breaking changes.
24
+
25
+ 📋 See the [Changelog](./CHANGELOG.md) for the full list of changes.
26
+
27
+ ### Looking for v3.2.6?
28
+ If you need the previous stable version: [3.2.6 readme](https://github.com/kesha-antonov/react-native-background-downloader/blob/8f4b8a844a2d7f00d1558f6ea65bac94c8dd6fc9/README.md)
29
+
5
30
  # @kesha-antonov/react-native-background-downloader
6
31
 
7
32
  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.
@@ -41,6 +66,35 @@ Then:
41
66
  cd ios && pod install
42
67
  ```
43
68
 
69
+ #### Manual Setup (Advanced)
70
+ If you need to manually configure the package for New Architecture:
71
+
72
+ **iOS**: The library automatically detects New Architecture via compile-time flags.
73
+
74
+ **Android**: For New Architecture, you can optionally use `RNBackgroundDownloaderTurboPackage` instead of the default package:
75
+ ```java
76
+ import com.eko.RNBackgroundDownloaderTurboPackage;
77
+
78
+ // In your MainApplication.java
79
+ @Override
80
+ protected List<ReactPackage> getPackages() {
81
+ return Arrays.<ReactPackage>asList(
82
+ // ... other packages
83
+ new RNBackgroundDownloaderTurboPackage() // For New Architecture
84
+ );
85
+ }
86
+ ```
87
+
88
+ #### Known Issues with New Architecture
89
+ 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.
90
+
91
+ **Workaround:** If you encounter this error frequently with large files, consider:
92
+ 1. Breaking large downloads into smaller chunks
93
+ 2. Implementing retry logic in your app
94
+ 3. Using alternative download strategies for very large files
95
+
96
+ The library now provides enhanced error handling for this specific case with detailed logging and cleanup.
97
+
44
98
  ### Mostly automatic installation
45
99
  Any React Native version **`>= 0.60`** supports autolinking so nothing should be done.
46
100
 
@@ -76,21 +130,99 @@ For anything **`< 0.60`** run the following link command
76
130
  </details>
77
131
 
78
132
  ### iOS - Extra Mandatory Step
79
- In your `AppDelegate.m` add the following code:
80
- ```objc
81
- ...
82
- #import <RNBackgroundDownloader.h>
83
133
 
84
- ...
134
+ #### Option 1: Using Expo Config Plugin (Recommended for Expo/EAS users)
135
+
136
+ If you're using Expo or EAS Build, you can use the included Expo config plugin to automatically configure the iOS native code:
85
137
 
86
- - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
138
+ **In your `app.json`:**
139
+ ```json
87
140
  {
88
- [RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
141
+ "expo": {
142
+ "name": "Your App",
143
+ "plugins": [
144
+ "@kesha-antonov/react-native-background-downloader"
145
+ ]
146
+ }
89
147
  }
148
+ ```
90
149
 
91
- ...
150
+ **Or in your `app.config.js`:**
151
+ ```js
152
+ export default {
153
+ expo: {
154
+ name: "Your App",
155
+ plugins: [
156
+ "@kesha-antonov/react-native-background-downloader"
157
+ ]
158
+ }
159
+ }
92
160
  ```
93
- Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to `pod install` first.
161
+
162
+ The plugin will automatically:
163
+ - Add the required import to your AppDelegate (Objective-C) or Bridging Header (Swift)
164
+ - Add the `handleEventsForBackgroundURLSession` method to your AppDelegate
165
+ - Handle both React Native < 0.77 (Objective-C) and >= 0.77 (Swift) projects
166
+
167
+ After adding the plugin, run:
168
+ ```bash
169
+ expo prebuild --clean
170
+ ```
171
+
172
+ #### Option 2: Manual Setup
173
+
174
+ <details>
175
+ <summary>Manual setup for React Native 0.77+ (Click to expand)</summary>
176
+
177
+ In your project bridging header file (e.g. `ios/{projectName}-Bridging-Header.h`)
178
+ add an import for RNBackgroundDownloader:
179
+
180
+ ```objc
181
+ ...
182
+ #import <RNBackgroundDownloader.h>
183
+ ```
184
+
185
+ Then in your `AppDelegate.swift` add the following method inside of your `AppDelegate` class:
186
+
187
+ ```swift
188
+ ...
189
+
190
+ @main
191
+ class AppDelegate: UIResponder, UIApplicationDelegate
192
+ ...
193
+
194
+ func application(
195
+ _ application: UIApplication,
196
+ handleEventsForBackgroundURLSession identifier: String,
197
+ completionHandler: @escaping () -> Void
198
+ ) {
199
+ RNBackgroundDownloader.setCompletionHandlerWithIdentifier(identifier, completionHandler: completionHandler)
200
+ }
201
+ }
202
+ ...
203
+ ```
204
+ Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to `pod install` first.
205
+ </details>
206
+
207
+ <details>
208
+ <summary>Manual setup for React Native < 0.77 (Click to expand)</summary>
209
+
210
+ In your `AppDelegate.m` add the following code:
211
+ ```objc
212
+ ...
213
+ #import <RNBackgroundDownloader.h>
214
+
215
+ ...
216
+
217
+ - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
218
+ {
219
+ [RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
220
+ }
221
+
222
+ ...
223
+ ```
224
+ Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to `pod install` first.
225
+ </details>
94
226
 
95
227
  ## Usage
96
228
 
@@ -98,11 +230,11 @@ Failing to add this code will result in canceled background downloads. If Xcode
98
230
 
99
231
  ```javascript
100
232
  import { Platform } from 'react-native'
101
- import { download, completeHandler, directories } from '@kesha-antonov/react-native-background-downloader'
233
+ import { createDownloadTask, completeHandler, directories } from '@kesha-antonov/react-native-background-downloader'
102
234
 
103
235
  const jobId = 'file123'
104
236
 
105
- let task = download({
237
+ let task = createDownloadTask({
106
238
  id: jobId,
107
239
  url: 'https://link-to-very.large/file.zip',
108
240
  destination: `${directories.documents}/file.zip`,
@@ -122,16 +254,64 @@ let task = download({
122
254
  console.log('Download canceled due to error: ', { error, errorCode });
123
255
  })
124
256
 
257
+ // starts download
258
+ task.start()
259
+
125
260
  // Pause the task (iOS only)
261
+ // Note: On Android, pause/resume is not supported by DownloadManager
126
262
  task.pause()
127
263
 
128
264
  // Resume after pause (iOS only)
265
+ // Note: On Android, pause/resume is not supported by DownloadManager
129
266
  task.resume()
130
267
 
131
268
  // Cancel the task
132
269
  task.stop()
133
270
  ```
134
271
 
272
+ ### Platform-Aware Pause/Resume
273
+
274
+ ```javascript
275
+ import { Platform } from 'react-native'
276
+ import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
277
+
278
+ const task = createDownloadTask({
279
+ id: 'file123',
280
+ url: 'https://link-to-very.large/file.zip',
281
+ destination: `${directories.documents}/file.zip`,
282
+ metadata: {}
283
+ }).begin(({ expectedBytes, headers }) => {
284
+ console.log(`Going to download ${expectedBytes} bytes!`)
285
+ }).progress(({ bytesDownloaded, bytesTotal }) => {
286
+ console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
287
+ }).done(({ bytesDownloaded, bytesTotal }) => {
288
+ console.log('Download is done!', { bytesDownloaded, bytesTotal })
289
+ }).error(({ error, errorCode }) => {
290
+ console.log('Download canceled due to error: ', { error, errorCode });
291
+ })
292
+
293
+ task.start()
294
+
295
+ // Platform-aware pause/resume handling
296
+ function pauseDownloadTask() {
297
+ if (Platform.OS === 'ios') {
298
+ task.pause()
299
+ console.log('Download paused')
300
+ } else {
301
+ console.log('Pause not supported on Android. Consider using stop() instead.')
302
+ }
303
+ }
304
+
305
+ function resumeDownloadTask() {
306
+ if (Platform.OS === 'ios') {
307
+ task.resume()
308
+ console.log('Download resumed')
309
+ } else {
310
+ console.log('Resume not supported on Android. You may need to restart the download.')
311
+ }
312
+ }
313
+ ```
314
+
135
315
  ### Re-Attaching to background downloads
136
316
 
137
317
  This is the main selling point of this library (but it's free!).
@@ -141,9 +321,9 @@ What happens to your downloads after the OS stopped your app? Well, they are sti
141
321
  Add this code to app's init stage, and you'll never lose a download again!
142
322
 
143
323
  ```javascript
144
- import RNBackgroundDownloader from '@kesha-antonov/react-native-background-downloader'
324
+ import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
145
325
 
146
- let lostTasks = await RNBackgroundDownloader.checkForExistingDownloads()
326
+ let lostTasks = await getExistingDownloadTasks()
147
327
  for (let task of lostTasks) {
148
328
  console.log(`Task ${task.id} was found!`)
149
329
  task.progress(({ bytesDownloaded, bytesTotal }) => {
@@ -161,9 +341,11 @@ for (let task of lostTasks) {
161
341
  ### Using custom headers
162
342
  If you need to send custom headers with your download request, you can do in it 2 ways:
163
343
 
164
- 1) Globally using `RNBackgroundDownloader.setConfig()`:
344
+ 1) Globally using `setConfig()`:
165
345
  ```javascript
166
- RNBackgroundDownloader.setConfig({
346
+ import { setConfig } from '@kesha-antonov/react-native-background-downloader'
347
+
348
+ setConfig({
167
349
  headers: {
168
350
  Authorization: 'Bearer 2we$@$@Ddd223',
169
351
  }
@@ -171,12 +353,14 @@ RNBackgroundDownloader.setConfig({
171
353
  ```
172
354
  This way, all downloads with have the given headers.
173
355
 
174
- 2) Per download by passing a headers object in the options of `RNBackgroundDownloader.download()`:
356
+ 2) Per download by passing a headers object in the options of `createDownloadTask()`:
175
357
  ```javascript
176
- let task = RNBackgroundDownloader.download({
358
+ import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
359
+
360
+ const task = createDownloadTask({
177
361
  id: 'file123',
178
362
  url: 'https://link-to-very.large/file.zip'
179
- destination: `${RNBackgroundDownloader.directories.documents}/file.zip`,
363
+ destination: `${directories.documents}/file.zip`,
180
364
  headers: {
181
365
  Authorization: 'Bearer 2we$@$@Ddd223'
182
366
  }
@@ -189,14 +373,93 @@ let task = RNBackgroundDownloader.download({
189
373
  }).error(({ error, errorCode }) => {
190
374
  console.log('Download canceled due to error: ', { error, errorCode })
191
375
  })
376
+
377
+ task.start()
378
+ ```
379
+ Headers given in `createDownloadTask()` are **merged** with the ones given in `setConfig({ headers: { ... } })`.
380
+
381
+ ### Handling Slow-Responding URLs
382
+
383
+ This library automatically includes connection timeout improvements for slow-responding URLs. By default, the following headers are added to all download requests on Android:
384
+
385
+ - `Connection: keep-alive` - Keeps the connection open for better handling
386
+ - `Keep-Alive: timeout=600, max=1000` - Sets a 10-minute keep-alive timeout
387
+ - `User-Agent: ReactNative-BackgroundDownloader/3.2.6` - Proper user agent for better server compatibility
388
+
389
+ These headers help prevent downloads from getting stuck in "pending" state when servers take several minutes to respond initially. You can override these headers by providing your own in the `headers` option.
390
+
391
+ ### Handling URLs with Many Redirects (Android)
392
+
393
+ Android's DownloadManager has a built-in redirect limit that can cause `ERROR_TOO_MANY_REDIRECTS` for URLs with multiple redirects (common with podcast URLs, tracking services, CDNs, etc.).
394
+
395
+ To handle this, you can use the `maxRedirects` option to pre-resolve redirects before passing the final URL to DownloadManager:
396
+
397
+ ```javascript
398
+ import { Platform } from 'react-native'
399
+ import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
400
+
401
+ // Example: Podcast URL with multiple redirects
402
+ const task = createDownloadTask({
403
+ id: 'podcast-episode',
404
+ url: 'https://pdst.fm/e/chrt.fm/track/479722/arttrk.com/p/example.mp3',
405
+ destination: `${directories.documents}/episode.mp3`,
406
+ maxRedirects: 10, // Follow up to 10 redirects before downloading
407
+ }).begin(({ expectedBytes }) => {
408
+ console.log(`Going to download ${expectedBytes} bytes!`)
409
+ }).progress(({ bytesDownloaded, bytesTotal }) => {
410
+ console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
411
+ }).done(({ bytesDownloaded, bytesTotal }) => {
412
+ console.log('Download is done!', { bytesDownloaded, bytesTotal })
413
+ }).error(({ error, errorCode }) => {
414
+ console.log('Download canceled due to error: ', { error, errorCode })
415
+
416
+ if (errorCode === 1005) { // ERROR_TOO_MANY_REDIRECTS
417
+ console.log('Consider increasing maxRedirects or using a different URL')
418
+ }
419
+ })
420
+
421
+ task.start()
192
422
  ```
193
- Headers given in the `download` function are **merged** with the ones given in `setConfig({ headers: { ... } })`.
423
+
424
+ **Notes on maxRedirects:**
425
+ - Only available on Android (iOS handles redirects automatically)
426
+ - If not specified or set to 0, no redirect resolution is performed
427
+ - Uses HEAD requests to resolve redirects efficiently
428
+ - Falls back to original URL if redirect resolution fails
429
+ - Respects the same headers and timeouts as the main download
194
430
 
195
431
  ## API
196
432
 
197
- ### RNBackgroundDownloader
433
+ ### Named Exports
434
+
435
+ The library exports the following functions and objects:
436
+
437
+ ```typescript
438
+ import {
439
+ setConfig,
440
+ createDownloadTask,
441
+ getExistingDownloadTasks,
442
+ ensureDownloadsAreRunning,
443
+ completeHandler,
444
+ directories
445
+ } from '@kesha-antonov/react-native-background-downloader'
446
+ ```
447
+
448
+ **Default Export:**
449
+
450
+ ```typescript
451
+ import RNBackgroundDownloader from '@kesha-antonov/react-native-background-downloader'
198
452
 
199
- ### `download(options)`
453
+ // Contains all the above as properties:
454
+ RNBackgroundDownloader.setConfig
455
+ RNBackgroundDownloader.createDownloadTask
456
+ RNBackgroundDownloader.getExistingDownloadTasks
457
+ RNBackgroundDownloader.ensureDownloadsAreRunning
458
+ RNBackgroundDownloader.completeHandler
459
+ RNBackgroundDownloader.directories
460
+ ```
461
+
462
+ ### `createDownloadTask(options)`
200
463
 
201
464
  Download a file to destination
202
465
 
@@ -206,11 +469,12 @@ An object containing options properties
206
469
 
207
470
  | Property | Type | Required | Platforms | Info |
208
471
  | ------------- | ------------------------------------------------ | :------: | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
209
- | `id` | String | ✅ | All | A Unique ID to provide for this download. This ID will help to identify the download task when the app re-launches |
472
+ | `id` | String | ✅ | All | A unique ID to provide for this download. This ID will help to identify the download task when the app re-launches |
210
473
  | `url` | String | ✅ | All | URL to file you want to download |
211
- | `destination` | String | ✅ | All | Where to copy the file to once the download is done |
212
- | `metadata` | Object | | All | Data to be preserved on reboot. |
213
- | `headers` | Object | | All | Costume headers to add to the download request. These are merged with the headers given in the `setConfig({ headers: { ... } })` function |
474
+ | `destination` | String | ✅ | All | Where to copy the file to once the download is done. The 'file://' prefix will be automatically removed if present |
475
+ | `metadata` | Record<string, unknown> | | All | Custom data to be preserved across app restarts. Will be serialized to JSON |
476
+ | `headers` | Record<string, string \| null> | | All | Custom headers to add to the download request. These are merged with the headers given in `setConfig({ headers: { ... } })`. Headers with null values will be removed |
477
+ | `maxRedirects` | Number | | Android | Maximum number of redirects to follow before passing URL to DownloadManager. If not specified or 0, no redirect resolution is performed. Helps avoid ERROR_TOO_MANY_REDIRECTS for URLs with many redirects (e.g., podcast URLs) |
214
478
  | `isAllowedOverRoaming` | Boolean | | Android | whether this download may proceed over a roaming connection. By default, roaming is allowed |
215
479
  | `isAllowedOverMetered` | Boolean | | Android | Whether this download may proceed over a metered network connection. By default, metered networks are allowed |
216
480
  | `isNotificationVisible` | Boolean | | Android | Whether to show a download notification or not |
@@ -218,47 +482,65 @@ An object containing options properties
218
482
 
219
483
  **returns**
220
484
 
221
- `DownloadTask` - The download task to control and monitor this download
485
+ `DownloadTask` - The download task to control and monitor this download. Call `task.start()` to begin the download.
222
486
 
223
- ### `checkForExistingDownloads()`
487
+ ### `getExistingDownloadTasks()`
224
488
 
225
- Checks for downloads that ran in background while you app was terminated. And also forces them to resume downloads.
489
+ Checks for downloads that ran in background while your app was terminated.
226
490
 
227
491
  Recommended to run at the init stage of the app.
228
492
 
229
493
  **returns**
230
494
 
231
- `DownloadTask[]` - Array of tasks that were running in the background so you can re-attach callbacks to them
495
+ `Promise<DownloadTask[]>` - A promise that resolves to an array of tasks that were running in the background so you can re-attach callbacks to them
496
+
497
+ ### `setConfig(config)`
498
+
499
+ Sets global configuration for the downloader.
232
500
 
233
- ### `setConfig({})`
501
+ **config**
502
+
503
+ An object containing configuration properties
234
504
 
235
505
  | Name | Type | Info |
236
506
  | -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
237
- | `headers` | Object | optional headers to use in all future downloads |
238
- | `progressInterval` | Number | Interval in which download progress sent from downloader. Number should be >= 250. It's in ms |
239
- | `isLogsEnabled` | Boolean | Enables/disables logs in library |
507
+ | `headers` | Record<string, string \| null> | Optional headers to use in all future downloads. Headers with null values will be removed |
508
+ | `progressInterval` | Number | Interval in milliseconds for download progress updates. Must be >= 250. Default is 1000 |
509
+ | `progressMinBytes` | Number | Minimum number of bytes that must be downloaded before a progress event is emitted. When set to 0, only the percentage threshold (1% change) triggers progress updates. Default is 1048576 (1MB) |
510
+ | `isLogsEnabled` | Boolean | Enables/disables debug logs in library. Default is false |
240
511
 
241
512
  ### DownloadTask
242
513
 
243
- A class representing a download task created by `RNBackgroundDownloader.download`
514
+ A class representing a download task created by `createDownloadTask()`. Note: You must call `task.start()` to begin the download after setting up event handlers.
244
515
 
245
516
  ### `Members`
246
517
  | Name | Type | Info |
247
518
  | -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
248
- | `id` | String | The id you gave the task when calling `RNBackgroundDownloader.download` |
249
- | `metadata` | Object | The metadata you gave the task when calling `RNBackgroundDownloader.download` |
519
+ | `id` | String | The id you gave the task when calling `createDownloadTask` |
520
+ | `metadata` | Record<string, unknown> | The metadata you gave the task when calling `createDownloadTask` |
521
+ | `state` | 'PENDING' \| 'DOWNLOADING' \| 'PAUSED' \| 'DONE' \| 'FAILED' \| 'STOPPED' | Current state of the download task |
250
522
  | `bytesDownloaded` | Number | The number of bytes currently written by the task |
251
523
  | `bytesTotal` | Number | The number bytes expected to be written by this task or more plainly, the file size being downloaded |
524
+ | `downloadParams` | DownloadParams | The download parameters set for this task |
252
525
 
253
- ### `completeHandler(jobId)`
526
+ ### `completeHandler(jobId: string)`
254
527
 
255
528
  Finishes download job and informs OS that app can be closed in background if needed.
256
529
  After finishing download in background you have some time to process your JS logic and finish the job.
257
530
 
258
- ### `ensureDownloadsAreRunning` (iOS only)
531
+ **Parameters:**
532
+ - `jobId` (String) - The ID of the download task to complete
533
+
534
+ **Note:** This should be called after processing your download in the `done` callback to properly signal completion to the OS.
535
+
536
+ ### `ensureDownloadsAreRunning()` (iOS only)
259
537
 
260
538
  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).
261
539
 
540
+ **returns**
541
+
542
+ `Promise<void>` - A promise that resolves when all downloads have been paused and resumed
543
+
262
544
  Here's example of how you can use it:
263
545
 
264
546
  1. When your app just loaded
@@ -266,7 +548,9 @@ Here's example of how you can use it:
266
548
  Either stop all tasks:
267
549
 
268
550
  ```javascript
269
- const tasks = await checkForExistingDownloads()
551
+ import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
552
+
553
+ const tasks = await getExistingDownloadTasks()
270
554
  for (const task of tasks)
271
555
  task.stop()
272
556
  ```
@@ -274,7 +558,9 @@ for (const task of tasks)
274
558
  Or re-attach them:
275
559
 
276
560
  ```javascript
277
- const tasks = await checkForExistingDownloads()
561
+ import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
562
+
563
+ const tasks = await getExistingDownloadTasks()
278
564
  for (const task of tasks) {
279
565
  task.pause()
280
566
  //
@@ -289,6 +575,7 @@ for (const task of tasks) {
289
575
  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!)
290
576
 
291
577
  ```javascript
578
+ import { ensureDownloadsAreRunning } from '@kesha-antonov/react-native-background-downloader'
292
579
 
293
580
  function handleAppStateChange (appState) {
294
581
  if (appState !== 'active')
@@ -309,17 +596,21 @@ All callback methods return the current instance of the `DownloadTask` for chain
309
596
 
310
597
  | Function | Callback Arguments | Info|
311
598
  | ---------- | --------------------------------- | ---- |
312
- | `begin` | { expectedBytes, headers } | Called when the first byte is received. 💡: this is good place to check if the device has enough storage space for this download |
313
- | `progress` | { bytesDownloaded, bytesTotal } | Called at max every 1.5s so you can update your progress bar accordingly |
314
- | `done` | { bytesDownloaded, bytesTotal } | Called when the download is done, the file is at the destination you've set |
315
- | `error` | { error, errorCode } | Called when the download stops due to an error |
599
+ | `begin` | `{ expectedBytes: number, headers: Record<string, string \| null> }` | Called when the first byte is received. 💡: this is good place to check if the device has enough storage space for this download |
600
+ | `progress` | `{ bytesDownloaded: number, bytesTotal: number }` | Called based on progressInterval (default: every 1000ms) so you can update your progress bar accordingly |
601
+ | `done` | `{ bytesDownloaded: number, bytesTotal: number }` | Called when the download is done, the file is at the destination you've set |
602
+ | `error` | `{ error: string, errorCode: number }` | Called when the download stops due to an error |
316
603
 
317
604
  ### `pause()` (iOS only)
318
605
  Pauses the download
319
606
 
607
+ **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.
608
+
320
609
  ### `resume()` (iOS only)
321
610
  Resumes a paused download
322
611
 
612
+ **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.
613
+
323
614
  ### `stop()`
324
615
  Stops the download for good and removes the file that was written so far
325
616
 
@@ -331,6 +622,24 @@ Stops the download for good and removes the file that was written so far
331
622
 
332
623
  An absolute path to the app's documents directory. It is recommended that you use this path as the target of downloaded files.
333
624
 
625
+ ## Platform-Specific Limitations
626
+
627
+ ### Android DownloadManager Limitations
628
+
629
+ The Android implementation uses the system's `DownloadManager` service, which has some limitations compared to iOS:
630
+
631
+ #### Pause/Resume Not Supported
632
+ - **Issue**: Android's DownloadManager does not provide a public API for pausing and resuming downloads
633
+ - **Impact**: Calling `task.pause()` or `task.resume()` on Android will log a warning but not perform any action
634
+ - **Workaround**: If you need to stop a download, use `task.stop()` and restart it later with a new download request
635
+ - **Technical Details**: The private APIs needed for pause/resume functionality are not accessible to third-party applications
636
+
637
+ #### Alternative Approaches for Android
638
+ If pause/resume functionality is critical for your application, consider:
639
+ 1. Using `task.stop()` and tracking progress to restart downloads from where they left off (if the server supports range requests)
640
+ 2. Implementing a custom download solution for Android that doesn't use DownloadManager
641
+ 3. Designing your app flow to minimize the need for pause/resume functionality
642
+
334
643
  ## Rules for proguard-rules.pro
335
644
 
336
645
  In case of error `java.lang.IllegalStateException: TypeToken must be created with a type argument: new TypeToken<...>()` in Android release add this to `proguard-rules.pro`: