Sitemap

Alarmee: Schedule Local and Push Notifications in KMP

4 min readJun 30, 2025

--

Notifications are essential in mobile apps, but handling local and push notifications in Kotlin Multiplatform (KMP) can quickly become a mess.

You need to deal with AlarmManager on Android, UNUserNotificationCenter on iOS, and glue it all together with shared 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.

Photo by Jamie Street on Unsplash

Features

  • Schedule one-time alarms
  • Set repeating alarms (hourly, daily, weekly, etc.)
  • Trigger instant notifications
  • Handle push notifications with 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")

You can get the latest version from Maven Central.

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 Alarmee

Then, in your commonMain sourceSet, Use the rememberAlarmeeService helper in a Composable function:

val alarmService = rememberAlarmeeService(
platformConfiguration = createAlarmeePlatformConfiguration()
)

Usage

Before you do anything, make sure the app has notification permissions. You can use moko-permissions to handle this.

Once you have permissions and Alarmee is initialized, get the services like 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 (only on Android & iOS)

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:

localService.schedule(
alarmee = Alarmee(
uuid = "dailyAlarm",
scheduledDateTime = LocalDateTime(2025, 1, 12, 9, 30),
repeatInterval = RepeatInterval.Daily,
notificationTitle = "🔁 Daily reminder",
notificationBody = "This will trigger every day at 9:30.",
androidNotificationConfiguration = AndroidNotificationConfiguration(
priority = AndroidNotificationPriority.DEFAULT,
channelId = "dailyNewsChannelId"
),
iosNotificationConfiguration = IosNotificationConfiguration()
)
)

Every 15 minutes (custom)

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

localService.schedule(
alarmee = Alarmee(
uuid = "repeat15min",
repeatInterval = RepeatInterval.Custom(duration = 15.minutes),
notificationTitle = "🔁 Every 15 min",
notificationBody = "This will trigger every 15 minutes.",
androidNotificationConfiguration = AndroidNotificationConfiguration(
priority = AndroidNotificationPriority.DEFAULT,
channelId = "otherChannelId"
),
iosNotificationConfiguration = IosNotificationConfiguration()
)
)

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 lets you customize sound, icon, and badge behavior.

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

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.

Alarmee will automatically display a notification when a push message with title and body is received. If an optional imageUrl is provided in the payload, it will be displayed within the notification.

Notifications with image, collapsed & expanded, on iOS and Android

Try Alarmee

If you’re building a Kotlin Multiplatform app and dreading the mess of notifications, Alarmee is here to save you time and sanity.

It already powers real apps like Bloomeo, handling local and push notifications across Android and iOS using a single, shared API.

No hacks. No duplicated logic. Just clean, predictable code.

👉 GitHub Repo
⭐ Give it a star if you find it useful
📦 Add it to your project today
🔔 Focus on features, not platform quirks

--

--

Vivien Mahé
Vivien Mahé

Written by Vivien Mahé

Creating mobile apps while sharing my journey and learnings.

No responses yet