Handling notifications in Kotlin Multiplatform isn’t exactly fun. Android wants AlarmManager
or WorkManager
, iOS expects UNUserNotificationCenter
, and good luck trying to share any logic.
So I built Alarmee, a Kotlin/Compose Multiplatform library to schedule local and push notifications using a single, shared API. It works on Android and iOS, and lets you create one-off or repeating alarms, send push notifications, and even show instant alerts.
Let’s dive in.
Features
➡️ Schedule one-time alarms
➡️ Set repeating alarms (hourly, daily, weekly, etc.)
➡️ Trigger instant notifications
➡️ Handle push notifications (FCM and APNs)
➡️ Customize platform behaviors
Alarmee works with Kotlin Multiplatform and Compose Multiplatform, so you can use it from your shared codebase.
Installation
To get started, add the Maven Central repository in your settings.gradle.kts
:
repositories {
mavenCentral()
}
If you're using a version catalog (libs.versions.toml
), declare the version and dependency like this:
[versions]
alarmee = "2.0.0" # Or the latest version
[libraries]
alarmee = { group = "io.github.tweener", name = "alarmee", version.ref = "alarmee" }
Then use it in your build.gradle.kts
:
dependencies {
implementation(libs.alarmee)
}
If you’re not using a catalog, add the dependency directly:
val alarmee_version = "2.0.0"
implementation("io.github.tweener:alarmee:$alarmee_version")
Configuration (Android & iOS)
Before using Alarmee, you’ll need to provide platform-specific configuration. Here's how to do it.
Step 1: Declare an expect function in shared code
In your commonMain
sourceSet:
expect fun createAlarmeePlatformConfiguration(): AlarmeePlatformConfiguration
Step 2: Android implementation
In your androidMain
, provide the actual configuration:
actual fun createAlarmeePlatformConfiguration(): AlarmeePlatformConfiguration =
AlarmeeAndroidPlatformConfiguration(
notificationIconResId = R.drawable.ic_notification,
notificationIconColor = Color.Red,
notificationChannels = listOf(
AlarmeeNotificationChannel(
id = "dailyNewsChannelId",
name = "Daily news notifications",
importance = NotificationManager.IMPORTANCE_HIGH,
soundFilename = "notifications_sound"
),
AlarmeeNotificationChannel(
id = "breakingNewsChannelId",
name = "Breaking news notifications",
importance = NotificationManager.IMPORTANCE_LOW
)
)
)
Step 3: iOS implementation
In your iosMain
sourceSet, return the built-in platform config:
actual fun createAlarmeePlatformConfiguration(): AlarmeePlatformConfiguration =
AlarmeeIosPlatformConfiguration
⚠️ Make sure to add Firebase to your Xcode project and enable Remote notifications and Push notifications in the capabilities.
Step 4: Initialize in your root Composable
Then, in your commonMain
sourceSet, Use the rememberAlarmeeService helper in a Composable function:
val alarmService = rememberAlarmeeService(
platformConfiguration = createAlarmeePlatformConfiguration()
)
Usage
First, make sure your app has notification permissions. You can use moko-permissions to help with this.
Access the local notification service
To use local notifications, you need to get the LocalNotificationService
like this:
val localService = alarmService.local
Access the push notification service (mobile only)
To use push notifications, you need to get the PushNotificationService
like this:
val pushService = (alarmService as? MobileAlarmeeService)?.push
Push notifications are only supported on Android and iOS, so be sure to call this from one of those targets.
Schedule one-off alarm
To schedule an alarm for a specific date and time, pass an Alarmee
instance with scheduledDateTime
:
localService.schedule(
alarmee = Alarmee(
uuid = "myAlarmId",
scheduledDateTime = LocalDateTime(2025, 1, 12, 17, 0),
notificationTitle = "🎉 Congratulations!",
notificationBody = "This will appear at the specified time.",
deepLinkUri = "https://example.com",
androidNotificationConfiguration = AndroidNotificationConfiguration(
priority = AndroidNotificationPriority.HIGH,
channelId = "dailyNewsChannelId"
),
iosNotificationConfiguration = IosNotificationConfiguration()
)
)
Schedule repeating alarm
Alarmee supports both fixed and custom repeat intervals.
Daily at 9:30 AM
Here's how to repeat an alarm every day at the same time:
repeatInterval = RepeatInterval.Daily
Every 15 minutes (custom)
If you want a custom duration (like every 15 minutes), use RepeatInterval.Custom
:
repeatInterval = RepeatInterval.Custom(duration = 15.minutes)
Cancel an alarm
To stop a scheduled alarm, just cancel it by its uuid
:
localService.cancel(uuid = "myAlarmId")
Trigger an instant notification
You can display a notification immediately using immediate(...)
:
localService.immediate(
alarmee = Alarmee(
uuid = "now",
notificationTitle = "🚀 Instant alert!",
notificationBody = "This shows right away.",
androidNotificationConfiguration = AndroidNotificationConfiguration(
priority = AndroidNotificationPriority.DEFAULT,
channelId = "immediateChannelId"
),
iosNotificationConfiguration = IosNotificationConfiguration()
)
)
Customize notifications
Alarmee supports custom sound files, icon colors, and badge updates on iOS.
Custom sound
Android: Place the
.ogg
file inres/raw
and reference it insoundFilename
when defining your notification channel.iOS: Add the file to the main bundle and pass it in
IosNotificationConfiguration
.
Custom icon (Android only)
Set a default icon in the platform config or override per alarm:
notificationIconResId = R.drawable.ic_notification
notificationIconColor = Color.Yellow
Badge count (iOS only)
Use this to update or clear the app icon badge:
IosNotificationConfiguration(badge = 4) // Set badge
IosNotificationConfiguration(badge = 0) // Clear badge
Push Notifications
On Android and iOS, Alarmee supports FCM and APNs through MobileAlarmeeService.push
.
Right now, Alarmee will automatically display a notification when a push message with title
and body
is received. More custom handling is coming soon.
Try Alarmee
If you're building a Kotlin Multiplatform app and want to handle notifications without writing platform-specific mess, give Alarmee a shot.
It already powers real apps like Bloomeo and is included by default in KMPShip, my KMP boilerplate.
📚 GitHub: github.com/Tweener/alarmee
🌐 Website: kmpship.app
If you find it helpful, star the repo or drop your feedback in the comments.