@kesha-antonov/react-native-background-downloader 4.4.0 โ 4.4.4
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 +298 -483
- package/android/build.gradle +8 -0
- package/android/src/main/java/com/eko/DownloadConstants.kt +3 -3
- package/android/src/main/java/com/eko/Downloader.kt +44 -6
- package/android/src/main/java/com/eko/ProgressReporter.kt +6 -2
- package/android/src/main/java/com/eko/RNBGDUploadTaskConfig.kt +1 -0
- package/android/src/main/java/com/eko/RNBackgroundDownloaderModuleImpl.kt +88 -9
- package/android/src/main/java/com/eko/Uploader.kt +5 -1
- package/android/src/main/java/com/eko/utils/StorageManager.kt +175 -1
- package/ios/RNBGDTaskConfig.h +1 -0
- package/ios/RNBGDTaskConfig.mm +3 -0
- package/ios/RNBackgroundDownloader.mm +212 -6
- package/package.json +1 -1
- package/plugin/build/index.d.ts +16 -7
- package/plugin/build/index.js +39 -5
- package/react-native-background-downloader.podspec +3 -3
package/README.md
CHANGED
|
@@ -1,141 +1,99 @@
|
|
|
1
|
-
|
|
2
1
|
<p align="center">
|
|
3
2
|
<img width="300" src="https://github.com/user-attachments/assets/25e89808-9eb7-42b2-8031-b48d8c24796c" />
|
|
4
3
|
</p>
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
- โ
Android code converted to Kotlin
|
|
16
|
-
- โ
Full TypeScript support
|
|
17
|
-
- โ
New `progressMinBytes` option for hybrid progress reporting
|
|
18
|
-
- โ
`maxRedirects` option for Android
|
|
19
|
-
|
|
20
|
-
### Upgrading from v3.x?
|
|
21
|
-
๐ See the [Migration Guide](./MIGRATION.md) for detailed upgrade instructions and breaking changes.
|
|
22
|
-
|
|
23
|
-
๐ See the [Changelog](./CHANGELOG.md) for the full list of changes.
|
|
24
|
-
|
|
25
|
-
### Looking for v3.2.6?
|
|
26
|
-
If you need the previous stable version: [3.2.6 readme](https://github.com/kesha-antonov/react-native-background-downloader/blob/8f4b8a844a2d7f00d1558f6ea65bac94c8dd6fc9/README.md)
|
|
27
|
-
|
|
28
|
-
# @kesha-antonov/react-native-background-downloader
|
|
29
|
-
|
|
30
|
-
A library for React-Native to help you download and upload large files on iOS and Android both in the foreground and most importantly in the background.
|
|
31
|
-
|
|
32
|
-
### Why?
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader"><img src="https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader.svg" alt="npm version"></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/@kesha-antonov/react-native-background-downloader"><img src="https://img.shields.io/npm/dm/@kesha-antonov/react-native-background-downloader.svg" alt="npm downloads"></a>
|
|
8
|
+
<a href="https://github.com/kesha-antonov/react-native-background-downloader/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="license"></a>
|
|
9
|
+
<img src="https://img.shields.io/badge/platforms-iOS%20%7C%20Android-lightgrey.svg" alt="platforms">
|
|
10
|
+
<img src="https://img.shields.io/badge/TypeScript-supported-blue.svg" alt="TypeScript">
|
|
11
|
+
<img src="https://img.shields.io/badge/Expo-compatible-000020.svg" alt="Expo compatible">
|
|
12
|
+
<img src="https://img.shields.io/badge/New%20Architecture-supported-green.svg" alt="New Architecture">
|
|
13
|
+
</p>
|
|
33
14
|
|
|
34
|
-
|
|
15
|
+
# React Native Background Downloader
|
|
35
16
|
|
|
36
|
-
|
|
17
|
+
Download and upload large files on iOS & Android โ even when your app is in the background or terminated.
|
|
37
18
|
|
|
38
|
-
|
|
19
|
+
## โจ Features
|
|
39
20
|
|
|
40
|
-
|
|
21
|
+
- ๐ฅ **Background Downloads** - Downloads continue even when app is in background or terminated
|
|
22
|
+
- ๐ค **Background Uploads** - Upload files reliably in the background
|
|
23
|
+
- โธ๏ธ **Pause/Resume** - Full pause and resume support on both iOS and Android
|
|
24
|
+
- ๐ **Re-attach to Downloads** - Reconnect to ongoing downloads after app restart
|
|
25
|
+
- ๐ **Progress Tracking** - Real-time progress updates with customizable intervals
|
|
26
|
+
- ๐ **Custom Headers** - Support for authentication and custom request headers
|
|
27
|
+
- ๐ฑ **Expo Support** - Config plugin for easy Expo integration
|
|
28
|
+
- โก **New Architecture** - Full TurboModules support for React Native
|
|
29
|
+
- ๐ **TypeScript** - Complete TypeScript definitions included
|
|
41
30
|
|
|
42
|
-
|
|
31
|
+
## Why?
|
|
43
32
|
|
|
44
|
-
|
|
33
|
+
**The Problem:** Standard network requests in React Native are tied to your app's lifecycle. When the user switches to another app or the OS terminates your app to free memory, your downloads stop. For small files this is fine, but for large files (videos, podcasts, documents) this creates a frustrating user experience.
|
|
45
34
|
|
|
46
|
-
-
|
|
47
|
-
- [
|
|
48
|
-
|
|
49
|
-
- [Uploading](#uploading-a-file)
|
|
50
|
-
- [API](#api)
|
|
51
|
-
- [Constants](#constants)
|
|
35
|
+
**The Solution:** Both iOS and Android provide system-level APIs for background file transfers:
|
|
36
|
+
- **iOS:** [`NSURLSession`](https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background) - handles downloads in a separate process, continuing even after your app is terminated
|
|
37
|
+
- **Android:** A combination of [`DownloadManager`](https://developer.android.com/reference/android/app/DownloadManager) for system-managed downloads, [Foreground Services](https://developer.android.com/develop/background-work/services/foreground-services) for pause/resume support, and [MMKV](https://github.com/Tencent/MMKV) for persistent state storage
|
|
52
38
|
|
|
53
|
-
|
|
39
|
+
**The Challenge:** These APIs are powerful but complex. Downloads run in a separate process, so your app might restart from scratch while downloads are still in progress. Keeping your UI in sync with background downloads requires careful state management.
|
|
54
40
|
|
|
55
|
-
|
|
41
|
+
**This Library:** `@kesha-antonov/react-native-background-downloader` wraps these native APIs in a simple, unified JavaScript interface. Start a download, close your app, reopen it hours later, and seamlessly reconnect to your ongoing downloads with a single function call.
|
|
56
42
|
|
|
57
|
-
|
|
58
|
-
yarn add @kesha-antonov/react-native-background-downloader
|
|
59
|
-
```
|
|
43
|
+
## Table of Contents
|
|
60
44
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
45
|
+
- [React Native Background Downloader](#react-native-background-downloader)
|
|
46
|
+
- [โจ Features](#-features)
|
|
47
|
+
- [Why?](#why)
|
|
48
|
+
- [Table of Contents](#table-of-contents)
|
|
49
|
+
- [Requirements](#requirements)
|
|
50
|
+
- [Installation](#installation)
|
|
51
|
+
- [Expo Projects](#expo-projects)
|
|
52
|
+
- [Bare React Native Projects](#bare-react-native-projects)
|
|
53
|
+
- [Usage](#usage)
|
|
54
|
+
- [Downloading a file](#downloading-a-file)
|
|
55
|
+
- [Re-Attaching to background tasks](#re-attaching-to-background-tasks)
|
|
56
|
+
- [Advanced Configuration](#advanced-configuration)
|
|
57
|
+
- [Max Parallel Downloads (iOS only)](#max-parallel-downloads-ios-only)
|
|
58
|
+
- [Cellular/WiFi Restrictions](#cellularwifi-restrictions)
|
|
59
|
+
- [API](#api)
|
|
60
|
+
- [Quick Reference](#quick-reference)
|
|
61
|
+
- [Platform Notes](#platform-notes)
|
|
62
|
+
- [Troubleshooting](#troubleshooting)
|
|
63
|
+
- [Example App](#example-app)
|
|
64
|
+
- [Use Cases](#use-cases)
|
|
65
|
+
- [Migration Guide](#migration-guide)
|
|
66
|
+
- [Contributing](#contributing)
|
|
67
|
+
- [Development Setup](#development-setup)
|
|
68
|
+
- [Authors](#authors)
|
|
69
|
+
- [License](#license)
|
|
65
70
|
|
|
66
|
-
|
|
71
|
+
## Requirements
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
| Requirement | Version |
|
|
74
|
+
|-------------|--------|
|
|
75
|
+
| React Native | >= 0.70.0 |
|
|
76
|
+
| iOS | >= 15.1 |
|
|
77
|
+
| Android | API 24+ (Android 7.0) |
|
|
78
|
+
| Expo | SDK 50+ (with config plugin) |
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
<summary>Manual Setup (Advanced)</summary>
|
|
80
|
+
> **Note:** For older React Native versions (0.57.0 - 0.69.x), use version 2.x of this library.
|
|
74
81
|
|
|
75
|
-
|
|
82
|
+
## Installation
|
|
76
83
|
|
|
77
|
-
|
|
84
|
+
### Expo Projects
|
|
78
85
|
|
|
79
|
-
**
|
|
80
|
-
```java
|
|
81
|
-
import com.eko.RNBackgroundDownloaderTurboPackage;
|
|
86
|
+
**Step 1:** Install the package
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
@
|
|
85
|
-
protected List<ReactPackage> getPackages() {
|
|
86
|
-
return Arrays.<ReactPackage>asList(
|
|
87
|
-
// ... other packages
|
|
88
|
-
new RNBackgroundDownloaderTurboPackage() // For New Architecture
|
|
89
|
-
);
|
|
90
|
-
}
|
|
88
|
+
```bash
|
|
89
|
+
npx expo install @kesha-antonov/react-native-background-downloader
|
|
91
90
|
```
|
|
92
|
-
</details>
|
|
93
|
-
|
|
94
|
-
### Mostly automatic installation
|
|
95
|
-
Any React Native version **`>= 0.60`** supports autolinking so nothing should be done.
|
|
96
|
-
|
|
97
|
-
For anything **`< 0.60`** run the following link command
|
|
98
|
-
|
|
99
|
-
`$ react-native link @kesha-antonov/react-native-background-downloader`
|
|
100
|
-
|
|
101
|
-
### Manual installation
|
|
102
|
-
|
|
103
|
-
<details>
|
|
104
91
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
1. In XCode, in the project navigator, right click `Libraries` โ `Add Files to [your project's name]`
|
|
108
|
-
2. Go to `node_modules` โ `@kesha-antonov/react-native-background-downloader` and add `RNBackgroundDownloader.xcodeproj`
|
|
109
|
-
3. In XCode, in the project navigator, select your project. Add `libRNBackgroundDownloader.a` to your project's `Build Phases` โ `Link Binary With Libraries`
|
|
110
|
-
4. Run your project (`Cmd+R`)
|
|
111
|
-
|
|
112
|
-
#### Android
|
|
113
|
-
|
|
114
|
-
1. Open up `android/app/src/main/java/[...]/MainActivity.java`
|
|
115
|
-
- Add `import com.eko.RNBackgroundDownloaderPackage;` to the imports at the top of the file
|
|
116
|
-
- Add `new RNBackgroundDownloaderPackage()` to the list returned by the `getPackages()` method
|
|
117
|
-
2. Append the following lines to `android/settings.gradle`:
|
|
118
|
-
```
|
|
119
|
-
include ':react-native-background-downloader'
|
|
120
|
-
project(':react-native-background-downloader').projectDir = new File(rootProject.projectDir, '../node_modules/@kesha-antonov/react-native-background-downloader/android')
|
|
121
|
-
```
|
|
122
|
-
3. Insert the following lines inside the dependencies block in `android/app/build.gradle`:
|
|
123
|
-
```
|
|
124
|
-
compile project(':react-native-background-downloader')
|
|
125
|
-
```
|
|
126
|
-
</details>
|
|
127
|
-
|
|
128
|
-
### iOS - Extra Mandatory Step
|
|
129
|
-
|
|
130
|
-
#### Option 1: Using Expo Config Plugin (Recommended for Expo/EAS users)
|
|
131
|
-
|
|
132
|
-
If you're using Expo or EAS Build, you can use the included Expo config plugin to automatically configure the native code:
|
|
92
|
+
**Step 2:** Add the config plugin to your `app.json` or `app.config.js`:
|
|
133
93
|
|
|
134
|
-
**In your `app.json`:**
|
|
135
94
|
```json
|
|
136
95
|
{
|
|
137
96
|
"expo": {
|
|
138
|
-
"name": "Your App",
|
|
139
97
|
"plugins": [
|
|
140
98
|
"@kesha-antonov/react-native-background-downloader"
|
|
141
99
|
]
|
|
@@ -143,33 +101,16 @@ If you're using Expo or EAS Build, you can use the included Expo config plugin t
|
|
|
143
101
|
}
|
|
144
102
|
```
|
|
145
103
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
export default {
|
|
149
|
-
expo: {
|
|
150
|
-
name: "Your App",
|
|
151
|
-
plugins: [
|
|
152
|
-
"@kesha-antonov/react-native-background-downloader"
|
|
153
|
-
]
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
**Plugin Options:**
|
|
159
|
-
|
|
160
|
-
You can customize the plugin behavior with options:
|
|
104
|
+
<details>
|
|
105
|
+
<summary><strong>Plugin Options (optional)</strong></summary>
|
|
161
106
|
|
|
162
107
|
```js
|
|
163
108
|
// app.config.js
|
|
164
109
|
export default {
|
|
165
110
|
expo: {
|
|
166
|
-
name: "Your App",
|
|
167
111
|
plugins: [
|
|
168
112
|
["@kesha-antonov/react-native-background-downloader", {
|
|
169
|
-
//
|
|
170
|
-
addMmkvDependency: true,
|
|
171
|
-
// Customize the MMKV version (default: '2.2.4')
|
|
172
|
-
mmkvVersion: "2.2.4"
|
|
113
|
+
mmkvVersion: "2.2.4" // Customize MMKV version on Android
|
|
173
114
|
}]
|
|
174
115
|
]
|
|
175
116
|
}
|
|
@@ -178,75 +119,94 @@ export default {
|
|
|
178
119
|
|
|
179
120
|
| Option | Type | Default | Description |
|
|
180
121
|
|--------|------|---------|-------------|
|
|
181
|
-
| `
|
|
182
|
-
| `mmkvVersion` | string | `'2.2.4'` | The version of MMKV to use on Android. |
|
|
122
|
+
| `mmkvVersion` | string | `'2.2.4'` | The version of [MMKV](https://github.com/Tencent/MMKV/releases) to use on Android. |
|
|
183
123
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
- **iOS:** Handle both React Native < 0.77 (Objective-C) and >= 0.77 (Swift) projects
|
|
188
|
-
- **Android:** Add the required MMKV dependency (unless `addMmkvDependency: false`)
|
|
124
|
+
</details>
|
|
125
|
+
|
|
126
|
+
**Step 3:** Rebuild your app
|
|
189
127
|
|
|
190
|
-
After adding the plugin, run:
|
|
191
128
|
```bash
|
|
192
|
-
expo prebuild --clean
|
|
129
|
+
npx expo prebuild --clean
|
|
130
|
+
npx expo run:ios # or npx expo run:android
|
|
193
131
|
```
|
|
194
132
|
|
|
195
|
-
|
|
133
|
+
The plugin automatically handles:
|
|
134
|
+
- **iOS:** Adding the required `handleEventsForBackgroundURLSession` method to AppDelegate
|
|
135
|
+
- **Android:** Adding the required MMKV dependency
|
|
196
136
|
|
|
197
|
-
|
|
198
|
-
<summary>Manual setup for React Native 0.77+ (Click to expand)</summary>
|
|
137
|
+
---
|
|
199
138
|
|
|
200
|
-
|
|
201
|
-
add an import for RNBackgroundDownloader:
|
|
139
|
+
### Bare React Native Projects
|
|
202
140
|
|
|
203
|
-
|
|
204
|
-
...
|
|
205
|
-
#import <RNBackgroundDownloader.h>
|
|
206
|
-
```
|
|
141
|
+
**Step 1:** Install the package
|
|
207
142
|
|
|
208
|
-
|
|
143
|
+
```bash
|
|
144
|
+
# Using yarn
|
|
145
|
+
yarn add @kesha-antonov/react-native-background-downloader
|
|
209
146
|
|
|
210
|
-
|
|
211
|
-
|
|
147
|
+
# Using npm
|
|
148
|
+
npm install @kesha-antonov/react-native-background-downloader
|
|
149
|
+
```
|
|
212
150
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
151
|
+
**Step 2:** Install iOS pods
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
cd ios && pod install && cd ..
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Step 3:** Configure iOS AppDelegate
|
|
158
|
+
|
|
159
|
+
<details>
|
|
160
|
+
<summary><strong>React Native 0.77+ (Swift)</strong></summary>
|
|
161
|
+
|
|
162
|
+
In your project bridging header file (e.g. `ios/{projectName}-Bridging-Header.h`):
|
|
163
|
+
|
|
164
|
+
```objc
|
|
165
|
+
#import <RNBackgroundDownloader.h>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
In your `AppDelegate.swift`:
|
|
169
|
+
|
|
170
|
+
```swift
|
|
171
|
+
func application(
|
|
172
|
+
_ application: UIApplication,
|
|
173
|
+
handleEventsForBackgroundURLSession identifier: String,
|
|
174
|
+
completionHandler: @escaping () -> Void
|
|
175
|
+
) {
|
|
176
|
+
RNBackgroundDownloader.setCompletionHandlerWithIdentifier(identifier, completionHandler: completionHandler)
|
|
177
|
+
}
|
|
178
|
+
```
|
|
216
179
|
|
|
217
|
-
func application(
|
|
218
|
-
_ application: UIApplication,
|
|
219
|
-
handleEventsForBackgroundURLSession identifier: String,
|
|
220
|
-
completionHandler: @escaping () -> Void
|
|
221
|
-
) {
|
|
222
|
-
RNBackgroundDownloader.setCompletionHandlerWithIdentifier(identifier, completionHandler: completionHandler)
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
...
|
|
226
|
-
```
|
|
227
|
-
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.
|
|
228
180
|
</details>
|
|
229
181
|
|
|
230
182
|
<details>
|
|
231
|
-
|
|
183
|
+
<summary><strong>React Native < 0.77 (Objective-C)</strong></summary>
|
|
232
184
|
|
|
233
|
-
|
|
234
|
-
```objc
|
|
235
|
-
...
|
|
236
|
-
#import <RNBackgroundDownloader.h>
|
|
185
|
+
In your `AppDelegate.m`:
|
|
237
186
|
|
|
238
|
-
|
|
187
|
+
```objc
|
|
188
|
+
#import <RNBackgroundDownloader.h>
|
|
239
189
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
190
|
+
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
|
|
191
|
+
{
|
|
192
|
+
[RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
|
|
193
|
+
}
|
|
194
|
+
```
|
|
244
195
|
|
|
245
|
-
...
|
|
246
|
-
```
|
|
247
|
-
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.
|
|
248
196
|
</details>
|
|
249
197
|
|
|
198
|
+
**Step 4:** Configure Android MMKV dependency
|
|
199
|
+
|
|
200
|
+
Add MMKV to your `android/app/build.gradle`:
|
|
201
|
+
|
|
202
|
+
```gradle
|
|
203
|
+
dependencies {
|
|
204
|
+
implementation 'com.tencent:mmkv-shared:2.2.4'
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
> **Note:** If you're already using [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv) in your project, skip this step โ it already includes MMKV.
|
|
209
|
+
|
|
250
210
|
## Usage
|
|
251
211
|
|
|
252
212
|
### Downloading a file
|
|
@@ -292,33 +252,56 @@ await task.resume()
|
|
|
292
252
|
await task.stop()
|
|
293
253
|
```
|
|
294
254
|
|
|
295
|
-
### Re-Attaching to background
|
|
255
|
+
### Re-Attaching to background tasks
|
|
256
|
+
|
|
257
|
+
The killer feature of this library: **reconnect to downloads and uploads that continued running while your app was closed**, or **resume paused tasks from a previous session**.
|
|
296
258
|
|
|
297
|
-
|
|
259
|
+
When the OS terminates your app to free memory, background transfers keep running. When your app restarts, call `getExistingDownloadTasks()` or `getExistingUploadTasks()` to get back in sync. Paused tasks are also preserved and can be resumed with `task.resume()`.
|
|
298
260
|
|
|
299
|
-
|
|
261
|
+
> **๐ก Tip:** Use meaningful task IDs (not random UUIDs) so you can match tasks to your UI components after restart.
|
|
300
262
|
|
|
301
|
-
|
|
263
|
+
**Downloads:**
|
|
302
264
|
|
|
303
265
|
```javascript
|
|
304
266
|
import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'
|
|
305
267
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
268
|
+
const lostTasks = await getExistingDownloadTasks()
|
|
269
|
+
|
|
270
|
+
for (const task of lostTasks) {
|
|
271
|
+
console.log(`Found download: ${task.id}`)
|
|
272
|
+
|
|
309
273
|
task.progress(({ bytesDownloaded, bytesTotal }) => {
|
|
310
274
|
console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
|
|
311
275
|
}).done(({ location, bytesDownloaded, bytesTotal }) => {
|
|
312
|
-
console.log('Download
|
|
276
|
+
console.log('Download complete!', { location, bytesDownloaded, bytesTotal })
|
|
313
277
|
}).error(({ error, errorCode }) => {
|
|
314
|
-
console.log('Download
|
|
278
|
+
console.log('Download failed:', { error, errorCode })
|
|
315
279
|
})
|
|
316
280
|
}
|
|
317
281
|
```
|
|
318
282
|
|
|
319
|
-
|
|
283
|
+
**Uploads:**
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
import { getExistingUploadTasks } from '@kesha-antonov/react-native-background-downloader'
|
|
287
|
+
|
|
288
|
+
const lostUploads = await getExistingUploadTasks()
|
|
289
|
+
|
|
290
|
+
for (const task of lostUploads) {
|
|
291
|
+
console.log(`Found upload: ${task.id}`)
|
|
320
292
|
|
|
321
|
-
|
|
293
|
+
task.progress(({ bytesUploaded, bytesTotal }) => {
|
|
294
|
+
console.log(`Uploaded: ${bytesUploaded / bytesTotal * 100}%`)
|
|
295
|
+
}).done(({ responseCode, responseBody }) => {
|
|
296
|
+
console.log('Upload complete!', { responseCode, responseBody })
|
|
297
|
+
}).error(({ error, errorCode }) => {
|
|
298
|
+
console.log('Upload failed:', { error, errorCode })
|
|
299
|
+
})
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
<details>
|
|
304
|
+
<summary><strong>Uploading a file</strong></summary>
|
|
322
305
|
|
|
323
306
|
```javascript
|
|
324
307
|
import { Platform } from 'react-native'
|
|
@@ -368,27 +351,12 @@ await task.resume()
|
|
|
368
351
|
await task.stop()
|
|
369
352
|
```
|
|
370
353
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
Similar to downloads, you can re-attach to uploads that were running when your app was terminated:
|
|
354
|
+
</details>
|
|
374
355
|
|
|
375
|
-
|
|
376
|
-
import { getExistingUploadTasks } from '@kesha-antonov/react-native-background-downloader'
|
|
356
|
+
## Advanced Configuration
|
|
377
357
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
console.log(`Upload task ${task.id} was found!`)
|
|
381
|
-
task.progress(({ bytesUploaded, bytesTotal }) => {
|
|
382
|
-
console.log(`Uploaded: ${bytesUploaded / bytesTotal * 100}%`)
|
|
383
|
-
}).done(({ responseCode, responseBody }) => {
|
|
384
|
-
console.log('Upload is done!', { responseCode, responseBody })
|
|
385
|
-
}).error(({ error, errorCode }) => {
|
|
386
|
-
console.log('Upload canceled due to error: ', { error, errorCode })
|
|
387
|
-
})
|
|
388
|
-
}
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
### Using custom headers
|
|
358
|
+
<details>
|
|
359
|
+
<summary><strong>Using custom headers</strong></summary>
|
|
392
360
|
If you need to send custom headers with your download request, you can do in it 2 ways:
|
|
393
361
|
|
|
394
362
|
1) Globally using `setConfig()`:
|
|
@@ -428,7 +396,10 @@ task.start()
|
|
|
428
396
|
```
|
|
429
397
|
Headers given in `createDownloadTask()` are **merged** with the ones given in `setConfig({ headers: { ... } })`.
|
|
430
398
|
|
|
431
|
-
|
|
399
|
+
</details>
|
|
400
|
+
|
|
401
|
+
<details>
|
|
402
|
+
<summary><strong>Configuring parallel downloads and network types</strong></summary>
|
|
432
403
|
|
|
433
404
|
You can configure global settings for download behavior using `setConfig()`:
|
|
434
405
|
|
|
@@ -480,7 +451,10 @@ const task = createDownloadTask({
|
|
|
480
451
|
})
|
|
481
452
|
```
|
|
482
453
|
|
|
483
|
-
|
|
454
|
+
</details>
|
|
455
|
+
|
|
456
|
+
<details>
|
|
457
|
+
<summary><strong>Enabling debug logs</strong></summary>
|
|
484
458
|
|
|
485
459
|
The library includes verbose debug logging that can help diagnose download issues. Logging is disabled by default but can be enabled at runtime using `setConfig()`. **Logging works in both debug and production/release builds.**
|
|
486
460
|
|
|
@@ -517,7 +491,10 @@ setConfig({
|
|
|
517
491
|
- Logs include detailed information about download lifecycle, session management, and errors
|
|
518
492
|
- In production builds, logs are only printed when explicitly enabled via `isLogsEnabled`
|
|
519
493
|
|
|
520
|
-
|
|
494
|
+
</details>
|
|
495
|
+
|
|
496
|
+
<details>
|
|
497
|
+
<summary><strong>Handling slow-responding URLs</strong></summary>
|
|
521
498
|
|
|
522
499
|
This library automatically includes connection timeout improvements for slow-responding URLs. By default, the following headers are added to all download requests on Android:
|
|
523
500
|
|
|
@@ -527,7 +504,10 @@ This library automatically includes connection timeout improvements for slow-res
|
|
|
527
504
|
|
|
528
505
|
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.
|
|
529
506
|
|
|
530
|
-
|
|
507
|
+
</details>
|
|
508
|
+
|
|
509
|
+
<details>
|
|
510
|
+
<summary><strong>Handling URLs with many redirects (Android)</strong></summary>
|
|
531
511
|
|
|
532
512
|
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.).
|
|
533
513
|
|
|
@@ -567,11 +547,13 @@ task.start()
|
|
|
567
547
|
- Falls back to original URL if redirect resolution fails
|
|
568
548
|
- Respects the same headers and timeouts as the main download
|
|
569
549
|
|
|
550
|
+
</details>
|
|
551
|
+
|
|
570
552
|
## API
|
|
571
553
|
|
|
572
|
-
|
|
554
|
+
For complete API documentation, see the **[API Reference](./docs/API.md)**.
|
|
573
555
|
|
|
574
|
-
|
|
556
|
+
### Quick Reference
|
|
575
557
|
|
|
576
558
|
```typescript
|
|
577
559
|
import {
|
|
@@ -585,318 +567,151 @@ import {
|
|
|
585
567
|
} from '@kesha-antonov/react-native-background-downloader'
|
|
586
568
|
```
|
|
587
569
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
|
597
|
-
| ------------- | ------------------------------------------------ | :------: | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
598
|
-
| `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 |
|
|
599
|
-
| `url` | String | โ
| All | URL to file you want to download |
|
|
600
|
-
| `destination` | String | โ
| All | Where to copy the file to once the download is done. The 'file://' prefix will be automatically removed if present |
|
|
601
|
-
| `metadata` | Record<string, unknown> | | All | Custom data to be preserved across app restarts. Will be serialized to JSON |
|
|
602
|
-
| `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 |
|
|
603
|
-
| `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) |
|
|
604
|
-
| `isAllowedOverRoaming` | Boolean | | Android | whether this download may proceed over a roaming connection. By default, roaming is allowed |
|
|
605
|
-
| `isAllowedOverMetered` | Boolean | | Android | Whether this download may proceed over a metered network connection. By default, metered networks are allowed |
|
|
606
|
-
| `isNotificationVisible` | Boolean | | Android | Whether to show a download notification or not |
|
|
607
|
-
| `notificationTitle` | String | | Android | Title of the download notification |
|
|
608
|
-
|
|
609
|
-
**returns**
|
|
610
|
-
|
|
611
|
-
`DownloadTask` - The download task to control and monitor this download. Call `task.start()` to begin the download.
|
|
612
|
-
|
|
613
|
-
### `getExistingDownloadTasks()`
|
|
614
|
-
|
|
615
|
-
Checks for downloads that ran in background while your app was terminated.
|
|
616
|
-
|
|
617
|
-
Recommended to run at the init stage of the app.
|
|
618
|
-
|
|
619
|
-
**returns**
|
|
620
|
-
|
|
621
|
-
`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
|
|
622
|
-
|
|
623
|
-
### `createUploadTask(options)`
|
|
624
|
-
|
|
625
|
-
Upload a file to a server
|
|
626
|
-
|
|
627
|
-
**options**
|
|
628
|
-
|
|
629
|
-
An object containing options properties
|
|
630
|
-
|
|
631
|
-
| Property | Type | Required | Platforms | Info |
|
|
632
|
-
| ------------- | ------------------------------------------------ | :------: | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
633
|
-
| `id` | String | โ
| All | A unique ID to provide for this upload. This ID will help to identify the upload task when the app re-launches |
|
|
634
|
-
| `url` | String | โ
| All | URL to upload the file to |
|
|
635
|
-
| `source` | String | โ
| All | Path to the local file to upload. The 'file://' prefix will be automatically removed if present |
|
|
636
|
-
| `method` | 'POST' \| 'PUT' \| 'PATCH' | | All | HTTP method for upload. Default is 'POST' |
|
|
637
|
-
| `metadata` | Record<string, unknown> | | All | Custom data to be preserved across app restarts. Will be serialized to JSON |
|
|
638
|
-
| `headers` | Record<string, string \| null> | | All | Custom headers to add to the upload request. These are merged with the headers given in `setConfig({ headers: { ... } })`. Headers with null values will be removed |
|
|
639
|
-
| `fieldName` | String | | All | Name of the multipart form field for the file. Default is 'file' |
|
|
640
|
-
| `mimeType` | String | | All | MIME type of the file being uploaded. Default is inferred from file extension |
|
|
641
|
-
| `parameters` | Record<string, string> | | All | Additional form parameters to send with the upload |
|
|
642
|
-
| `isAllowedOverRoaming` | Boolean | | Android | Whether this upload may proceed over a roaming connection. By default, roaming is allowed |
|
|
643
|
-
| `isAllowedOverMetered` | Boolean | | Android | Whether this upload may proceed over a metered network connection. By default, metered networks are allowed |
|
|
644
|
-
| `isNotificationVisible` | Boolean | | Android | Whether to show an upload notification or not |
|
|
645
|
-
| `notificationTitle` | String | | Android | Title of the upload notification |
|
|
646
|
-
|
|
647
|
-
**returns**
|
|
648
|
-
|
|
649
|
-
`UploadTask` - The upload task to control and monitor this upload. Call `task.start()` to begin the upload.
|
|
650
|
-
|
|
651
|
-
### `getExistingUploadTasks()`
|
|
652
|
-
|
|
653
|
-
Checks for uploads that ran in background while your app was terminated.
|
|
654
|
-
|
|
655
|
-
Recommended to run at the init stage of the app.
|
|
656
|
-
|
|
657
|
-
**returns**
|
|
658
|
-
|
|
659
|
-
`Promise<UploadTask[]>` - A promise that resolves to an array of upload tasks that were running in the background so you can re-attach callbacks to them
|
|
660
|
-
|
|
661
|
-
### `setConfig(config)`
|
|
662
|
-
|
|
663
|
-
Sets global configuration for the downloader.
|
|
664
|
-
|
|
665
|
-
**config**
|
|
666
|
-
|
|
667
|
-
An object containing configuration properties
|
|
668
|
-
|
|
669
|
-
| Name | Type | Info |
|
|
670
|
-
| -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
|
671
|
-
| `headers` | Record<string, string \| null> | Optional headers to use in all future downloads. Headers with null values will be removed |
|
|
672
|
-
| `progressInterval` | Number | Interval in milliseconds for download progress updates. Must be >= 250. Default is 1000 |
|
|
673
|
-
| `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) |
|
|
674
|
-
| `isLogsEnabled` | Boolean | Enables/disables verbose debug logs in native code (iOS and Android). Works in both debug and release builds. Default is false |
|
|
675
|
-
| `logCallback` | (log: { message: string, taskId?: string }) => void | Optional callback function to receive native debug logs in JavaScript. Only called when `isLogsEnabled` is true |
|
|
676
|
-
| `maxParallelDownloads` | Number | **iOS only**. Sets the maximum number of simultaneous connections per host for the download session. Must be >= 1. Default is 4. Note: Android's DownloadManager does not support this configuration |
|
|
677
|
-
| `allowsCellularAccess` | Boolean | Controls whether downloads are allowed over cellular (metered) connections. When set to `false`, downloads will only occur over WiFi. Default is `true`. This is a cross-platform abstraction - on iOS it sets `allowsCellularAccess`, on Android it sets `isAllowedOverMetered` |
|
|
678
|
-
|
|
679
|
-
**Example:**
|
|
680
|
-
|
|
681
|
-
```javascript
|
|
682
|
-
import { setConfig } from '@kesha-antonov/react-native-background-downloader'
|
|
683
|
-
|
|
684
|
-
// Configure parallel downloads (iOS only) and cellular access
|
|
685
|
-
setConfig({
|
|
686
|
-
maxParallelDownloads: 8, // iOS only - max simultaneous connections per host
|
|
687
|
-
allowsCellularAccess: false, // Only download over WiFi
|
|
688
|
-
})
|
|
689
|
-
|
|
690
|
-
// Enable verbose logging with callback
|
|
691
|
-
setConfig({
|
|
692
|
-
isLogsEnabled: true,
|
|
693
|
-
logCallback: (log) => {
|
|
694
|
-
console.log('[BackgroundDownloader]', log.message, log.taskId ? `(${log.taskId})` : '')
|
|
695
|
-
}
|
|
696
|
-
})
|
|
697
|
-
|
|
698
|
-
// Or just enable native console logging without JS callback
|
|
699
|
-
setConfig({
|
|
700
|
-
isLogsEnabled: true
|
|
701
|
-
})
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
### DownloadTask
|
|
705
|
-
|
|
706
|
-
A class representing a download task created by `createDownloadTask()`. Note: You must call `task.start()` to begin the download after setting up event handlers.
|
|
707
|
-
|
|
708
|
-
### UploadTask
|
|
709
|
-
|
|
710
|
-
A class representing an upload task created by `createUploadTask()`. Note: You must call `task.start()` to begin the upload after setting up event handlers.
|
|
711
|
-
|
|
712
|
-
**Members** (same structure as DownloadTask with upload-specific properties)
|
|
713
|
-
|
|
714
|
-
| Name | Type | Info |
|
|
715
|
-
| -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
|
716
|
-
| `id` | String | The id you gave the task when calling `createUploadTask` |
|
|
717
|
-
| `metadata` | Record<string, unknown> | The metadata you gave the task when calling `createUploadTask` |
|
|
718
|
-
| `state` | 'PENDING' \| 'UPLOADING' \| 'PAUSED' \| 'DONE' \| 'FAILED' \| 'STOPPED' | Current state of the upload task |
|
|
719
|
-
| `bytesUploaded` | Number | The number of bytes currently uploaded by the task |
|
|
720
|
-
| `bytesTotal` | Number | The total number bytes to be uploaded by this task |
|
|
721
|
-
| `uploadParams` | UploadParams | The upload parameters set for this task |
|
|
722
|
-
|
|
723
|
-
**Callback Methods**
|
|
724
|
-
|
|
725
|
-
| Function | Callback Arguments | Info|
|
|
726
|
-
| ---------- | --------------------------------- | ---- |
|
|
727
|
-
| `begin` | `{ expectedBytes: number }` | Called when upload starts |
|
|
728
|
-
| `progress` | `{ bytesUploaded: number, bytesTotal: number }` | Called based on progressInterval (default: every 1000ms) so you can update your progress bar accordingly |
|
|
729
|
-
| `done` | `{ responseCode: number, responseBody: string, bytesUploaded: number, bytesTotal: number }` | Called when the upload is done. Includes server response code and body |
|
|
730
|
-
| `error` | `{ error: string, errorCode: number }` | Called when the upload stops due to an error |
|
|
731
|
-
|
|
732
|
-
**Methods**
|
|
733
|
-
|
|
734
|
-
- `pause(): Promise<void>` - Pauses the upload (platform support may vary)
|
|
735
|
-
- `resume(): Promise<void>` - Resumes a paused upload
|
|
736
|
-
- `stop(): Promise<void>` - Stops the upload and removes temporary data
|
|
737
|
-
- `start(): void` - Starts the upload
|
|
738
|
-
|
|
739
|
-
### `Members`
|
|
740
|
-
| Name | Type | Info |
|
|
741
|
-
| -------------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
|
742
|
-
| `id` | String | The id you gave the task when calling `createDownloadTask` |
|
|
743
|
-
| `metadata` | Record<string, unknown> | The metadata you gave the task when calling `createDownloadTask` |
|
|
744
|
-
| `state` | 'PENDING' \| 'DOWNLOADING' \| 'PAUSED' \| 'DONE' \| 'FAILED' \| 'STOPPED' | Current state of the download task |
|
|
745
|
-
| `bytesDownloaded` | Number | The number of bytes currently written by the task |
|
|
746
|
-
| `bytesTotal` | Number | The number bytes expected to be written by this task or more plainly, the file size being downloaded. **Note:** This value will be `-1` if the server does not provide a `Content-Length` header |
|
|
747
|
-
| `downloadParams` | DownloadParams | The download parameters set for this task |
|
|
748
|
-
|
|
749
|
-
### `completeHandler(jobId: string)`
|
|
750
|
-
|
|
751
|
-
Finishes download job and informs OS that app can be closed in background if needed.
|
|
752
|
-
After finishing download in background you have some time to process your JS logic and finish the job.
|
|
753
|
-
|
|
754
|
-
**Parameters:**
|
|
755
|
-
- `jobId` (String) - The ID of the download task to complete
|
|
756
|
-
|
|
757
|
-
**Note:** This should be called after processing your download in the `done` callback to properly signal completion to the OS.
|
|
758
|
-
|
|
759
|
-
### `Callback Methods`
|
|
760
|
-
Use these methods to stay updated on what's happening with the task.
|
|
761
|
-
|
|
762
|
-
All callback methods return the current instance of the `DownloadTask` for chaining.
|
|
763
|
-
|
|
764
|
-
| Function | Callback Arguments | Info|
|
|
765
|
-
| ---------- | --------------------------------- | ---- |
|
|
766
|
-
| `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 |
|
|
767
|
-
| `progress` | `{ bytesDownloaded: number, bytesTotal: number }` | Called based on progressInterval (default: every 1000ms) so you can update your progress bar accordingly. **Note:** `bytesTotal` will be `-1` if the server does not provide a `Content-Length` header |
|
|
768
|
-
| `done` | `{ location: string, bytesDownloaded: number, bytesTotal: number }` | Called when the download is done, the file is at the destination you've set. `location` is the final file path. **Note:** `bytesTotal` will be `-1` if the server did not provide a `Content-Length` header |
|
|
769
|
-
| `error` | `{ error: string, errorCode: number }` | Called when the download stops due to an error |
|
|
570
|
+
| Function | Description |
|
|
571
|
+
|----------|-------------|
|
|
572
|
+
| `createDownloadTask(options)` | Create a new download task |
|
|
573
|
+
| `createUploadTask(options)` | Create a new upload task |
|
|
574
|
+
| `getExistingDownloadTasks()` | Get downloads running in background |
|
|
575
|
+
| `getExistingUploadTasks()` | Get uploads running in background |
|
|
576
|
+
| `setConfig(config)` | Set global configuration |
|
|
577
|
+
| `completeHandler(jobId)` | Signal download completion to OS |
|
|
578
|
+
| `directories.documents` | Path to app's documents directory |
|
|
770
579
|
|
|
771
|
-
|
|
772
|
-
Pauses the download. Returns a promise that resolves when the pause operation is complete.
|
|
580
|
+
## Platform Notes
|
|
773
581
|
|
|
774
|
-
|
|
582
|
+
For detailed platform-specific information, see **[Platform Notes](./docs/PLATFORM_NOTES.md)**.
|
|
775
583
|
|
|
776
|
-
|
|
777
|
-
|
|
584
|
+
Key points:
|
|
585
|
+
- **iOS**: Uses `NSURLSession` for true background downloads
|
|
586
|
+
- **Android**: Uses `DownloadManager` + Foreground Services + MMKV
|
|
587
|
+
- **Pause/Resume**: Works on both platforms (Android requires server Range header support)
|
|
778
588
|
|
|
779
|
-
|
|
589
|
+
## Troubleshooting
|
|
780
590
|
|
|
781
|
-
|
|
782
|
-
|
|
591
|
+
<details>
|
|
592
|
+
<summary><strong>Download stuck in "pending" state (Android)</strong></summary>
|
|
783
593
|
|
|
784
|
-
|
|
594
|
+
This can happen with slow-responding servers. The library automatically adds keep-alive headers, but you can also try:
|
|
595
|
+
- Increase timeout by setting custom headers
|
|
596
|
+
- Check if the server supports the download URL
|
|
597
|
+
- Enable debug logs to see what's happening: `setConfig({ isLogsEnabled: true })`
|
|
598
|
+
</details>
|
|
785
599
|
|
|
786
|
-
|
|
600
|
+
<details>
|
|
601
|
+
<summary><strong>Duplicate class errors with react-native-mmkv (Android)</strong></summary>
|
|
787
602
|
|
|
788
|
-
|
|
603
|
+
If you're using `react-native-mmkv`, you don't need to add the MMKV dependency manually - it's already included. The library uses `compileOnly` to avoid conflicts.
|
|
604
|
+
</details>
|
|
789
605
|
|
|
790
|
-
|
|
606
|
+
<details>
|
|
607
|
+
<summary><strong>EXC_BAD_ACCESS crash on iOS with react-native-mmkv</strong></summary>
|
|
791
608
|
|
|
792
|
-
|
|
609
|
+
This was fixed in v4.4.0. Update to the latest version. If you're not using `react-native-mmkv`, add `pod 'MMKV', '>= 1.0.0'` to your Podfile.
|
|
610
|
+
</details>
|
|
793
611
|
|
|
794
|
-
|
|
612
|
+
<details>
|
|
613
|
+
<summary><strong>Downloads not resuming after app restart</strong></summary>
|
|
795
614
|
|
|
796
|
-
|
|
615
|
+
Make sure to call `getExistingDownloadTasks()` at app startup and re-attach your callbacks. The task IDs you provide are used to identify downloads across restarts.
|
|
616
|
+
</details>
|
|
797
617
|
|
|
798
|
-
|
|
618
|
+
<details>
|
|
619
|
+
<summary><strong>Google Play Console asking about Foreground Service</strong></summary>
|
|
799
620
|
|
|
800
|
-
|
|
621
|
+
See the [Google Play Console Declaration](#google-play-console-declaration) section for the required steps.
|
|
622
|
+
</details>
|
|
801
623
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
```
|
|
624
|
+
<details>
|
|
625
|
+
<summary><strong>TypeToken errors in release builds (Android)</strong></summary>
|
|
805
626
|
|
|
806
|
-
|
|
627
|
+
Add the Proguard rules mentioned in the [Proguard Rules](#proguard-rules) section.
|
|
628
|
+
</details>
|
|
807
629
|
|
|
808
|
-
|
|
630
|
+
## Example App
|
|
809
631
|
|
|
810
|
-
|
|
632
|
+
The repository includes a full example app demonstrating all features:
|
|
811
633
|
|
|
812
|
-
|
|
634
|
+
```bash
|
|
635
|
+
cd example
|
|
636
|
+
yarn install
|
|
813
637
|
|
|
814
|
-
|
|
638
|
+
# iOS
|
|
639
|
+
cd ios && pod install && cd ..
|
|
640
|
+
yarn ios
|
|
815
641
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
// ... other dependencies
|
|
819
|
-
implementation 'com.tencent:mmkv-shared:2.2.4' // or newer
|
|
820
|
-
}
|
|
642
|
+
# Android
|
|
643
|
+
yarn android
|
|
821
644
|
```
|
|
822
645
|
|
|
823
|
-
|
|
646
|
+
The example app shows:
|
|
647
|
+
- Starting multiple downloads
|
|
648
|
+
- Pause/resume functionality
|
|
649
|
+
- Progress tracking with animations
|
|
650
|
+
- Re-attaching to background tasks
|
|
651
|
+
- File management
|
|
824
652
|
|
|
825
|
-
|
|
653
|
+
## Use Cases
|
|
826
654
|
|
|
827
|
-
|
|
655
|
+
This library is perfect for apps that need reliable file transfers:
|
|
828
656
|
|
|
829
|
-
|
|
830
|
-
- **
|
|
831
|
-
- **
|
|
832
|
-
- **
|
|
833
|
-
- **
|
|
657
|
+
- ๐ต **Music/Podcast Apps** - Download episodes for offline listening
|
|
658
|
+
- ๐ **E-book Readers** - Download books in the background
|
|
659
|
+
- ๐ฌ **Video Streaming** - Offline video downloads
|
|
660
|
+
- ๐ **File Managers** - Large file transfers
|
|
661
|
+
- ๐ฎ **Games** - Download game assets and updates
|
|
662
|
+
- ๐ฑ **Enterprise Apps** - Sync large documents and media
|
|
834
663
|
|
|
835
|
-
|
|
836
|
-
- **Implementation**: Pause/resume on Android is implemented using HTTP Range headers
|
|
837
|
-
- **How it works**: When you pause a download, the current progress is saved. When resumed, a new download starts from where it left off using the `Range` header
|
|
838
|
-
- **Server requirement**: The server must support HTTP Range requests for resume to work correctly. If the server doesn't support range requests, the download will restart from the beginning
|
|
839
|
-
- **Temp files**: During pause/resume, progress is stored in a `.tmp` file which is renamed to the final destination upon completion
|
|
664
|
+
## Migration Guide
|
|
840
665
|
|
|
841
|
-
|
|
666
|
+
Upgrading from an older version? Check the [Migration Guide](./MIGRATION.md) for detailed instructions:
|
|
842
667
|
|
|
843
|
-
|
|
668
|
+
- [v4.3.x โ v4.4.0](./MIGRATION.md#migration-guide-v43x--v440) - iOS MMKV dependency change
|
|
669
|
+
- [v4.1.x โ v4.2.0](./MIGRATION.md#migration-guide-v41x--v420) - Android pause/resume support
|
|
670
|
+
- [v4.0.x โ v4.1.0](./MIGRATION.md#migration-guide-v40x--v410) - MMKV dependency change
|
|
671
|
+
- [v3.2.6 โ v4.0.0](./MIGRATION.md#migration-guide-v326--v400) - Major API changes
|
|
844
672
|
|
|
845
|
-
|
|
673
|
+
See the [Changelog](./CHANGELOG.md) for a complete list of changes in each version.
|
|
846
674
|
|
|
847
|
-
|
|
675
|
+
## Contributing
|
|
848
676
|
|
|
849
|
-
|
|
677
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
850
678
|
|
|
851
|
-
1.
|
|
852
|
-
2.
|
|
853
|
-
3.
|
|
854
|
-
4.
|
|
855
|
-
5.
|
|
856
|
-
6.
|
|
679
|
+
1. Fork the repository
|
|
680
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
681
|
+
3. Install dependencies (`yarn install`)
|
|
682
|
+
4. Make your changes
|
|
683
|
+
5. Run tests (`yarn test`)
|
|
684
|
+
6. Run linting (`yarn lint`)
|
|
685
|
+
7. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
686
|
+
8. Push to the branch (`git push origin feature/amazing-feature`)
|
|
687
|
+
9. Open a Pull Request
|
|
857
688
|
|
|
858
|
-
|
|
859
|
-
> "This app downloads files in the background using a foreground service with a user-visible notification. The foreground service ensures downloads continue reliably when the app is in the background or when the device is under memory pressure. Users initiate downloads and can see download progress via the notification."
|
|
689
|
+
### Development Setup
|
|
860
690
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
691
|
+
```bash
|
|
692
|
+
# Install dependencies
|
|
693
|
+
yarn install
|
|
864
694
|
|
|
865
|
-
|
|
695
|
+
# Run tests
|
|
696
|
+
yarn test
|
|
866
697
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
-keep class com.eko.RNBGDTaskConfig { *; }
|
|
698
|
+
# Run linting
|
|
699
|
+
yarn lint
|
|
870
700
|
|
|
871
|
-
#
|
|
872
|
-
-
|
|
873
|
-
-keep class com.google.gson.reflect.TypeToken { *; }
|
|
874
|
-
-keep class * extends com.google.gson.reflect.TypeToken
|
|
701
|
+
# Build the Expo plugin
|
|
702
|
+
yarn build-plugin
|
|
875
703
|
|
|
876
|
-
#
|
|
877
|
-
|
|
878
|
-
|
|
704
|
+
# Run the example app
|
|
705
|
+
cd example && yarn install
|
|
706
|
+
yarn ios # or yarn android
|
|
879
707
|
```
|
|
880
708
|
|
|
881
|
-
## Known Issues with New Architecture
|
|
882
|
-
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.
|
|
883
|
-
|
|
884
|
-
**Workaround:** If you encounter this error frequently with large files, consider:
|
|
885
|
-
1. Breaking large downloads into smaller chunks
|
|
886
|
-
2. Implementing retry logic in your app
|
|
887
|
-
3. Using alternative download strategies for very large files
|
|
888
|
-
|
|
889
|
-
The library now provides enhanced error handling for this specific case with detailed logging and cleanup.
|
|
890
|
-
|
|
891
|
-
## TODO
|
|
892
|
-
|
|
893
|
-
- [ ] Write better API for downloads - current kinda boilerplate
|
|
894
|
-
|
|
895
709
|
## Authors
|
|
896
710
|
|
|
897
|
-
|
|
711
|
+
Maintained by [Kesha Antonov](https://github.com/kesha-antonov)
|
|
898
712
|
|
|
899
|
-
|
|
713
|
+
Based on [react-native-background-downloader](https://github.com/ekolabs/react-native-background-downloader) by [Elad Gil](https://github.com/ptelad) (unmaintained since 2019)
|
|
900
714
|
|
|
901
715
|
## License
|
|
902
|
-
|
|
716
|
+
|
|
717
|
+
[Apache 2.0](./LICENSE)
|