
Enables seamless file downloading, local saving, and opening through the system's default application, while abstracting platform-specific details for a streamlined experience.
KDownloadFile is a simple yet powerful Kotlin Multiplatform library that enables downloading files over HTTP with support for custom headers, saving them locally, and opening them using the system’s default app — all while abstracting away platform-specific details.
✅ Supports background downloads on Android
🍏 Supports Live Activities on iOS 16.1+ to reflect download progress
🌍 Built for KMP, with clean API and platform-safe design
implementation("io.github.the-best-is-best:kdownload-file:2.5.0")To ensure proper file access and sharing in Android, follow these steps:
<provider android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider" android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider><?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>To use KDownloadFile, you must add the Swift interop library to your iOS targets.
Use Swift Package Manager and add the following:
https://github.com/the-best-is-best/KDownloadFileInterop
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import io.github.kdownloadfile.downloadFile
import io.github.kdownloadfile.openFile
import kotlinx.coroutines.launch
@Composable
fun DownloadButton() {
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
val pathRes = downloadFile(
url = "https://your-server.com/file.jpg",
fileName = "file.jpg",
folderName = "downloads"
)
pathRes?.fold(
onSuccess = { path ->
println("File saved at: $path")
openFile(path)
},
onFailure = { error ->
println("Download failed: ${error.message}")
}
)
}
}) {
Text("Download")
}
}expect suspend fun downloadFile(
url: String,
fileName: String,
folderName: String? = null,
configuration: KDownloadFileConfiguration = KDownloadFileConfiguration(),
customHeaders: Map<String, String> = emptyMap()
): Result<String>
expect fun openFile(
filePath: String
)data class KDownloadFileConfiguration(
val android: AndroidKDownloadFileConfiguration = AndroidKDownloadFileConfiguration(),
val ios: IosKDownloadFileConfiguration = IosKDownloadFileConfiguration(),
)data class AndroidKDownloadFileConfiguration(
val notificationVisibility: DownloadNotificationVisibility = DownloadNotificationVisibility.Hidden,
val title: String = "Download File",
val description: String = "Downloading file..."
)| Enum | Description |
|---|---|
| Visible | Shows notification during download only |
| VisibleAndNotifyCompleted | Shows notification during and after ✅ |
| Hidden | No notification at all |
| NotifyOnlyOnCompletion | Only notify when download completes |
data class IosKDownloadFileConfiguration(
val showLiveActivity: Boolean = false
)showLiveActivity = true enables iOS Live Activities (iOS 16.1+)KDownloadFiles supports Live Activities on iOS 16.1+.
This shows real-time download progress in the Dynamic Island and Lock Screen.
Info.plist of the main iOS app:<key>NSSupportsLiveActivities</key><true />import ActivityKit
import WidgetKit
import SwiftUI
import KDownloadFileInterop
struct Downloader_WidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: DownloadAttributes.self) { context in
LiveActivityView(
fileName: context.attributes.fileName,
progress: context.state.progress,
status: context.state.status
)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
.font(.title2)
}
DynamicIslandExpandedRegion(.trailing) {
Text(String(format: "%.0f%%", context.state.progress * 100))
.fontWeight(.semibold)
.foregroundColor(.blue)
}
DynamicIslandExpandedRegion(.bottom) {
VStack(alignment: .leading, spacing: 6) {
Text(context.attributes.fileName)
.bold()
.font(.headline)
.lineLimit(1)
.truncationMode(.middle)
ProgressView(value: context.state.progress)
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
Text(context.state.status)
.font(.caption2)
.foregroundColor(.gray)
}
.padding(.top, 4)
}
} compactLeading: {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
} compactTrailing: {
Text(String(format: "%.0f%%", context.state.progress * 100))
.fontWeight(.bold)
.foregroundColor(.blue)
} minimal: {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
}
.keylineTint(Color.blue)
}
}
}
struct LiveActivityView: View {
let fileName: String
let progress: Double
let status: String
var body: some View {
VStack(spacing: 12) {
Text("📁 \(fileName)")
.font(.headline)
.lineLimit(1)
.truncationMode(.middle)
.foregroundColor(.white)
ProgressView(value: progress)
.progressViewStyle(LinearProgressViewStyle(tint: .white))
.scaleEffect(x: 1, y: 3, anchor: .center)
.background(Color.white.opacity(0.3))
.cornerRadius(4)
Text(status)
.font(.caption)
.foregroundColor(.white.opacity(0.8))
}
.padding(16)
.background(
RoundedRectangle(cornerRadius: 20)
.fill(LinearGradient(
gradient: Gradient(colors: [Color.blue.opacity(0.8), Color.cyan]),
startPoint: .topLeading,
endPoint: .bottomTrailing
))
)
.padding(8)
}
}## 🛡 License
Licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0)KDownloadFile is a simple yet powerful Kotlin Multiplatform library that enables downloading files over HTTP with support for custom headers, saving them locally, and opening them using the system’s default app — all while abstracting away platform-specific details.
✅ Supports background downloads on Android
🍏 Supports Live Activities on iOS 16.1+ to reflect download progress
🌍 Built for KMP, with clean API and platform-safe design
implementation("io.github.the-best-is-best:kdownload-file:2.5.0")To ensure proper file access and sharing in Android, follow these steps:
<provider android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider" android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider><?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>To use KDownloadFile, you must add the Swift interop library to your iOS targets.
Use Swift Package Manager and add the following:
https://github.com/the-best-is-best/KDownloadFileInterop
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import io.github.kdownloadfile.downloadFile
import io.github.kdownloadfile.openFile
import kotlinx.coroutines.launch
@Composable
fun DownloadButton() {
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
val pathRes = downloadFile(
url = "https://your-server.com/file.jpg",
fileName = "file.jpg",
folderName = "downloads"
)
pathRes?.fold(
onSuccess = { path ->
println("File saved at: $path")
openFile(path)
},
onFailure = { error ->
println("Download failed: ${error.message}")
}
)
}
}) {
Text("Download")
}
}expect suspend fun downloadFile(
url: String,
fileName: String,
folderName: String? = null,
configuration: KDownloadFileConfiguration = KDownloadFileConfiguration(),
customHeaders: Map<String, String> = emptyMap()
): Result<String>
expect fun openFile(
filePath: String
)data class KDownloadFileConfiguration(
val android: AndroidKDownloadFileConfiguration = AndroidKDownloadFileConfiguration(),
val ios: IosKDownloadFileConfiguration = IosKDownloadFileConfiguration(),
)data class AndroidKDownloadFileConfiguration(
val notificationVisibility: DownloadNotificationVisibility = DownloadNotificationVisibility.Hidden,
val title: String = "Download File",
val description: String = "Downloading file..."
)| Enum | Description |
|---|---|
| Visible | Shows notification during download only |
| VisibleAndNotifyCompleted | Shows notification during and after ✅ |
| Hidden | No notification at all |
| NotifyOnlyOnCompletion | Only notify when download completes |
data class IosKDownloadFileConfiguration(
val showLiveActivity: Boolean = false
)showLiveActivity = true enables iOS Live Activities (iOS 16.1+)KDownloadFiles supports Live Activities on iOS 16.1+.
This shows real-time download progress in the Dynamic Island and Lock Screen.
Info.plist of the main iOS app:<key>NSSupportsLiveActivities</key><true />import ActivityKit
import WidgetKit
import SwiftUI
import KDownloadFileInterop
struct Downloader_WidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: DownloadAttributes.self) { context in
LiveActivityView(
fileName: context.attributes.fileName,
progress: context.state.progress,
status: context.state.status
)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
.font(.title2)
}
DynamicIslandExpandedRegion(.trailing) {
Text(String(format: "%.0f%%", context.state.progress * 100))
.fontWeight(.semibold)
.foregroundColor(.blue)
}
DynamicIslandExpandedRegion(.bottom) {
VStack(alignment: .leading, spacing: 6) {
Text(context.attributes.fileName)
.bold()
.font(.headline)
.lineLimit(1)
.truncationMode(.middle)
ProgressView(value: context.state.progress)
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
Text(context.state.status)
.font(.caption2)
.foregroundColor(.gray)
}
.padding(.top, 4)
}
} compactLeading: {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
} compactTrailing: {
Text(String(format: "%.0f%%", context.state.progress * 100))
.fontWeight(.bold)
.foregroundColor(.blue)
} minimal: {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
}
.keylineTint(Color.blue)
}
}
}
struct LiveActivityView: View {
let fileName: String
let progress: Double
let status: String
var body: some View {
VStack(spacing: 12) {
Text("📁 \(fileName)")
.font(.headline)
.lineLimit(1)
.truncationMode(.middle)
.foregroundColor(.white)
ProgressView(value: progress)
.progressViewStyle(LinearProgressViewStyle(tint: .white))
.scaleEffect(x: 1, y: 3, anchor: .center)
.background(Color.white.opacity(0.3))
.cornerRadius(4)
Text(status)
.font(.caption)
.foregroundColor(.white.opacity(0.8))
}
.padding(16)
.background(
RoundedRectangle(cornerRadius: 20)
.fill(LinearGradient(
gradient: Gradient(colors: [Color.blue.opacity(0.8), Color.cyan]),
startPoint: .topLeading,
endPoint: .bottomTrailing
))
)
.padding(8)
}
}## 🛡 License
Licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0)