Alarmee: Local and Push Notifications in Kotlin Multiplatform (KMP)
Vivien Mahé

Vivien Mahé @vivien_mahe

About: Indie maker working on all things mobile app development with Kotlin Multiplatform. My secret ingredients for success: coffees, failures, and repeat.

Location:
France
Joined:
Jun 18, 2025

Alarmee: Local and Push Notifications in Kotlin Multiplatform (KMP)

Publish Date: Jul 2
0 0

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()
}
Enter fullscreen mode Exit fullscreen mode

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" }
Enter fullscreen mode Exit fullscreen mode

Then use it in your build.gradle.kts:

dependencies {
    implementation(libs.alarmee)
}
Enter fullscreen mode Exit fullscreen mode

If you’re not using a catalog, add the dependency directly:

val alarmee_version = "2.0.0"
implementation("io.github.tweener:alarmee:$alarmee_version")
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
            )
        )
    )
Enter fullscreen mode Exit fullscreen mode

Step 3: iOS implementation

In your iosMain sourceSet, return the built-in platform config:

actual fun createAlarmeePlatformConfiguration(): AlarmeePlatformConfiguration =
    AlarmeeIosPlatformConfiguration
Enter fullscreen mode Exit fullscreen mode

⚠️ 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()
)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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()
    )
)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Every 15 minutes (custom)

If you want a custom duration (like every 15 minutes), use RepeatInterval.Custom:

repeatInterval = RepeatInterval.Custom(duration = 15.minutes)
Enter fullscreen mode Exit fullscreen mode

Cancel an alarm

To stop a scheduled alarm, just cancel it by its uuid:

localService.cancel(uuid = "myAlarmId")
Enter fullscreen mode Exit fullscreen mode

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()
    )
)
Enter fullscreen mode Exit fullscreen mode

Customize notifications

Alarmee supports custom sound files, icon colors, and badge updates on iOS.

Custom sound

  • Android: Place the .ogg file in res/raw and reference it in soundFilename 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
Enter fullscreen mode Exit fullscreen mode

Badge count (iOS only)

Use this to update or clear the app icon badge:

IosNotificationConfiguration(badge = 4) // Set badge
IosNotificationConfiguration(badge = 0) // Clear badge
Enter fullscreen mode Exit fullscreen mode

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.

Comments 0 total

    Add comment