State-driven, resumable download API for Tauri 2.x apps.
This plugin provides a cross-platform download interface with resumable downloads, progress tracking, and proper resource management.
- Parallel, resumable download support
- Persistable, thread-safe store
- State and progress notifications
- Cross-platform support (Linux, Windows, macOS, Android, iOS)
| Platform | Supported |
|---|---|
| Linux | âś“ |
| Windows | âś“ |
| macOS | âś“ |
| Android | âś“ |
| iOSÂą | âś“ |
Âą Supports fully interruptible and resumable background downloads, even when the app
is suspended or terminated using
URLSession with a
background configuration. See iOS Background Downloads
for details.
-
Install NPM dependencies:
npm install
-
Build the TypeScript bindings:
npm run build
-
Build the Rust plugin:
cargo build
Run Rust tests:
cargo testThis plugin requires a Rust version of at least 1.77.2
Add the plugin to your Cargo.toml:
src-tauri/Cargo.toml
[dependencies]
tauri-plugin-download = { git = "https://github.com/silvermine/tauri-plugin-download" }Install the JavaScript bindings:
npm install @silvermine/tauri-plugin-downloadInitialize the plugin in your tauri::Builder:
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_download::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}import { list } from 'tauri-plugin-download';
async function listDownloads() {
const downloads = await list();
for (const download of downloads) {
console.debug(`Found '${download.path}': [${download.status}, ${download.progress}%]`);
}
}import { get, DownloadStatus } from 'tauri-plugin-download';
async function getDownload() {
const download = await get('/path/to/file.zip');
if (download.status === DownloadStatus.Pending) {
console.debug(`Download '${download.path}' not found in store`);
} else {
console.debug(`Found '${download.path}': [${download.status}, ${download.progress}%]`);
}
}The API uses discriminated unions with type guards for compile-time safety. Only valid methods are available based on the download's status.
import { get, DownloadStatus, hasAction, DownloadAction } from 'tauri-plugin-download';
async function createAndStartDownload() {
const download = await get('/path/to/file.zip');
if (download.status === DownloadStatus.Pending) {
// Download not in store - create it first
const { download: created } = await download.create('https://example.com/file.zip');
await created.start();
}
}
async function manageDownload() {
const download = await get('/path/to/file.zip');
if (hasAction(download, DownloadAction.Start)) {
await download.start(); // TypeScript knows start() is available
} else if (hasAction(download, DownloadAction.Pause)) {
await download.pause(); // TypeScript knows pause() is available
} else if (hasAction(download, DownloadAction.Resume)) {
await download.resume(); // TypeScript knows resume() is available
}
}Listeners can be attached to downloads in any status, including Pending.
This allows you to set up listeners before creating the download.
import { get, DownloadStatus } from 'tauri-plugin-download';
async function setupAndStartDownload() {
const download = await get('/path/to/file.zip');
// Attach listener (works for Pending downloads too)
const unlisten = await download.listen((updated) => {
console.debug(`'${updated.path}': ${updated.progress}%`);
});
// Create and start if pending
if (download.status === DownloadStatus.Pending) {
const { download: created } = await download.create('https://example.com/file.zip');
await created.start();
}
// To stop listening
unlisten();
}Check out the examples/tauri-app directory for a working example of how to use this plugin.
This project follows the Silvermine standardization guidelines. Key standards include:
- EditorConfig: Consistent editor settings across the team
- Markdownlint: Markdown linting for documentation
- Commitlint: Conventional commit message format
- Code Style: 3-space indentation, LF line endings
npm run standardsOn iOS, this plugin uses URLSession with a background configuration, which allows
downloads
to continue even when the app is suspended or terminated by the system.
- App Running: Downloads proceed normally with real-time progress updates
- App Suspended: iOS continues downloads in the background
- App Terminated: iOS completes downloads and relaunches the app in the background to deliver results
- App Resumed: The plugin reconciles state and emits completion events
Background downloads work automatically in Tauri apps. When the app resumes, all delegate callbacks are delivered and state is properly reconciled.
Note: Tauri's iOS architecture doesn't currently expose the AppDelegate hook for
handleEventsForBackgroundURLSession. Without calling this completion handler, iOS cannot
determine when background event processing is complete. This may cause iOS to:
- Keep the app running longer than necessary (wasting battery)
- Skip taking a UI snapshot for the app switcher
- Deprioritize future background execution for this app
In practice, this has minimal impact for typical download scenarios since iOS delivers all pending delegate callbacks when the app resumes regardless of whether the completion handler is called.
If Tauri exposes AppDelegate hooks in the future, add this for optimal background
handling:
import DownloadManagerKit
func application(_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void) {
DownloadManager.shared.setBackgroundCompletionHandler(completionHandler)
}MIT
Contributions are welcome! Please follow the established coding standards and commit message conventions.