Files
2025-11-11 08:53:23 +01:00

259 lines
6.3 KiB
Markdown

# Background Downloader Module
A native iOS and Android module for downloading large files in the background using `NSURLSession` (iOS) and `DownloadManager` (Android).
## Features
- **Background Downloads**: Downloads continue even when the app is backgrounded or suspended
- **Progress Tracking**: Real-time progress updates via events
- **Multiple Downloads**: Support for concurrent downloads
- **Cancellation**: Cancel individual or all downloads
- **Custom Destination**: Optionally specify custom file paths
- **Error Handling**: Comprehensive error reporting
- **Cross-Platform**: Works on both iOS and Android
## Usage
### Basic Example
```typescript
import { BackgroundDownloader } from '@/modules';
// Start a download
const taskId = await BackgroundDownloader.startDownload(
'https://example.com/largefile.mp4'
);
// Listen for progress updates
const progressSub = BackgroundDownloader.addProgressListener((event) => {
console.log(`Progress: ${Math.floor(event.progress * 100)}%`);
console.log(`Downloaded: ${event.bytesWritten} / ${event.totalBytes}`);
});
// Listen for completion
const completeSub = BackgroundDownloader.addCompleteListener((event) => {
console.log('Download complete!');
console.log('File saved to:', event.filePath);
console.log('Task ID:', event.taskId);
});
// Listen for errors
const errorSub = BackgroundDownloader.addErrorListener((event) => {
console.error('Download failed:', event.error);
});
// Cancel a download
BackgroundDownloader.cancelDownload(taskId);
// Get all active downloads
const activeDownloads = await BackgroundDownloader.getActiveDownloads();
// Cleanup listeners when done
progressSub.remove();
completeSub.remove();
errorSub.remove();
```
### Custom Destination Path
```typescript
import { BackgroundDownloader } from '@/modules';
import * as FileSystem from 'expo-file-system';
const destinationPath = `${FileSystem.documentDirectory}myfile.mp4`;
const taskId = await BackgroundDownloader.startDownload(
'https://example.com/video.mp4',
destinationPath
);
```
### Managing Multiple Downloads
```typescript
import { BackgroundDownloader } from '@/modules';
const downloads = new Map();
async function startMultipleDownloads(urls: string[]) {
for (const url of urls) {
const taskId = await BackgroundDownloader.startDownload(url);
downloads.set(taskId, { url, progress: 0 });
}
}
// Track progress for each download
const progressSub = BackgroundDownloader.addProgressListener((event) => {
const download = downloads.get(event.taskId);
if (download) {
download.progress = event.progress;
}
});
// Cancel all downloads
BackgroundDownloader.cancelAllDownloads();
```
## API Reference
### Methods
#### `startDownload(url: string, destinationPath?: string): Promise<number>`
Starts a new background download.
- **Parameters:**
- `url`: The URL of the file to download
- `destinationPath`: (Optional) Custom file path for the downloaded file
- **Returns:** Promise that resolves to the task ID (number)
#### `cancelDownload(taskId: number): void`
Cancels a specific download by task ID.
- **Parameters:**
- `taskId`: The task ID returned by `startDownload`
#### `cancelAllDownloads(): void`
Cancels all active downloads.
#### `getActiveDownloads(): Promise<ActiveDownload[]>`
Gets information about all active downloads.
- **Returns:** Promise that resolves to an array of active downloads
### Event Listeners
#### `addProgressListener(listener: (event: DownloadProgressEvent) => void): Subscription`
Listens for download progress updates.
- **Event payload:**
- `taskId`: number
- `bytesWritten`: number
- `totalBytes`: number
- `progress`: number (0.0 to 1.0)
#### `addCompleteListener(listener: (event: DownloadCompleteEvent) => void): Subscription`
Listens for download completion.
- **Event payload:**
- `taskId`: number
- `filePath`: string
- `url`: string
#### `addErrorListener(listener: (event: DownloadErrorEvent) => void): Subscription`
Listens for download errors.
- **Event payload:**
- `taskId`: number
- `error`: string
#### `addStartedListener(listener: (event: DownloadStartedEvent) => void): Subscription`
Listens for download start confirmation.
- **Event payload:**
- `taskId`: number
- `url`: string
## Types
```typescript
interface DownloadProgressEvent {
taskId: number;
bytesWritten: number;
totalBytes: number;
progress: number;
}
interface DownloadCompleteEvent {
taskId: number;
filePath: string;
url: string;
}
interface DownloadErrorEvent {
taskId: number;
error: string;
}
interface DownloadStartedEvent {
taskId: number;
url: string;
}
interface ActiveDownload {
taskId: number;
url: string;
state: 'running' | 'suspended' | 'canceling' | 'completed' | 'unknown';
}
```
## Implementation Details
### iOS Background Downloads
- Uses `NSURLSession` with background configuration
- Session identifier: `com.fredrikburmester.streamyfin.backgrounddownloader`
- Downloads continue when app is backgrounded or suspended
- System may terminate downloads if app is force-quit
### Android Background Downloads
- Uses Android's `DownloadManager` API
- Downloads are managed by the system and continue in the background
- Shows download notification in the notification tray
- Downloads continue even if the app is closed
- Requires `INTERNET` permission (automatically added by Expo)
### Background Modes
The app's `Info.plist` already includes the required background mode for iOS:
- `UIBackgroundModes`: `["audio", "fetch"]`
### File Storage
**iOS:** By default, downloaded files are saved to the app's Documents directory.
**Android:** By default, files are saved to the app's external files directory (accessible via `FileSystem.documentDirectory` in Expo).
You can specify a custom path using the `destinationPath` parameter on both platforms.
## Building
After adding this module, rebuild the app:
```bash
# iOS
npx expo prebuild -p ios
npx expo run:ios
# Android
npx expo prebuild -p android
npx expo run:android
```
Or install manually:
```bash
# iOS
cd ios
pod install
cd ..
# Android - prebuild handles everything
npx expo prebuild -p android
```
## Notes
- Background downloads may be cancelled if the user force-quits the app (iOS)
- The OS manages download priority and may pause downloads to save battery
- Android shows a system notification for ongoing downloads
- Downloads over cellular are allowed by default on both platforms