@kesha-antonov/react-native-background-downloader 3.2.6 → 4.0.0-alpha.0
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 +347 -44
- package/android/build.gradle +48 -5
- package/android/src/main/java/com/eko/Downloader.kt +133 -0
- package/android/src/main/java/com/eko/RNBGDTaskConfig.kt +12 -0
- package/android/src/main/java/com/eko/RNBackgroundDownloaderModuleImpl.kt +863 -0
- package/android/src/main/java/com/eko/RNBackgroundDownloaderPackage.kt +39 -0
- package/android/src/main/java/com/eko/handlers/OnBegin.kt +92 -0
- package/android/src/main/java/com/eko/handlers/OnBeginState.kt +10 -0
- package/android/src/main/java/com/eko/handlers/OnProgress.kt +121 -0
- package/android/src/main/java/com/eko/handlers/OnProgressState.kt +9 -0
- package/android/src/main/java/com/eko/interfaces/BeginCallback.kt +7 -0
- package/android/src/main/java/com/eko/interfaces/ProgressCallback.kt +5 -0
- package/android/src/main/java/com/eko/utils/FileUtils.kt +62 -0
- package/android/src/newarch/java/com/eko/RNBackgroundDownloaderModule.kt +60 -0
- package/android/src/oldarch/java/com/eko/RNBackgroundDownloaderModule.kt +70 -0
- package/app.plugin.js +3 -0
- package/example/android/app/debug.keystore +0 -0
- package/ios/.DS_Store +0 -0
- package/ios/RNBGDTaskConfig.h +13 -54
- package/ios/RNBGDTaskConfig.mm +62 -0
- package/ios/RNBackgroundDownloader.h +15 -2
- package/ios/RNBackgroundDownloader.mm +973 -0
- package/ios/RNBackgroundDownloader.xcodeproj/project.pbxproj +13 -21
- package/ios/RNBackgroundDownloader.xcodeproj/project.xcworkspace/xcuserdata/kesha.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/package.json +52 -32
- package/plugin/build/index.d.ts +3 -0
- package/plugin/build/index.js +169 -0
- package/react-native-background-downloader.podspec +22 -8
- package/src/DownloadTask.ts +184 -0
- package/src/NativeRNBackgroundDownloader.ts +83 -0
- package/src/index.ts +302 -0
- package/src/types.ts +124 -0
- package/android/src/main/java/com/eko/Downloader.java +0 -148
- package/android/src/main/java/com/eko/RNBGDTaskConfig.java +0 -21
- package/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java +0 -562
- package/android/src/main/java/com/eko/RNBackgroundDownloaderPackage.java +0 -22
- package/android/src/main/java/com/eko/handlers/OnBegin.java +0 -88
- package/android/src/main/java/com/eko/handlers/OnBeginState.java +0 -16
- package/android/src/main/java/com/eko/handlers/OnProgress.java +0 -123
- package/android/src/main/java/com/eko/handlers/OnProgressState.java +0 -14
- package/android/src/main/java/com/eko/interfaces/BeginCallback.java +0 -7
- package/android/src/main/java/com/eko/interfaces/ProgressCallback.java +0 -5
- package/android/src/main/java/com/eko/utils/FileUtils.java +0 -67
- package/example/vendor/bundle/ruby/2.7.0/gems/ffi-1.16.3/ext/ffi_c/libffi/.ci/compile +0 -351
- package/example/vendor/bundle/ruby/2.7.0/gems/ffi-1.16.3/ext/ffi_c/libffi/testsuite/libffi.bhaible/Makefile +0 -28
- package/index.d.ts +0 -142
- package/index.ts +0 -172
- package/ios/RNBackgroundDownloader.m +0 -630
- package/ios/RNBackgroundDownloader.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/lib/DownloadTask.ts +0 -116
package/README.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader)
|
|
4
4
|
|
|
5
|
+
## 🚧👷 Important notice
|
|
6
|
+
|
|
7
|
+
There's currently WIP going on to make the library support New Architecture. If you have any issues, please report them. If you want to contribute, please do so.
|
|
8
|
+
|
|
9
|
+
The most stable version is `3.2.6`. If you want to use the latest version, please be aware that it's a work in progress.
|
|
10
|
+
|
|
11
|
+
Readme for that version: [3.2.6 readme](https://github.com/kesha-antonov/react-native-background-downloader/blob/8f4b8a844a2d7f00d1558f6ea65bac94c8dd6fc9/README.md)
|
|
12
|
+
|
|
13
|
+
I'm working on making the library compatible with the New Architecture while keeping backward compatibility with the old one. I plan to use Nitro Modules so apps on the old architecture can also benefit from the performance improvements.
|
|
14
|
+
|
|
5
15
|
# @kesha-antonov/react-native-background-downloader
|
|
6
16
|
|
|
7
17
|
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 +51,44 @@ Then:
|
|
|
41
51
|
cd ios && pod install
|
|
42
52
|
```
|
|
43
53
|
|
|
54
|
+
### New Architecture Support
|
|
55
|
+
|
|
56
|
+
This library supports React Native's New Architecture (Fabric + TurboModules) starting from React Native 0.70+.
|
|
57
|
+
|
|
58
|
+
#### Automatic Detection
|
|
59
|
+
The library automatically detects whether the New Architecture is enabled in your app and uses the appropriate implementation:
|
|
60
|
+
- **New Architecture**: Uses TurboModules for optimal performance
|
|
61
|
+
- **Legacy Architecture**: Uses the traditional bridge implementation
|
|
62
|
+
|
|
63
|
+
#### Manual Setup (Advanced)
|
|
64
|
+
If you need to manually configure the package for New Architecture:
|
|
65
|
+
|
|
66
|
+
**iOS**: The library automatically detects New Architecture via compile-time flags.
|
|
67
|
+
|
|
68
|
+
**Android**: For New Architecture, you can optionally use `RNBackgroundDownloaderTurboPackage` instead of the default package:
|
|
69
|
+
```java
|
|
70
|
+
import com.eko.RNBackgroundDownloaderTurboPackage;
|
|
71
|
+
|
|
72
|
+
// In your MainApplication.java
|
|
73
|
+
@Override
|
|
74
|
+
protected List<ReactPackage> getPackages() {
|
|
75
|
+
return Arrays.<ReactPackage>asList(
|
|
76
|
+
// ... other packages
|
|
77
|
+
new RNBackgroundDownloaderTurboPackage() // For New Architecture
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Known Issues with New Architecture
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
**Workaround:** If you encounter this error frequently with large files, consider:
|
|
86
|
+
1. Breaking large downloads into smaller chunks
|
|
87
|
+
2. Implementing retry logic in your app
|
|
88
|
+
3. Using alternative download strategies for very large files
|
|
89
|
+
|
|
90
|
+
The library now provides enhanced error handling for this specific case with detailed logging and cleanup.
|
|
91
|
+
|
|
44
92
|
### Mostly automatic installation
|
|
45
93
|
Any React Native version **`>= 0.60`** supports autolinking so nothing should be done.
|
|
46
94
|
|
|
@@ -76,21 +124,99 @@ For anything **`< 0.60`** run the following link command
|
|
|
76
124
|
</details>
|
|
77
125
|
|
|
78
126
|
### iOS - Extra Mandatory Step
|
|
79
|
-
In your `AppDelegate.m` add the following code:
|
|
80
|
-
```objc
|
|
81
|
-
...
|
|
82
|
-
#import <RNBackgroundDownloader.h>
|
|
83
127
|
|
|
84
|
-
|
|
128
|
+
#### Option 1: Using Expo Config Plugin (Recommended for Expo/EAS users)
|
|
129
|
+
|
|
130
|
+
If you're using Expo or EAS Build, you can use the included Expo config plugin to automatically configure the iOS native code:
|
|
85
131
|
|
|
86
|
-
|
|
132
|
+
**In your `app.json`:**
|
|
133
|
+
```json
|
|
87
134
|
{
|
|
88
|
-
|
|
135
|
+
"expo": {
|
|
136
|
+
"name": "Your App",
|
|
137
|
+
"plugins": [
|
|
138
|
+
"@kesha-antonov/react-native-background-downloader"
|
|
139
|
+
]
|
|
140
|
+
}
|
|
89
141
|
}
|
|
142
|
+
```
|
|
90
143
|
|
|
91
|
-
|
|
144
|
+
**Or in your `app.config.js`:**
|
|
145
|
+
```js
|
|
146
|
+
export default {
|
|
147
|
+
expo: {
|
|
148
|
+
name: "Your App",
|
|
149
|
+
plugins: [
|
|
150
|
+
"@kesha-antonov/react-native-background-downloader"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
92
154
|
```
|
|
93
|
-
|
|
155
|
+
|
|
156
|
+
The plugin will automatically:
|
|
157
|
+
- Add the required import to your AppDelegate (Objective-C) or Bridging Header (Swift)
|
|
158
|
+
- Add the `handleEventsForBackgroundURLSession` method to your AppDelegate
|
|
159
|
+
- Handle both React Native < 0.77 (Objective-C) and >= 0.77 (Swift) projects
|
|
160
|
+
|
|
161
|
+
After adding the plugin, run:
|
|
162
|
+
```bash
|
|
163
|
+
expo prebuild --clean
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Option 2: Manual Setup
|
|
167
|
+
|
|
168
|
+
<details>
|
|
169
|
+
<summary>Manual setup for React Native 0.77+ (Click to expand)</summary>
|
|
170
|
+
|
|
171
|
+
In your project bridging header file (e.g. `ios/{projectName}-Bridging-Header.h`)
|
|
172
|
+
add an import for RNBackgroundDownloader:
|
|
173
|
+
|
|
174
|
+
```objc
|
|
175
|
+
...
|
|
176
|
+
#import <RNBackgroundDownloader.h>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Then in your `AppDelegate.swift` add the following method inside of your `AppDelegate` class:
|
|
180
|
+
|
|
181
|
+
```swift
|
|
182
|
+
...
|
|
183
|
+
|
|
184
|
+
@main
|
|
185
|
+
class AppDelegate: UIResponder, UIApplicationDelegate
|
|
186
|
+
...
|
|
187
|
+
|
|
188
|
+
func application(
|
|
189
|
+
_ application: UIApplication,
|
|
190
|
+
handleEventsForBackgroundURLSession identifier: String,
|
|
191
|
+
completionHandler: @escaping () -> Void
|
|
192
|
+
) {
|
|
193
|
+
RNBackgroundDownloader.setCompletionHandlerWithIdentifier(identifier, completionHandler: completionHandler)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
...
|
|
197
|
+
```
|
|
198
|
+
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.
|
|
199
|
+
</details>
|
|
200
|
+
|
|
201
|
+
<details>
|
|
202
|
+
<summary>Manual setup for React Native < 0.77 (Click to expand)</summary>
|
|
203
|
+
|
|
204
|
+
In your `AppDelegate.m` add the following code:
|
|
205
|
+
```objc
|
|
206
|
+
...
|
|
207
|
+
#import <RNBackgroundDownloader.h>
|
|
208
|
+
|
|
209
|
+
...
|
|
210
|
+
|
|
211
|
+
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
|
|
212
|
+
{
|
|
213
|
+
[RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
...
|
|
217
|
+
```
|
|
218
|
+
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.
|
|
219
|
+
</details>
|
|
94
220
|
|
|
95
221
|
## Usage
|
|
96
222
|
|
|
@@ -98,11 +224,11 @@ Failing to add this code will result in canceled background downloads. If Xcode
|
|
|
98
224
|
|
|
99
225
|
```javascript
|
|
100
226
|
import { Platform } from 'react-native'
|
|
101
|
-
import {
|
|
227
|
+
import { createDownloadTask, completeHandler, directories } from '@kesha-antonov/react-native-background-downloader'
|
|
102
228
|
|
|
103
229
|
const jobId = 'file123'
|
|
104
230
|
|
|
105
|
-
let task =
|
|
231
|
+
let task = createDownloadTask({
|
|
106
232
|
id: jobId,
|
|
107
233
|
url: 'https://link-to-very.large/file.zip',
|
|
108
234
|
destination: `${directories.documents}/file.zip`,
|
|
@@ -122,16 +248,64 @@ let task = download({
|
|
|
122
248
|
console.log('Download canceled due to error: ', { error, errorCode });
|
|
123
249
|
})
|
|
124
250
|
|
|
251
|
+
// starts download
|
|
252
|
+
task.start()
|
|
253
|
+
|
|
125
254
|
// Pause the task (iOS only)
|
|
255
|
+
// Note: On Android, pause/resume is not supported by DownloadManager
|
|
126
256
|
task.pause()
|
|
127
257
|
|
|
128
258
|
// Resume after pause (iOS only)
|
|
259
|
+
// Note: On Android, pause/resume is not supported by DownloadManager
|
|
129
260
|
task.resume()
|
|
130
261
|
|
|
131
262
|
// Cancel the task
|
|
132
263
|
task.stop()
|
|
133
264
|
```
|
|
134
265
|
|
|
266
|
+
### Platform-Aware Pause/Resume
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
import { Platform } from 'react-native'
|
|
270
|
+
import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
|
|
271
|
+
|
|
272
|
+
const task = createDownloadTask({
|
|
273
|
+
id: 'file123',
|
|
274
|
+
url: 'https://link-to-very.large/file.zip',
|
|
275
|
+
destination: `${directories.documents}/file.zip`,
|
|
276
|
+
metadata: {}
|
|
277
|
+
}).begin(({ expectedBytes, headers }) => {
|
|
278
|
+
console.log(`Going to download ${expectedBytes} bytes!`)
|
|
279
|
+
}).progress(({ bytesDownloaded, bytesTotal }) => {
|
|
280
|
+
console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
|
|
281
|
+
}).done(({ bytesDownloaded, bytesTotal }) => {
|
|
282
|
+
console.log('Download is done!', { bytesDownloaded, bytesTotal })
|
|
283
|
+
}).error(({ error, errorCode }) => {
|
|
284
|
+
console.log('Download canceled due to error: ', { error, errorCode });
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
task.start()
|
|
288
|
+
|
|
289
|
+
// Platform-aware pause/resume handling
|
|
290
|
+
function pauseDownloadTask() {
|
|
291
|
+
if (Platform.OS === 'ios') {
|
|
292
|
+
task.pause()
|
|
293
|
+
console.log('Download paused')
|
|
294
|
+
} else {
|
|
295
|
+
console.log('Pause not supported on Android. Consider using stop() instead.')
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function resumeDownloadTask() {
|
|
300
|
+
if (Platform.OS === 'ios') {
|
|
301
|
+
task.resume()
|
|
302
|
+
console.log('Download resumed')
|
|
303
|
+
} else {
|
|
304
|
+
console.log('Resume not supported on Android. You may need to restart the download.')
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
135
309
|
### Re-Attaching to background downloads
|
|
136
310
|
|
|
137
311
|
This is the main selling point of this library (but it's free!).
|
|
@@ -141,9 +315,9 @@ What happens to your downloads after the OS stopped your app? Well, they are sti
|
|
|
141
315
|
Add this code to app's init stage, and you'll never lose a download again!
|
|
142
316
|
|
|
143
317
|
```javascript
|
|
144
|
-
import
|
|
318
|
+
import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
|
|
145
319
|
|
|
146
|
-
let lostTasks = await
|
|
320
|
+
let lostTasks = await getExistingDownloadTasks()
|
|
147
321
|
for (let task of lostTasks) {
|
|
148
322
|
console.log(`Task ${task.id} was found!`)
|
|
149
323
|
task.progress(({ bytesDownloaded, bytesTotal }) => {
|
|
@@ -161,9 +335,11 @@ for (let task of lostTasks) {
|
|
|
161
335
|
### Using custom headers
|
|
162
336
|
If you need to send custom headers with your download request, you can do in it 2 ways:
|
|
163
337
|
|
|
164
|
-
1) Globally using `
|
|
338
|
+
1) Globally using `setConfig()`:
|
|
165
339
|
```javascript
|
|
166
|
-
|
|
340
|
+
import { setConfig } from '@kesha-antonov/react-native-background-downloader'
|
|
341
|
+
|
|
342
|
+
setConfig({
|
|
167
343
|
headers: {
|
|
168
344
|
Authorization: 'Bearer 2we$@$@Ddd223',
|
|
169
345
|
}
|
|
@@ -171,12 +347,14 @@ RNBackgroundDownloader.setConfig({
|
|
|
171
347
|
```
|
|
172
348
|
This way, all downloads with have the given headers.
|
|
173
349
|
|
|
174
|
-
2) Per download by passing a headers object in the options of `
|
|
350
|
+
2) Per download by passing a headers object in the options of `createDownloadTask()`:
|
|
175
351
|
```javascript
|
|
176
|
-
|
|
352
|
+
import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
|
|
353
|
+
|
|
354
|
+
const task = createDownloadTask({
|
|
177
355
|
id: 'file123',
|
|
178
356
|
url: 'https://link-to-very.large/file.zip'
|
|
179
|
-
destination: `${
|
|
357
|
+
destination: `${directories.documents}/file.zip`,
|
|
180
358
|
headers: {
|
|
181
359
|
Authorization: 'Bearer 2we$@$@Ddd223'
|
|
182
360
|
}
|
|
@@ -189,14 +367,93 @@ let task = RNBackgroundDownloader.download({
|
|
|
189
367
|
}).error(({ error, errorCode }) => {
|
|
190
368
|
console.log('Download canceled due to error: ', { error, errorCode })
|
|
191
369
|
})
|
|
370
|
+
|
|
371
|
+
task.start()
|
|
372
|
+
```
|
|
373
|
+
Headers given in `createDownloadTask()` are **merged** with the ones given in `setConfig({ headers: { ... } })`.
|
|
374
|
+
|
|
375
|
+
### Handling Slow-Responding URLs
|
|
376
|
+
|
|
377
|
+
This library automatically includes connection timeout improvements for slow-responding URLs. By default, the following headers are added to all download requests on Android:
|
|
378
|
+
|
|
379
|
+
- `Connection: keep-alive` - Keeps the connection open for better handling
|
|
380
|
+
- `Keep-Alive: timeout=600, max=1000` - Sets a 10-minute keep-alive timeout
|
|
381
|
+
- `User-Agent: ReactNative-BackgroundDownloader/3.2.6` - Proper user agent for better server compatibility
|
|
382
|
+
|
|
383
|
+
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.
|
|
384
|
+
|
|
385
|
+
### Handling URLs with Many Redirects (Android)
|
|
386
|
+
|
|
387
|
+
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.).
|
|
388
|
+
|
|
389
|
+
To handle this, you can use the `maxRedirects` option to pre-resolve redirects before passing the final URL to DownloadManager:
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
import { Platform } from 'react-native'
|
|
393
|
+
import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
|
|
394
|
+
|
|
395
|
+
// Example: Podcast URL with multiple redirects
|
|
396
|
+
const task = createDownloadTask({
|
|
397
|
+
id: 'podcast-episode',
|
|
398
|
+
url: 'https://pdst.fm/e/chrt.fm/track/479722/arttrk.com/p/example.mp3',
|
|
399
|
+
destination: `${directories.documents}/episode.mp3`,
|
|
400
|
+
maxRedirects: 10, // Follow up to 10 redirects before downloading
|
|
401
|
+
}).begin(({ expectedBytes }) => {
|
|
402
|
+
console.log(`Going to download ${expectedBytes} bytes!`)
|
|
403
|
+
}).progress(({ bytesDownloaded, bytesTotal }) => {
|
|
404
|
+
console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
|
|
405
|
+
}).done(({ bytesDownloaded, bytesTotal }) => {
|
|
406
|
+
console.log('Download is done!', { bytesDownloaded, bytesTotal })
|
|
407
|
+
}).error(({ error, errorCode }) => {
|
|
408
|
+
console.log('Download canceled due to error: ', { error, errorCode })
|
|
409
|
+
|
|
410
|
+
if (errorCode === 1005) { // ERROR_TOO_MANY_REDIRECTS
|
|
411
|
+
console.log('Consider increasing maxRedirects or using a different URL')
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
task.start()
|
|
192
416
|
```
|
|
193
|
-
|
|
417
|
+
|
|
418
|
+
**Notes on maxRedirects:**
|
|
419
|
+
- Only available on Android (iOS handles redirects automatically)
|
|
420
|
+
- If not specified or set to 0, no redirect resolution is performed
|
|
421
|
+
- Uses HEAD requests to resolve redirects efficiently
|
|
422
|
+
- Falls back to original URL if redirect resolution fails
|
|
423
|
+
- Respects the same headers and timeouts as the main download
|
|
194
424
|
|
|
195
425
|
## API
|
|
196
426
|
|
|
197
|
-
###
|
|
427
|
+
### Named Exports
|
|
428
|
+
|
|
429
|
+
The library exports the following functions and objects:
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
import {
|
|
433
|
+
setConfig,
|
|
434
|
+
createDownloadTask,
|
|
435
|
+
getExistingDownloadTasks,
|
|
436
|
+
ensureDownloadsAreRunning,
|
|
437
|
+
completeHandler,
|
|
438
|
+
directories
|
|
439
|
+
} from '@kesha-antonov/react-native-background-downloader'
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Default Export:**
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import RNBackgroundDownloader from '@kesha-antonov/react-native-background-downloader'
|
|
198
446
|
|
|
199
|
-
|
|
447
|
+
// Contains all the above as properties:
|
|
448
|
+
RNBackgroundDownloader.setConfig
|
|
449
|
+
RNBackgroundDownloader.createDownloadTask
|
|
450
|
+
RNBackgroundDownloader.getExistingDownloadTasks
|
|
451
|
+
RNBackgroundDownloader.ensureDownloadsAreRunning
|
|
452
|
+
RNBackgroundDownloader.completeHandler
|
|
453
|
+
RNBackgroundDownloader.directories
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### `createDownloadTask(options)`
|
|
200
457
|
|
|
201
458
|
Download a file to destination
|
|
202
459
|
|
|
@@ -206,11 +463,12 @@ An object containing options properties
|
|
|
206
463
|
|
|
207
464
|
| Property | Type | Required | Platforms | Info |
|
|
208
465
|
| ------------- | ------------------------------------------------ | :------: | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
209
|
-
| `id` | String | ✅ | All | A
|
|
466
|
+
| `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
467
|
| `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` |
|
|
213
|
-
| `headers` |
|
|
468
|
+
| `destination` | String | ✅ | All | Where to copy the file to once the download is done. The 'file://' prefix will be automatically removed if present |
|
|
469
|
+
| `metadata` | Record<string, unknown> | | All | Custom data to be preserved across app restarts. Will be serialized to JSON |
|
|
470
|
+
| `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 |
|
|
471
|
+
| `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
472
|
| `isAllowedOverRoaming` | Boolean | | Android | whether this download may proceed over a roaming connection. By default, roaming is allowed |
|
|
215
473
|
| `isAllowedOverMetered` | Boolean | | Android | Whether this download may proceed over a metered network connection. By default, metered networks are allowed |
|
|
216
474
|
| `isNotificationVisible` | Boolean | | Android | Whether to show a download notification or not |
|
|
@@ -218,47 +476,65 @@ An object containing options properties
|
|
|
218
476
|
|
|
219
477
|
**returns**
|
|
220
478
|
|
|
221
|
-
`DownloadTask` - The download task to control and monitor this download
|
|
479
|
+
`DownloadTask` - The download task to control and monitor this download. Call `task.start()` to begin the download.
|
|
222
480
|
|
|
223
|
-
### `
|
|
481
|
+
### `getExistingDownloadTasks()`
|
|
224
482
|
|
|
225
|
-
Checks for downloads that ran in background while
|
|
483
|
+
Checks for downloads that ran in background while your app was terminated.
|
|
226
484
|
|
|
227
485
|
Recommended to run at the init stage of the app.
|
|
228
486
|
|
|
229
487
|
**returns**
|
|
230
488
|
|
|
231
|
-
`DownloadTask[]
|
|
489
|
+
`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
|
|
490
|
+
|
|
491
|
+
### `setConfig(config)`
|
|
492
|
+
|
|
493
|
+
Sets global configuration for the downloader.
|
|
232
494
|
|
|
233
|
-
|
|
495
|
+
**config**
|
|
496
|
+
|
|
497
|
+
An object containing configuration properties
|
|
234
498
|
|
|
235
499
|
| Name | Type | Info |
|
|
236
500
|
| -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
|
237
|
-
| `headers` |
|
|
238
|
-
| `progressInterval` | Number | Interval in
|
|
239
|
-
| `
|
|
501
|
+
| `headers` | Record<string, string \| null> | Optional headers to use in all future downloads. Headers with null values will be removed |
|
|
502
|
+
| `progressInterval` | Number | Interval in milliseconds for download progress updates. Must be >= 250. Default is 1000 |
|
|
503
|
+
| `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) |
|
|
504
|
+
| `isLogsEnabled` | Boolean | Enables/disables debug logs in library. Default is false |
|
|
240
505
|
|
|
241
506
|
### DownloadTask
|
|
242
507
|
|
|
243
|
-
A class representing a download task created by `
|
|
508
|
+
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
509
|
|
|
245
510
|
### `Members`
|
|
246
511
|
| Name | Type | Info |
|
|
247
512
|
| -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
|
248
|
-
| `id` | String | The id you gave the task when calling `
|
|
249
|
-
| `metadata` |
|
|
513
|
+
| `id` | String | The id you gave the task when calling `createDownloadTask` |
|
|
514
|
+
| `metadata` | Record<string, unknown> | The metadata you gave the task when calling `createDownloadTask` |
|
|
515
|
+
| `state` | 'PENDING' \| 'DOWNLOADING' \| 'PAUSED' \| 'DONE' \| 'FAILED' \| 'STOPPED' | Current state of the download task |
|
|
250
516
|
| `bytesDownloaded` | Number | The number of bytes currently written by the task |
|
|
251
517
|
| `bytesTotal` | Number | The number bytes expected to be written by this task or more plainly, the file size being downloaded |
|
|
518
|
+
| `downloadParams` | DownloadParams | The download parameters set for this task |
|
|
252
519
|
|
|
253
|
-
### `completeHandler(jobId)`
|
|
520
|
+
### `completeHandler(jobId: string)`
|
|
254
521
|
|
|
255
522
|
Finishes download job and informs OS that app can be closed in background if needed.
|
|
256
523
|
After finishing download in background you have some time to process your JS logic and finish the job.
|
|
257
524
|
|
|
258
|
-
|
|
525
|
+
**Parameters:**
|
|
526
|
+
- `jobId` (String) - The ID of the download task to complete
|
|
527
|
+
|
|
528
|
+
**Note:** This should be called after processing your download in the `done` callback to properly signal completion to the OS.
|
|
529
|
+
|
|
530
|
+
### `ensureDownloadsAreRunning()` (iOS only)
|
|
259
531
|
|
|
260
532
|
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
533
|
|
|
534
|
+
**returns**
|
|
535
|
+
|
|
536
|
+
`Promise<void>` - A promise that resolves when all downloads have been paused and resumed
|
|
537
|
+
|
|
262
538
|
Here's example of how you can use it:
|
|
263
539
|
|
|
264
540
|
1. When your app just loaded
|
|
@@ -266,7 +542,9 @@ Here's example of how you can use it:
|
|
|
266
542
|
Either stop all tasks:
|
|
267
543
|
|
|
268
544
|
```javascript
|
|
269
|
-
|
|
545
|
+
import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
|
|
546
|
+
|
|
547
|
+
const tasks = await getExistingDownloadTasks()
|
|
270
548
|
for (const task of tasks)
|
|
271
549
|
task.stop()
|
|
272
550
|
```
|
|
@@ -274,7 +552,9 @@ for (const task of tasks)
|
|
|
274
552
|
Or re-attach them:
|
|
275
553
|
|
|
276
554
|
```javascript
|
|
277
|
-
|
|
555
|
+
import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
|
|
556
|
+
|
|
557
|
+
const tasks = await getExistingDownloadTasks()
|
|
278
558
|
for (const task of tasks) {
|
|
279
559
|
task.pause()
|
|
280
560
|
//
|
|
@@ -289,6 +569,7 @@ for (const task of tasks) {
|
|
|
289
569
|
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
570
|
|
|
291
571
|
```javascript
|
|
572
|
+
import { ensureDownloadsAreRunning } from '@kesha-antonov/react-native-background-downloader'
|
|
292
573
|
|
|
293
574
|
function handleAppStateChange (appState) {
|
|
294
575
|
if (appState !== 'active')
|
|
@@ -309,17 +590,21 @@ All callback methods return the current instance of the `DownloadTask` for chain
|
|
|
309
590
|
|
|
310
591
|
| Function | Callback Arguments | Info|
|
|
311
592
|
| ---------- | --------------------------------- | ---- |
|
|
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
|
|
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 |
|
|
593
|
+
| `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 |
|
|
594
|
+
| `progress` | `{ bytesDownloaded: number, bytesTotal: number }` | Called based on progressInterval (default: every 1000ms) so you can update your progress bar accordingly |
|
|
595
|
+
| `done` | `{ bytesDownloaded: number, bytesTotal: number }` | Called when the download is done, the file is at the destination you've set |
|
|
596
|
+
| `error` | `{ error: string, errorCode: number }` | Called when the download stops due to an error |
|
|
316
597
|
|
|
317
598
|
### `pause()` (iOS only)
|
|
318
599
|
Pauses the download
|
|
319
600
|
|
|
601
|
+
**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.
|
|
602
|
+
|
|
320
603
|
### `resume()` (iOS only)
|
|
321
604
|
Resumes a paused download
|
|
322
605
|
|
|
606
|
+
**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.
|
|
607
|
+
|
|
323
608
|
### `stop()`
|
|
324
609
|
Stops the download for good and removes the file that was written so far
|
|
325
610
|
|
|
@@ -331,6 +616,24 @@ Stops the download for good and removes the file that was written so far
|
|
|
331
616
|
|
|
332
617
|
An absolute path to the app's documents directory. It is recommended that you use this path as the target of downloaded files.
|
|
333
618
|
|
|
619
|
+
## Platform-Specific Limitations
|
|
620
|
+
|
|
621
|
+
### Android DownloadManager Limitations
|
|
622
|
+
|
|
623
|
+
The Android implementation uses the system's `DownloadManager` service, which has some limitations compared to iOS:
|
|
624
|
+
|
|
625
|
+
#### Pause/Resume Not Supported
|
|
626
|
+
- **Issue**: Android's DownloadManager does not provide a public API for pausing and resuming downloads
|
|
627
|
+
- **Impact**: Calling `task.pause()` or `task.resume()` on Android will log a warning but not perform any action
|
|
628
|
+
- **Workaround**: If you need to stop a download, use `task.stop()` and restart it later with a new download request
|
|
629
|
+
- **Technical Details**: The private APIs needed for pause/resume functionality are not accessible to third-party applications
|
|
630
|
+
|
|
631
|
+
#### Alternative Approaches for Android
|
|
632
|
+
If pause/resume functionality is critical for your application, consider:
|
|
633
|
+
1. Using `task.stop()` and tracking progress to restart downloads from where they left off (if the server supports range requests)
|
|
634
|
+
2. Implementing a custom download solution for Android that doesn't use DownloadManager
|
|
635
|
+
3. Designing your app flow to minimize the need for pause/resume functionality
|
|
636
|
+
|
|
334
637
|
## Rules for proguard-rules.pro
|
|
335
638
|
|
|
336
639
|
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`:
|
package/android/build.gradle
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
|
+
apply plugin: 'kotlin-android'
|
|
3
|
+
|
|
4
|
+
def isNewArchitectureEnabled() {
|
|
5
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
6
|
+
}
|
|
2
7
|
|
|
3
8
|
def safeExtGet(prop, fallback) {
|
|
4
9
|
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
@@ -11,19 +16,57 @@ android {
|
|
|
11
16
|
namespace "com.eko"
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
compileSdkVersion safeExtGet('compileSdkVersion',
|
|
19
|
+
compileSdkVersion safeExtGet('compileSdkVersion', 36)
|
|
15
20
|
|
|
16
21
|
defaultConfig {
|
|
17
|
-
minSdkVersion safeExtGet('minSdkVersion',
|
|
18
|
-
targetSdkVersion safeExtGet('targetSdkVersion',
|
|
22
|
+
minSdkVersion safeExtGet('minSdkVersion', 24)
|
|
23
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 36)
|
|
19
24
|
versionCode 1
|
|
20
25
|
versionName '1.0'
|
|
26
|
+
|
|
27
|
+
// Support for 16KB memory page sizes (Android 15+)
|
|
28
|
+
ndk {
|
|
29
|
+
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
buildFeatures {
|
|
36
|
+
buildConfig true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
sourceSets {
|
|
40
|
+
main {
|
|
41
|
+
if (isNewArchitectureEnabled()) {
|
|
42
|
+
java.srcDirs += ['src/newarch/java']
|
|
43
|
+
} else {
|
|
44
|
+
java.srcDirs += ['src/oldarch/java']
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Configure packaging options for 16KB page size support
|
|
50
|
+
packagingOptions {
|
|
51
|
+
jniLibs {
|
|
52
|
+
useLegacyPackaging = false
|
|
53
|
+
}
|
|
21
54
|
}
|
|
22
55
|
}
|
|
23
56
|
|
|
57
|
+
repositories {
|
|
58
|
+
mavenCentral()
|
|
59
|
+
google()
|
|
60
|
+
}
|
|
61
|
+
|
|
24
62
|
dependencies {
|
|
25
63
|
//noinspection GradleDynamicVersion
|
|
26
64
|
implementation 'com.facebook.react:react-native:+'
|
|
27
|
-
|
|
28
|
-
|
|
65
|
+
|
|
66
|
+
// MMKV dependency updated for 16KB page size support
|
|
67
|
+
// MMKV 2.2.0+ includes support for 16KB memory page sizes required by Android 15+
|
|
68
|
+
// Uses mmkv-shared to maintain compatibility with react-native-mmkv 4+
|
|
69
|
+
implementation 'com.tencent:mmkv-shared:2.2.0'
|
|
70
|
+
|
|
71
|
+
implementation 'com.google.code.gson:gson:2.12.1'
|
|
29
72
|
}
|