@kesha-antonov/react-native-background-downloader 4.5.2 → 4.5.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/CHANGELOG.md +294 -0
- package/README.md +116 -10
- package/android/build.gradle +4 -2
- package/android/src/main/java/com/eko/Downloader.kt +10 -0
- package/android/src/main/java/com/eko/RNBackgroundDownloaderModuleImpl.kt +55 -9
- package/android/src/main/java/com/eko/UIDTDownloadJobService.kt +130 -30
- package/android/src/main/java/com/eko/uidt/UIDTJobManager.kt +17 -3
- package/android/src/main/java/com/eko/uidt/UIDTJobState.kt +164 -1
- package/android/src/main/java/com/eko/uidt/UIDTNotificationManager.kt +213 -4
- package/example/android/app/debug.keystore +0 -0
- package/ios/.DS_Store +0 -0
- package/ios/RNBackgroundDownloader.mm +53 -9
- package/package.json +1 -1
- package/plugin/build/index.d.ts +2 -2
- package/plugin/build/index.js +1 -1
- package/src/NativeRNBackgroundDownloader.ts +1 -1
- package/src/config.ts +3 -2
- package/src/index.ts +3 -1
- package/src/types.ts +9 -0
- package/plugin/package.json +0 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v4.5.3
|
|
4
|
+
|
|
5
|
+
### ✨ New Features
|
|
6
|
+
|
|
7
|
+
- **Android: Notification Grouping Mode (`summaryOnly`):** Added `mode` option to `NotificationsGroupingConfig`. Set `mode: 'summaryOnly'` to show only the summary notification for a group while individual download notifications are minimized (ultra-silent, no alert). Useful for keeping the notification shade clean during large batch downloads.
|
|
8
|
+
- `'individual'` (default) — all notifications shown, current behavior unchanged
|
|
9
|
+
- `'summaryOnly'` — only the group summary notification is shown with aggregate progress; individual notifications are invisible/silent
|
|
10
|
+
- **Android: Progress-Based Summary Notification:** In `summaryOnly` mode, the group summary notification now displays aggregate progress (total bytes downloaded / total bytes) across all downloads in the group.
|
|
11
|
+
- **Android: Auto-Remove Completed Downloads from Group:** Completed downloads are now automatically removed from notification groups, keeping the summary accurate.
|
|
12
|
+
|
|
13
|
+
### 🏗️ Architecture Changes
|
|
14
|
+
|
|
15
|
+
- **JS: `NotificationGroupingMode` Type:** New exported type `'individual' | 'summaryOnly'` for the `mode` field in `NotificationsGroupingConfig`.
|
|
16
|
+
- **Android: Ultra-Silent Notification Channel:** Added `NOTIFICATION_CHANNEL_ULTRA_SILENT_ID` (`IMPORTANCE_MIN`) channel used for individual notifications in `summaryOnly` mode.
|
|
17
|
+
- **Android: `updateSummaryNotificationForGroup()`:** New method that dispatches to the correct summary update strategy based on grouping mode.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## v4.5.2
|
|
22
|
+
|
|
23
|
+
### ✨ New Features
|
|
24
|
+
|
|
25
|
+
- **Update Headers on Paused Downloads:** Added ability to update headers (e.g., refresh auth tokens) on paused download tasks before resuming. Use `task.setDownloadParams()` to update headers while paused, then `task.resume()` to continue the download with new headers.
|
|
26
|
+
- **Use case:** User pauses a large download, returns hours/days later when auth token has expired. Now you can refresh the token and resume without restarting the download.
|
|
27
|
+
- **iOS:** Creates a fresh request with HTTP Range header and updated headers on resume
|
|
28
|
+
- **Android:** Updates both in-memory headers and persisted paused download state
|
|
29
|
+
|
|
30
|
+
### 🏗️ Architecture Changes
|
|
31
|
+
|
|
32
|
+
- **JS: `setDownloadParams()` Now Async:** The `DownloadTask.setDownloadParams()` method is now async and returns `Promise<boolean>` indicating whether native headers were updated (true when task is paused).
|
|
33
|
+
- **Native: Added `updateTaskHeaders` Method:** New native method on iOS and Android to update headers for paused tasks.
|
|
34
|
+
|
|
35
|
+
### 📚 Documentation
|
|
36
|
+
|
|
37
|
+
- Added "Updating headers on paused downloads" section to README
|
|
38
|
+
- Added `setDownloadParams()` method documentation to API.md
|
|
39
|
+
- Added iOS "Updating Headers on Paused Downloads" section to PLATFORM_NOTES.md
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## v4.5.1
|
|
44
|
+
|
|
45
|
+
### 🏗️ Architecture Changes
|
|
46
|
+
|
|
47
|
+
- **Android: UIDT Code Refactoring:** Extracted 980-line monolithic `UIDTDownloadJobService.kt` into modular components:
|
|
48
|
+
- `uidt/UIDTJobState.kt` - Data classes, constants, job registry
|
|
49
|
+
- `uidt/UIDTNotificationManager.kt` - All notification logic
|
|
50
|
+
- `uidt/UIDTJobManager.kt` - Job scheduling, cancel, pause, resume
|
|
51
|
+
- `utils/ProgressUtils.kt` - Progress calculation utilities
|
|
52
|
+
- Backward compatibility maintained via companion object delegates
|
|
53
|
+
- **Android: Removed Redundant jobScheduler.cancel():** In `pauseJob()`, removed unnecessary `jobScheduler.cancel()` after `jobFinished(params, false)` since `wantsReschedule=false` already tells the system the job is complete.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## v4.5.0
|
|
58
|
+
|
|
59
|
+
### ✨ New Features
|
|
60
|
+
|
|
61
|
+
- **Android: Global Notification Configuration:** Added `showNotificationsEnabled` and `notificationsGrouping` config options for controlling UIDT notifications globally via `setConfig()`.
|
|
62
|
+
- **Android: Customizable Paused Notification Text:** Added `downloadPaused` to `NotificationTexts` interface for customizing the "Paused" notification text.
|
|
63
|
+
- **Android: Notification Update Throttling:** Notification updates are now synced with `progressInterval` for consistent UI/notification progress display.
|
|
64
|
+
|
|
65
|
+
### 🐛 Bug Fixes
|
|
66
|
+
|
|
67
|
+
- **Android: Paused Downloads Continuing in Background:** Fixed paused UIDT downloads continuing to download in background after app restart. Now UIDT job is fully cancelled on pause, with a detached "Paused" notification shown.
|
|
68
|
+
- **Android: Duplicate Notifications After Resume:** Fixed duplicate notifications appearing when resuming downloads after app restart. Now uses stable notification IDs based on `configId.hashCode()`.
|
|
69
|
+
- **Android: Notification Not Updating After Resume:** Fixed notification stuck on old progress after resuming. Now resets notification timing on resume and shows correct progress immediately.
|
|
70
|
+
- **Android: Notification Updating While Paused:** Fixed notification progress updating even when download is paused.
|
|
71
|
+
- **Android: Stale Notifications on App Close:** All download notifications are now cancelled when the app closes via `invalidate()`.
|
|
72
|
+
|
|
73
|
+
### 💥 Breaking Changes
|
|
74
|
+
|
|
75
|
+
- **Removed Per-Task Notification Options:** `isNotificationVisible` and `notificationTitle` removed from `DownloadParams` and `UploadParams`. Use global `setConfig({ showNotificationsEnabled, notificationsGrouping })` instead.
|
|
76
|
+
|
|
77
|
+
### 🏗️ Architecture Changes
|
|
78
|
+
|
|
79
|
+
- **Android: Pause Behavior on Android 14+:** Complete redesign of pause/resume for User-Initiated Data Transfer (UIDT) jobs:
|
|
80
|
+
- **Problem:** UIDT jobs continue running in background even after app closes, causing "paused" downloads to secretly continue downloading.
|
|
81
|
+
- **Solution:** On pause, the UIDT job is properly terminated via `jobFinished(params, false)`. Download state is persisted to disk for resumption via HTTP Range headers.
|
|
82
|
+
- **UX:** A detached "Paused" notification (using `JOB_END_NOTIFICATION_POLICY_DETACH`) remains visible after job termination. On resume, a new UIDT job is created.
|
|
83
|
+
- **Follows Google's UIDT best practices:** State saved even without `onStopJob`, `jobFinished()` called on completion, notifications updated periodically with throttling.
|
|
84
|
+
- **Android: Separate Notification Channels:** Added separate channels for visible (`IMPORTANCE_LOW`) and silent (`IMPORTANCE_MIN`) notifications.
|
|
85
|
+
|
|
86
|
+
### ✨ Improvements
|
|
87
|
+
|
|
88
|
+
- **Android: Cleaner Notifications:** Added `setOnlyAlertOnce(true)` and `setShowWhen(false)` to all notifications for less intrusive updates.
|
|
89
|
+
- **Example App: Persistent Notification Settings:** Show Notifications and Notification Grouping toggles are now persisted with MMKV.
|
|
90
|
+
- **Example App: Android 13+ Permission Request:** Added POST_NOTIFICATIONS permission request when enabling notifications.
|
|
91
|
+
|
|
92
|
+
### 📚 Documentation
|
|
93
|
+
|
|
94
|
+
- Updated README with notification behavior during pause/resume
|
|
95
|
+
- Updated API.md with new notification configuration options
|
|
96
|
+
- Documented that notifications are removed when app closes
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## v4.4.5
|
|
101
|
+
|
|
102
|
+
### 🐛 Bug Fixes
|
|
103
|
+
|
|
104
|
+
- **Android: Stop Task Not Working on Android 14+:** Fixed `stopTask()` not actually stopping UIDT downloads on Android 14+. The JobScheduler job was cancelled but the underlying HTTP download continued. Now properly calls `resumableDownloader.cancel()` before removing from active jobs.
|
|
105
|
+
- **Android: Paused Tasks Not Persisting Across App Restarts:** Fixed paused UIDT downloads losing their state when the app was restarted. Added `getJobDownloadState()` to retrieve download state from active UIDT jobs and `savePausedDownloadState()` to properly persist pause state.
|
|
106
|
+
- **Android: ACCESS_NETWORK_STATE Permission:** Added missing permission required for JobScheduler network connectivity constraints on Android 14+.
|
|
107
|
+
- **Android: Downloaded Files List Showing Incomplete Files:** The "Downloaded Files" section in the example app now correctly filters out files that have active (non-DONE) download tasks, preventing incomplete files from appearing in the list.
|
|
108
|
+
|
|
109
|
+
### 🧹 Code Cleanup
|
|
110
|
+
|
|
111
|
+
- **Removed Verbose Debug Logs:** Cleaned up extensive debug logging in `StorageManager`, `Downloader`, and `RNBackgroundDownloaderModuleImpl` that was cluttering production logs. Removed serialization/deserialization logs, verification reads, and per-item iteration logs while keeping error logging.
|
|
112
|
+
- **Simplified Kotlin Code:** Removed unnecessary `else` blocks containing only debug/warning logs from `pauseTask()` and `resumeTask()` methods for cleaner code.
|
|
113
|
+
|
|
114
|
+
### ✨ Improvements
|
|
115
|
+
|
|
116
|
+
- **TypeScript: Added `destination` to Task Info:** The `destination` field is now returned from `getExistingDownloadTasks()` for paused downloads, allowing the app to know where the file will be saved.
|
|
117
|
+
|
|
118
|
+
### 📚 Documentation
|
|
119
|
+
|
|
120
|
+
- Added `skipMmkvDependency` option documentation to README for Expo plugin
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## v4.4.4
|
|
125
|
+
|
|
126
|
+
### 🐛 Bug Fixes
|
|
127
|
+
|
|
128
|
+
- **Expo Plugin: Fixed TypeScript Types:** Corrected TypeScript type definitions in the Expo config plugin.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## v4.4.3
|
|
133
|
+
|
|
134
|
+
### ✨ New Features
|
|
135
|
+
|
|
136
|
+
- **Expo Plugin: Auto-detect react-native-mmkv:** The Expo config plugin now automatically detects if `react-native-mmkv` is installed and skips adding the MMKV dependency to avoid duplicate class errors. Use `skipMmkvDependency: true` option to manually skip if needed.
|
|
137
|
+
- **Android: Version from package.json:** Android native code now reads the library version from `package.json` instead of hardcoding it.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## v4.4.2
|
|
142
|
+
|
|
143
|
+
### 🐛 Bug Fixes
|
|
144
|
+
|
|
145
|
+
- **Kotlin 2.0 Compatibility:** Fixed compilation error with Kotlin 2.0 (React Native 0.77+) by updating `progressReporter` to use named parameter syntax. This ensures compatibility with both Kotlin 1.9 (RN 0.76) and Kotlin 2.x (RN 0.77+).
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## v4.4.1
|
|
150
|
+
|
|
151
|
+
### 🐛 Bug Fixes
|
|
152
|
+
|
|
153
|
+
- **Android: Paused Tasks Persistence:** Fixed paused downloads not being restored after app restart on Android. Added persistent storage for paused download state using MMKV/SharedPreferences.
|
|
154
|
+
- **iOS: Improved Pause/Resume Handling:** Better handling of pause/resume operations on app restarts for iOS.
|
|
155
|
+
- **Upload Task App Restart Recovery:** Fixed upload tasks not being recoverable after app restart (#143). Added persistent storage for upload task configurations.
|
|
156
|
+
|
|
157
|
+
### ✨ Improvements
|
|
158
|
+
|
|
159
|
+
- **Example App:** Added task list display with animations and improved UI for managing downloads.
|
|
160
|
+
|
|
161
|
+
### 📚 Documentation
|
|
162
|
+
|
|
163
|
+
- Updated README with clearer MMKV dependency instructions
|
|
164
|
+
- Added information about resuming tasks after app restarts
|
|
165
|
+
- Updated authors section
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## v4.4.0
|
|
170
|
+
|
|
171
|
+
### ✨ New Features
|
|
172
|
+
|
|
173
|
+
- **Android 16 UIDT Support:** Downloads are now automatically marked as User-Initiated Data Transfers on Android 16+ (API 36) to prevent thermal throttling and job quota restrictions. Downloads will continue reliably even under moderate thermal conditions (~40°C).
|
|
174
|
+
|
|
175
|
+
### 🐛 Bug Fixes
|
|
176
|
+
|
|
177
|
+
- **iOS MMKV Conflict Fix:** Removed hard MMKV dependency from iOS podspec to prevent symbol conflicts with `react-native-mmkv`. Apps using `react-native-mmkv` no longer experience crashes (EXC_BAD_ACCESS) on iOS.
|
|
178
|
+
|
|
179
|
+
### 📦 Dependencies & Infrastructure
|
|
180
|
+
|
|
181
|
+
- **New Android Permission:** Added `RUN_USER_INITIATED_JOBS` permission for Android 16+ UIDT support
|
|
182
|
+
- **iOS MMKV Dependency:** MMKV is no longer a hard dependency in the podspec. Apps not using `react-native-mmkv` must add `pod 'MMKV', '>= 1.0.0'` to their Podfile.
|
|
183
|
+
|
|
184
|
+
### 📚 Documentation
|
|
185
|
+
|
|
186
|
+
- Added documentation about Android 16+ UIDT support in README
|
|
187
|
+
- Added iOS MMKV dependency section in README (similar to Android section)
|
|
188
|
+
- Added migration guide for iOS MMKV dependency change
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## v4.2.0
|
|
193
|
+
|
|
194
|
+
> 📖 **Upgrading from v4.1.x?** See the [Migration Guide](./MIGRATION.md) for details on the new Android pause/resume functionality.
|
|
195
|
+
|
|
196
|
+
### ✨ New Features
|
|
197
|
+
|
|
198
|
+
- **Android Pause/Resume Support:** Android now fully supports `task.pause()` and `task.resume()` methods using HTTP Range headers. Downloads can be paused and resumed just like on iOS.
|
|
199
|
+
- **Background Download Service:** Added `ResumableDownloadService` - a foreground service that ensures downloads continue even when the app is in the background or the screen is off.
|
|
200
|
+
- **WakeLock Support:** Downloads maintain a partial wake lock to prevent the device from sleeping during active downloads.
|
|
201
|
+
- **`bytesTotal` Unknown Size Handling:** When the server doesn't provide a `Content-Length` header, `bytesTotal` now returns `-1` instead of `0` to distinguish "unknown size" from "zero bytes".
|
|
202
|
+
|
|
203
|
+
### 🐛 Bug Fixes
|
|
204
|
+
|
|
205
|
+
- **Android Pause Error:** Fixed `COULD_NOT_FIND` error when pausing downloads on Android by properly tracking pausing state
|
|
206
|
+
- **Temp File Cleanup:** Fixed temp files (`.tmp`) not being deleted when stopping or deleting paused downloads
|
|
207
|
+
- **Content-Length Handling:** Fixed progress percentage calculation when server doesn't provide Content-Length header
|
|
208
|
+
|
|
209
|
+
### 📦 Dependencies & Infrastructure
|
|
210
|
+
|
|
211
|
+
- **New Android Permissions:** Added `FOREGROUND_SERVICE`, `FOREGROUND_SERVICE_DATA_SYNC`, and `WAKE_LOCK` permissions for background download support
|
|
212
|
+
- **New Service:** Added `ResumableDownloadService` with `dataSync` foreground service type
|
|
213
|
+
|
|
214
|
+
### 📚 Documentation
|
|
215
|
+
|
|
216
|
+
- Updated README to reflect Android pause/resume support
|
|
217
|
+
- Removed "iOS only" notes from pause/resume documentation
|
|
218
|
+
- Added documentation about `bytesTotal` returning `-1` for unknown sizes
|
|
219
|
+
- Updated Android DownloadManager Limitations section with new pause/resume implementation details
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## v4.1.0
|
|
224
|
+
|
|
225
|
+
> 📖 **Upgrading from v4.0.x?** See the [Migration Guide](./MIGRATION.md) for the MMKV dependency change.
|
|
226
|
+
|
|
227
|
+
### ⚠️ Breaking Changes
|
|
228
|
+
|
|
229
|
+
- **MMKV Dependency Changed to `compileOnly`:** The MMKV dependency is now `compileOnly` instead of `implementation` to avoid duplicate class errors when the app also uses `react-native-mmkv`. Apps not using `react-native-mmkv` must now explicitly add the MMKV dependency.
|
|
230
|
+
|
|
231
|
+
### ✨ New Features
|
|
232
|
+
|
|
233
|
+
- **Expo Plugin Android Support:** The Expo config plugin now automatically adds the MMKV dependency on Android. Use `addMmkvDependency: false` option if you're already using `react-native-mmkv`.
|
|
234
|
+
|
|
235
|
+
### 🐛 Bug Fixes
|
|
236
|
+
|
|
237
|
+
- **Duplicate Class Errors:** Fixed potential duplicate class errors when app uses both this library and `react-native-mmkv` by changing MMKV to `compileOnly` dependency
|
|
238
|
+
|
|
239
|
+
### 📚 Documentation
|
|
240
|
+
|
|
241
|
+
- Added documentation for MMKV dependency requirements in README
|
|
242
|
+
- Updated Platform-Specific Limitations section with MMKV setup instructions
|
|
243
|
+
- Added Expo plugin options documentation
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## v4.0.0
|
|
248
|
+
|
|
249
|
+
> 📖 **Upgrading from v3.x?** See the [Migration Guide](./MIGRATION.md) for detailed instructions.
|
|
250
|
+
|
|
251
|
+
### ⚠️ Breaking Changes
|
|
252
|
+
|
|
253
|
+
- **API Renamed:** `checkForExistingDownloads()` → `getExistingDownloadTasks()` - Now returns a Promise with better naming
|
|
254
|
+
- **API Renamed:** `download()` → `createDownloadTask()` - Downloads now require explicit `.start()` call
|
|
255
|
+
- **Download Tasks Start Explicitly:** Tasks created with `createDownloadTask()` are now in `PENDING` state and must call `.start()` to begin downloading
|
|
256
|
+
- **New Config Option:** Added `progressMinBytes` to `setConfig()` - controls minimum bytes change before progress callback fires (default: 1MB)
|
|
257
|
+
- **Source Structure Changed:** Code moved from `lib/` to `src/` directory with proper TypeScript types
|
|
258
|
+
|
|
259
|
+
### ✨ New Features
|
|
260
|
+
|
|
261
|
+
- **React Native New Architecture Support:** Full TurboModules support for both iOS and Android
|
|
262
|
+
- **Expo Config Plugin:** Added automatic iOS native code integration for Expo projects via `app.plugin.js`
|
|
263
|
+
- **Android Kotlin Migration:** All Java code converted to Kotlin
|
|
264
|
+
- **`maxRedirects` Option:** Configure maximum redirects for Android downloads (resolves #15)
|
|
265
|
+
- **`progressMinBytes` Option:** Hybrid progress reporting - callbacks fire based on time interval OR bytes downloaded
|
|
266
|
+
- **Android 15+ Support:** Added support for 16KB memory page sizes
|
|
267
|
+
- **Architecture Fallback:** Comprehensive x86/ARMv7 support with SharedPreferences fallback
|
|
268
|
+
|
|
269
|
+
### 🐛 Bug Fixes
|
|
270
|
+
|
|
271
|
+
- **iOS Pause/Resume:** Fixed pause and resume functionality on iOS
|
|
272
|
+
- **RN 0.78+ Compatibility:** Fixed bridge checks with safe emitter checks
|
|
273
|
+
- **New Architecture Events:** Fixed `downloadBegin` and `downloadProgress` events emission
|
|
274
|
+
- **Android Background Downloads:** Fixed completed files not moving to destination
|
|
275
|
+
- **Progress Callback Unknown Total:** Fixed progress callback not firing when total bytes unknown
|
|
276
|
+
- **Android 12 MMKV Crash:** Added robust error handling
|
|
277
|
+
- **`checkForExistingDownloads` TypeError:** Fixed TypeError on Android with architecture fallback
|
|
278
|
+
- **Firebase Performance Compatibility:** Fixed `completeHandler` method compatibility on Android
|
|
279
|
+
- **Slow Connection Handling:** Better handling of slow-responding URLs with timeouts
|
|
280
|
+
- **Android OldArch Export:** Fixed module method export issue (#79)
|
|
281
|
+
- **MMKV Compatibility:** Support for react-native-mmkv 4+ with mmkv-shared dependency
|
|
282
|
+
|
|
283
|
+
### 📦 Dependencies & Infrastructure
|
|
284
|
+
|
|
285
|
+
- **React Native:** Updated example app to RN 0.81.4
|
|
286
|
+
- **TypeScript:** Full TypeScript types in `src/types.ts`
|
|
287
|
+
- **iOS Native:** Converted from `.m` to `.mm` (Objective-C++)
|
|
288
|
+
- **Package Manager:** Switched to yarn as preferred package manager
|
|
289
|
+
|
|
290
|
+
### 📚 Documentation
|
|
291
|
+
|
|
292
|
+
- Added documentation for `progressMinBytes` option
|
|
293
|
+
- Updated README for React Native 0.77+ instructions
|
|
294
|
+
- Improved Expo config plugin examples
|
package/README.md
CHANGED
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
<h1 align="center">React Native Background Downloader</h1>
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
|
-
Download and upload large files on iOS & Android — even when your app is in the background or terminated.
|
|
18
|
+
Download and upload large files on iOS & Android — even when your app is in the background or terminated by the OS.
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
## ✨ Features
|
|
24
24
|
|
|
25
|
-
- 📥 **Background Downloads** - Downloads continue even when app is in background or terminated
|
|
25
|
+
- 📥 **Background Downloads** - Downloads continue even when app is in background or terminated by the OS
|
|
26
26
|
- 📤 **Background Uploads** - Upload files reliably in the background
|
|
27
27
|
- ⏸️ **Pause/Resume** - Full pause and resume support on both iOS and Android
|
|
28
28
|
- 🔄 **Re-attach to Downloads** - Reconnect to ongoing downloads after app restart
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
**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.
|
|
38
38
|
|
|
39
39
|
**The Solution:** Both iOS and Android provide system-level APIs for background file transfers:
|
|
40
|
-
- **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
|
|
40
|
+
- **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 by the OS. Note: if the user explicitly force-kills the app via the App Switcher, iOS cancels all background tasks — this is an iOS system limitation that cannot be overridden
|
|
41
41
|
- **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
|
|
42
42
|
|
|
43
43
|
**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.
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
- [📦 Installation](#-installation)
|
|
54
54
|
- [Expo Projects](#expo-projects)
|
|
55
55
|
- [Bare React Native Projects](#bare-react-native-projects)
|
|
56
|
+
- [MMKV version comparison](#mmkv-version-comparison)
|
|
56
57
|
- [🚀 Usage](#-usage)
|
|
57
58
|
- [Downloading a file](#downloading-a-file)
|
|
58
59
|
- [Re-Attaching to background tasks](#re-attaching-to-background-tasks)
|
|
@@ -113,7 +114,7 @@ export default {
|
|
|
113
114
|
expo: {
|
|
114
115
|
plugins: [
|
|
115
116
|
["@kesha-antonov/react-native-background-downloader", {
|
|
116
|
-
mmkvVersion: "
|
|
117
|
+
mmkvVersion: "1.3.16", // Customize MMKV version on Android
|
|
117
118
|
skipMmkvDependency: true // Skip if you want to add MMKV manually
|
|
118
119
|
}]
|
|
119
120
|
]
|
|
@@ -123,8 +124,8 @@ export default {
|
|
|
123
124
|
|
|
124
125
|
| Option | Type | Default | Description |
|
|
125
126
|
|--------|------|---------|-------------|
|
|
126
|
-
| `mmkvVersion` | string | `'
|
|
127
|
-
| `skipMmkvDependency` | boolean | `false` | Skip adding MMKV dependency. Set to `true` if you're using [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv) to avoid duplicate class errors. The plugin auto-detects `react-native-mmkv` but you can use this option to explicitly skip. |
|
|
127
|
+
| `mmkvVersion` | string | `'1.3.16'` | The version of [MMKV](https://github.com/Tencent/MMKV/releases) to use on Android. See [MMKV version comparison](#mmkv-version-comparison) for details. |
|
|
128
|
+
| `skipMmkvDependency` | boolean | `false` | Skip adding MMKV dependency. Set to `true` if you're using [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv) to avoid duplicate class errors. The plugin auto-detects `react-native-mmkv` but you can use this option to explicitly skip. See [MMKV version comparison](#mmkv-version-comparison). |
|
|
128
129
|
|
|
129
130
|
</details>
|
|
130
131
|
|
|
@@ -214,11 +215,29 @@ Add MMKV to your `android/app/build.gradle`:
|
|
|
214
215
|
|
|
215
216
|
```gradle
|
|
216
217
|
dependencies {
|
|
217
|
-
implementation 'com.tencent:mmkv-shared:
|
|
218
|
+
implementation 'com.tencent:mmkv-shared:1.3.16'
|
|
218
219
|
}
|
|
219
220
|
```
|
|
220
221
|
|
|
221
|
-
> **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.
|
|
222
|
+
> **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. Note that `react-native-mmkv` v4.x uses [Margelo's fork of MMKV](https://github.com/margelo/MMKV) (`io.github.zhongwuzw:mmkv`) which re-adds armeabi-v7a (32-bit ARM) support that was dropped in the official MMKV 2.x release.
|
|
223
|
+
|
|
224
|
+
> **⚠️ armeabi-v7a (32-bit ARM) users:** MMKV 2.x dropped 32-bit ABI support (since v2.0.0). If you need armeabi-v7a support and get a CMake error like `No compatible library found for //mmkv/mmkv`, use the MMKV 1.3.x LTS series instead — it supports both armeabi-v7a **and** 16KB page sizes (since v1.3.14):
|
|
225
|
+
> ```gradle
|
|
226
|
+
> dependencies {
|
|
227
|
+
> implementation 'com.tencent:mmkv-shared:1.3.16'
|
|
228
|
+
> }
|
|
229
|
+
> ```
|
|
230
|
+
|
|
231
|
+
#### MMKV version comparison
|
|
232
|
+
|
|
233
|
+
| Dependency | armeabi-v7a (32-bit) | arm64-v8a | 16KB page size | Recommended for |
|
|
234
|
+
|---|:---:|:---:|:---:|---|
|
|
235
|
+
| `com.tencent:mmkv-shared:1.3.16` (**default**) | ✅ | ✅ | ✅ (since 1.3.14) | Most apps — broadest device coverage |
|
|
236
|
+
| `com.tencent:mmkv-shared:2.x` | ❌ | ✅ | ✅ | 64-bit only apps (no legacy devices) |
|
|
237
|
+
| `io.github.zhongwuzw:mmkv:2.3.0` ([Margelo fork](https://github.com/margelo/MMKV)) | ✅ | ✅ | ✅ | Used automatically by `react-native-mmkv` v4.x — skip manual dependency |
|
|
238
|
+
| `react-native-mmkv` (already in project) | ✅ | ✅ | ✅ | If you already use `react-native-mmkv` — skip Step 4 entirely |
|
|
239
|
+
|
|
240
|
+
**TL;DR:** Use the default `1.3.16`. If you already have `react-native-mmkv` in your project, skip Step 4.
|
|
222
241
|
|
|
223
242
|
## 🚀 Usage
|
|
224
243
|
|
|
@@ -636,9 +655,92 @@ setConfig({
|
|
|
636
655
|
})
|
|
637
656
|
```
|
|
638
657
|
|
|
658
|
+
**Notification grouping modes:**
|
|
659
|
+
|
|
660
|
+
When downloading many files (e.g., thousands of photos), you can use the `mode` option to control how notifications are displayed:
|
|
661
|
+
|
|
662
|
+
| Mode | Description |
|
|
663
|
+
|------|-------------|
|
|
664
|
+
| `'individual'` | Default. Shows all individual notifications grouped together with a summary |
|
|
665
|
+
| `'summaryOnly'` | Shows only ONE notification with real-time aggregate progress (e.g., "45% - 5 files"). Individual UIDT notifications are collapsed into an invisible group. Ideal for bulk downloads |
|
|
666
|
+
|
|
667
|
+
```javascript
|
|
668
|
+
import { setConfig } from '@kesha-antonov/react-native-background-downloader'
|
|
669
|
+
|
|
670
|
+
// For bulk downloads (e.g., syncing thousands of photos)
|
|
671
|
+
// Use 'summaryOnly' mode to show only ONE notification with aggregate progress
|
|
672
|
+
setConfig({
|
|
673
|
+
showNotificationsEnabled: true,
|
|
674
|
+
notificationsGrouping: {
|
|
675
|
+
enabled: true,
|
|
676
|
+
mode: 'summaryOnly', // Only show summary notification with progress bar
|
|
677
|
+
texts: {
|
|
678
|
+
groupTitle: 'Syncing Photos',
|
|
679
|
+
groupText: '{count} files downloading',
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
})
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**Example: Batch downloading multiple files with grouped notifications:**
|
|
686
|
+
|
|
687
|
+
```javascript
|
|
688
|
+
import { setConfig, createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
|
|
689
|
+
|
|
690
|
+
// Configure for bulk downloads with single summary notification
|
|
691
|
+
setConfig({
|
|
692
|
+
showNotificationsEnabled: true,
|
|
693
|
+
notificationsGrouping: {
|
|
694
|
+
enabled: true,
|
|
695
|
+
mode: 'summaryOnly',
|
|
696
|
+
texts: {
|
|
697
|
+
groupTitle: 'Photo Sync',
|
|
698
|
+
groupText: '{count} photos downloading',
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
// Download multiple files - they all share ONE notification with aggregate progress
|
|
704
|
+
const photos = [
|
|
705
|
+
{ id: 'photo-1', url: 'https://example.com/photo1.jpg' },
|
|
706
|
+
{ id: 'photo-2', url: 'https://example.com/photo2.jpg' },
|
|
707
|
+
{ id: 'photo-3', url: 'https://example.com/photo3.jpg' },
|
|
708
|
+
// ... potentially thousands of files
|
|
709
|
+
]
|
|
710
|
+
|
|
711
|
+
const GROUP_ID = 'photo-sync-batch'
|
|
712
|
+
|
|
713
|
+
for (const photo of photos) {
|
|
714
|
+
const task = createDownloadTask({
|
|
715
|
+
id: photo.id,
|
|
716
|
+
url: photo.url,
|
|
717
|
+
destination: `${directories.documents}/${photo.id}.jpg`,
|
|
718
|
+
metadata: {
|
|
719
|
+
groupId: GROUP_ID, // Required for grouping
|
|
720
|
+
groupName: 'Photo Sync', // Displayed in notification title
|
|
721
|
+
},
|
|
722
|
+
})
|
|
723
|
+
.progress(({ bytesDownloaded, bytesTotal }) => {
|
|
724
|
+
// Progress tracked per file, notification shows aggregate progress
|
|
725
|
+
})
|
|
726
|
+
.done(() => {
|
|
727
|
+
console.log(`Downloaded ${photo.id}`)
|
|
728
|
+
})
|
|
729
|
+
.error(({ error }) => {
|
|
730
|
+
console.error(`Failed ${photo.id}:`, error)
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
task.start()
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// User sees: ONE notification showing "45% - 3 files" with progress bar
|
|
737
|
+
// instead of 3 separate notifications cluttering the notification shade
|
|
738
|
+
// Notification automatically disappears when all downloads complete
|
|
739
|
+
```
|
|
740
|
+
|
|
639
741
|
**Grouping downloads by category:**
|
|
640
742
|
|
|
641
|
-
When notification grouping is enabled, you can group related downloads (e.g., by album, playlist, podcast) by passing `groupId` and `groupName` in the task metadata
|
|
743
|
+
When notification grouping is enabled, you can group related downloads (e.g., by album, playlist, podcast) by passing `groupId` and `groupName` in the task `metadata`:
|
|
642
744
|
|
|
643
745
|
```javascript
|
|
644
746
|
import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'
|
|
@@ -650,7 +752,7 @@ const task = createDownloadTask({
|
|
|
650
752
|
destination: `${directories.documents}/track01.mp3`,
|
|
651
753
|
metadata: {
|
|
652
754
|
groupId: 'album-summer-hits', // Unique identifier for the group
|
|
653
|
-
groupName: 'Summer Hits 2024', // Display name in notification
|
|
755
|
+
groupName: 'Summer Hits 2024', // Display name in notification title
|
|
654
756
|
},
|
|
655
757
|
})
|
|
656
758
|
|
|
@@ -674,6 +776,7 @@ This ensures users always see the download status without unexpected background
|
|
|
674
776
|
|--------|------|---------|-------------|
|
|
675
777
|
| `showNotificationsEnabled` | boolean | `false` | Show full download notifications. When `false`, creates minimal silent notifications (UIDT jobs require a notification, but it will be barely visible). This is a top-level config option. |
|
|
676
778
|
| `notificationsGrouping.enabled` | boolean | `false` | Enable notification grouping |
|
|
779
|
+
| `notificationsGrouping.mode` | `'individual'` \| `'summaryOnly'` | `'individual'` | Notification display mode. Use `'summaryOnly'` for bulk downloads to show only ONE notification with real-time aggregate progress |
|
|
677
780
|
| `notificationsGrouping.texts` | object | See below | Customizable notification texts |
|
|
678
781
|
|
|
679
782
|
**Notification texts (`notificationsGrouping.texts`):**
|
|
@@ -696,6 +799,7 @@ This ensures users always see the download status without unexpected background
|
|
|
696
799
|
- This feature only affects Android 14+ (API 34) where UIDT jobs are used
|
|
697
800
|
- On older Android versions, the standard DownloadManager notifications are shown
|
|
698
801
|
- iOS uses system download notifications and doesn't support custom grouping
|
|
802
|
+
- **Use `mode: 'summaryOnly'` when downloading many files** to prevent notification spam - shows ONE notification with aggregate progress bar that updates in real-time
|
|
699
803
|
|
|
700
804
|
</details>
|
|
701
805
|
|
|
@@ -751,6 +855,8 @@ This can happen with slow-responding servers. The library automatically adds kee
|
|
|
751
855
|
<summary><strong>Duplicate class errors with react-native-mmkv (Android)</strong></summary>
|
|
752
856
|
|
|
753
857
|
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.
|
|
858
|
+
|
|
859
|
+
`react-native-mmkv` v4.x uses [Margelo's fork of MMKV](https://github.com/margelo/MMKV) (`io.github.zhongwuzw:mmkv`) which re-adds armeabi-v7a (32-bit ARM) support, so you have full ABI coverage including 32-bit devices when using `react-native-mmkv`.
|
|
754
860
|
</details>
|
|
755
861
|
|
|
756
862
|
<details>
|
package/android/build.gradle
CHANGED
|
@@ -36,6 +36,8 @@ android {
|
|
|
36
36
|
versionName '1.0'
|
|
37
37
|
|
|
38
38
|
// Support for 16KB memory page sizes (Android 15+)
|
|
39
|
+
// Note: MMKV 2.x dropped armeabi-v7a support. If your app targets armeabi-v7a,
|
|
40
|
+
// downgrade to MMKV 1.x in your app's build.gradle (see README for details).
|
|
39
41
|
ndk {
|
|
40
42
|
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
|
41
43
|
}
|
|
@@ -78,8 +80,8 @@ dependencies {
|
|
|
78
80
|
// MMKV dependency for persistent download state storage
|
|
79
81
|
// Uses compileOnly to avoid duplicate class errors when app also uses react-native-mmkv
|
|
80
82
|
// The app must provide MMKV dependency (either directly or via react-native-mmkv)
|
|
81
|
-
// MMKV
|
|
82
|
-
compileOnly 'com.tencent:mmkv-shared:
|
|
83
|
+
// MMKV 1.3.14+ supports both armeabi-v7a (32-bit) and 16KB page sizes (Android 15+)
|
|
84
|
+
compileOnly 'com.tencent:mmkv-shared:1.3.16'
|
|
83
85
|
|
|
84
86
|
implementation 'com.google.code.gson:gson:2.12.1'
|
|
85
87
|
}
|
|
@@ -111,8 +111,18 @@ class Downloader(private val context: Context, private val storageManager: com.e
|
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* Set the download listener for resumable downloads.
|
|
114
|
+
* Also sets the UIDT listener on Android 14+ so that ongoing UIDT jobs
|
|
115
|
+
* (e.g. ones that survived app termination) can forward events to JS after
|
|
116
|
+
* the app is reopened.
|
|
114
117
|
*/
|
|
115
118
|
fun setResumableDownloadListener(listener: ResumableDownloader.DownloadListener) {
|
|
119
|
+
// For UIDT jobs (Android 14+), set the listener directly on the registry.
|
|
120
|
+
// This reconnects event forwarding after app restart when UIDT jobs are
|
|
121
|
+
// already running but UIDTJobRegistry.downloadListener was null in the
|
|
122
|
+
// fresh process.
|
|
123
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
124
|
+
UIDTDownloadJobService.downloadListener = listener
|
|
125
|
+
}
|
|
116
126
|
executeWhenServiceReady {
|
|
117
127
|
downloadService?.setDownloadListener(listener)
|
|
118
128
|
}
|