@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.
- package/CHANGELOG.md +50 -0
- package/README.md +354 -45
- 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 +1016 -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/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
|
-
|
|
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
|
[](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
|
-
|
|
138
|
+
**In your `app.json`:**
|
|
139
|
+
```json
|
|
87
140
|
{
|
|
88
|
-
|
|
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
|
-
|
|
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 {
|
|
233
|
+
import { createDownloadTask, completeHandler, directories } from '@kesha-antonov/react-native-background-downloader'
|
|
102
234
|
|
|
103
235
|
const jobId = 'file123'
|
|
104
236
|
|
|
105
|
-
let task =
|
|
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
|
|
324
|
+
import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
|
|
145
325
|
|
|
146
|
-
let lostTasks = await
|
|
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 `
|
|
344
|
+
1) Globally using `setConfig()`:
|
|
165
345
|
```javascript
|
|
166
|
-
|
|
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 `
|
|
356
|
+
2) Per download by passing a headers object in the options of `createDownloadTask()`:
|
|
175
357
|
```javascript
|
|
176
|
-
|
|
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: `${
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
|
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` |
|
|
213
|
-
| `headers` |
|
|
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
|
-
### `
|
|
487
|
+
### `getExistingDownloadTasks()`
|
|
224
488
|
|
|
225
|
-
Checks for downloads that ran in background while
|
|
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[]
|
|
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
|
-
|
|
501
|
+
**config**
|
|
502
|
+
|
|
503
|
+
An object containing configuration properties
|
|
234
504
|
|
|
235
505
|
| Name | Type | Info |
|
|
236
506
|
| -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
|
237
|
-
| `headers` |
|
|
238
|
-
| `progressInterval` | Number | Interval in
|
|
239
|
-
| `
|
|
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 `
|
|
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 `
|
|
249
|
-
| `metadata` |
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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`:
|