
In-app update manager offering pluggable release sources (GitHub-ready), streaming download progress via StateFlow, customizable asset matching, installer hooks, optional Compose UI components, pre-release and background update support.
A Kotlin Multiplatform library for in-app updates. Supports Android and Desktop (JVM).
StateFlow<UpdateState>
.apk, .msi, .dmg, etc.)| Module | Description |
|---|---|
:core |
Headless KMP library — AppUpdater, UpdateSource, downloader, installer |
:compose-ui |
Optional Compose Multiplatform UI — UpdateCard, DownloadProgressIndicator, UpdateBanner
|
:sample:android |
Android demo app |
:sample:desktop |
Desktop demo app |
Platform defaults (downloader, installer, asset matcher) are wired automatically:
val updater = AppUpdater.github(
context = applicationContext,
owner = "your-org",
repo = "your-app",
// currentVersion auto-detected from PackageManager
)
// Check → Download → Install
updater.checkForUpdate()
updater.downloadUpdate() // observe updater.state for progress
updater.installUpdate()val updater = AppUpdater.github(
owner = "your-org",
repo = "your-app",
currentVersion = "1.0.0",
)Implement UpdateSource to connect any backend:
val updater = AppUpdater(
currentVersion = "1.0.0",
source = object : UpdateSource {
override suspend fun fetchReleases() = listOf(/* your releases */)
},
downloader = myDownloader,
installer = myInstaller,
assetMatcher = { it.endsWith(".msi") },
)UpdateCard(updater = updater)Idle → Checking → UpdateAvailable → Downloading(progress) → ReadyToInstall
→ UpToDate
→ Error
Observe updater.state: StateFlow<UpdateState> for reactive UI updates.
// Compose
val state by updater.state.collectAsState()
when (state) {
is UpdateState.Downloading -> DownloadProgressIndicator(state.progress, ...)
// ...
}
// Coroutines
updater.state.collect { state -> /* react */ }Use WorkManager to check for updates periodically in the background:
class UpdateCheckWorker(
context: Context,
params: WorkerParameters,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val updater = AppUpdater.github(
context = applicationContext,
owner = "your-org",
repo = "your-app",
)
val release = updater.checkForUpdate()
if (release != null) {
// Show a notification prompting the user to update
}
return Result.success()
}
}
// Schedule periodic checks (e.g. once every 24 hours)
val request = PeriodicWorkRequestBuilder<UpdateCheckWorker>(24, TimeUnit.HOURS)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"update_check",
ExistingPeriodicWorkPolicy.KEEP,
request,
)On Desktop JVM, use a coroutine-based timer to poll for updates:
fun startBackgroundUpdateCheck(updater: AppUpdater, scope: CoroutineScope) {
scope.launch {
while (isActive) {
updater.checkForUpdate()
delay(24.hours)
}
}
}Add to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>And res/xml/file_paths.xml:
<paths>
<cache-path name="app_updates" path="app_updates/" />
</paths>Apache 2.0
A Kotlin Multiplatform library for in-app updates. Supports Android and Desktop (JVM).
StateFlow<UpdateState>
.apk, .msi, .dmg, etc.)| Module | Description |
|---|---|
:core |
Headless KMP library — AppUpdater, UpdateSource, downloader, installer |
:compose-ui |
Optional Compose Multiplatform UI — UpdateCard, DownloadProgressIndicator, UpdateBanner
|
:sample:android |
Android demo app |
:sample:desktop |
Desktop demo app |
Platform defaults (downloader, installer, asset matcher) are wired automatically:
val updater = AppUpdater.github(
context = applicationContext,
owner = "your-org",
repo = "your-app",
// currentVersion auto-detected from PackageManager
)
// Check → Download → Install
updater.checkForUpdate()
updater.downloadUpdate() // observe updater.state for progress
updater.installUpdate()val updater = AppUpdater.github(
owner = "your-org",
repo = "your-app",
currentVersion = "1.0.0",
)Implement UpdateSource to connect any backend:
val updater = AppUpdater(
currentVersion = "1.0.0",
source = object : UpdateSource {
override suspend fun fetchReleases() = listOf(/* your releases */)
},
downloader = myDownloader,
installer = myInstaller,
assetMatcher = { it.endsWith(".msi") },
)UpdateCard(updater = updater)Idle → Checking → UpdateAvailable → Downloading(progress) → ReadyToInstall
→ UpToDate
→ Error
Observe updater.state: StateFlow<UpdateState> for reactive UI updates.
// Compose
val state by updater.state.collectAsState()
when (state) {
is UpdateState.Downloading -> DownloadProgressIndicator(state.progress, ...)
// ...
}
// Coroutines
updater.state.collect { state -> /* react */ }Use WorkManager to check for updates periodically in the background:
class UpdateCheckWorker(
context: Context,
params: WorkerParameters,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val updater = AppUpdater.github(
context = applicationContext,
owner = "your-org",
repo = "your-app",
)
val release = updater.checkForUpdate()
if (release != null) {
// Show a notification prompting the user to update
}
return Result.success()
}
}
// Schedule periodic checks (e.g. once every 24 hours)
val request = PeriodicWorkRequestBuilder<UpdateCheckWorker>(24, TimeUnit.HOURS)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"update_check",
ExistingPeriodicWorkPolicy.KEEP,
request,
)On Desktop JVM, use a coroutine-based timer to poll for updates:
fun startBackgroundUpdateCheck(updater: AppUpdater, scope: CoroutineScope) {
scope.launch {
while (isActive) {
updater.checkForUpdate()
delay(24.hours)
}
}
}Add to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>And res/xml/file_paths.xml:
<paths>
<cache-path name="app_updates" path="app_updates/" />
</paths>Apache 2.0