Library / SDK
mirzemehdi/KMPNotifier avatar
mirzemehdi/KMPNotifier

KMPNotifier: one notification API across five Kotlin targets

Kotlin Multiplatform Push Notification Library targetting android, iOS, Desktop and Web (JS and Wasm)

665 stars45 forksKotlinApache-2.0

At a glance

What is it?
KMPNotifier is a Kotlin Multiplatform library for local notifications on Android, iOS, desktop, JS and wasm, plus Firebase push on Android and iOS, with four publishable artifacts and a support table that is explicit about what each platform cannot do. The desktop and web targets accept a push call and do nothing, and that contract is stated in the documentation rather than left to be discovered.
Who is it for?
KMPNotifier suits Kotlin Multiplatform teams that want one notification implementation across their mobile, desktop and web targets and would rather share the call sites than write expect and actual declarations per platform.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem is writing the same notification code five times

KMPNotifier is a Kotlin Multiplatform notification library. Local notifications target android, iOS, desktop, js and wasm, and push notifications through Firebase Cloud Messaging target android and iOS.

The problem it solves is straightforward and extremely common. A Kotlin Multiplatform application shares its business logic but not its platform APIs, so a feature that notifies the user has to be written once per target, with expect and actual declarations, or with a wrapper that hides the platform differences badly. Notification handling is a particularly poor candidate for that duplication, because the surface is large: local notifications, scheduled delivery, action buttons, inline text input, rich images, custom sounds, click and payload delivery for deep linking, and a push channel with its own token lifecycle.

The library is used in production by FindTravelNow, a KMP project by the same author, which is a useful signal about the level of scrutiny behind it, and the README links full API documentation hosted at mirzemehdi.github.io/KMPNotifier.

The primary language is Kotlin, the licence is Apache-2.0, and the build badge shows Kotlin 2.4.0. The repository is a full multiplatform project rather than a single module, with androidApp, iosApp, desktopApp, webApp and shared directories, plus one directory per published module, which is a useful indicator that the sample apps in the repository are real applications rather than stubs.

The support table is the most useful page in the README

The feature and platform support table is what sets this library apart from a README that simply claims cross-platform support.

Local notifications with a title, body and payload are supported everywhere: android, iOS, desktop and JS or wasmJs. Remove and remove all are supported everywhere too, and so are the click and action listeners. So the baseline is genuinely uniform.

The rest is not. Scheduled notifications are supported on android and iOS, and on desktop and JS or wasm the call is accepted but the notification shows now, with the schedule ignored. Images, whether from a URL or a local file, are android and iOS only, as are action buttons and actions with text input. A custom sound is supported on android through a channel and on iOS through a bundle, and nowhere else. Push notifications through Firebase are android and iOS, and on desktop and web they are described as a no-op mock where the API exists and compiles, the token is null, and calls do nothing.

The README explains both shorthands in one line, and the reason given for the mock is the important part: shared code needs no expect/actual. In other words, the library keeps your common code compiling on every target, and the cost is that on the two targets that cannot do push, the call is a well-defined no-op rather than a compile error.

That is a deliberate trade, and whether it is right depends on your code. A shared function that notifies the user will compile and run everywhere, but on a web build the user may simply never be told anything.

Version 2 split one artifact into four, and kept a 1.x umbrella alive

Since 2.0.0 the library is split into focused modules, and the module table is short enough to be the whole dependency story.

kmpnotifier-core is the shared core holding configuration, permissions and events, and it is pulled in automatically, though you still export it in the iOS framework. kmpnotifier-local is for local notifications with no Firebase involved. kmpnotifier-push-firebase is the Firebase push module, and it includes local, with a no-op mock on desktop and web. And kmpnotifier is a deprecated 1.x compatibility umbrella that includes everything and targets all platforms.

The motivation for the split is the last line of the feature list: modular, so you can use local notifications without pulling in Firebase. On a project that only shows local notifications, adding the Firebase SDK and a google-services configuration to your build for a feature you do not use is a real cost, and the module split removes it.

The upgrade story is handled with paperwork rather than a hard break. The README says that upgrading from 1.x keeps your code working, and points at MIGRATION.md for the deprecation mapping and CHANGELOG.md for what changed. The deprecated/ directory at the root of the repository is there so the old umbrella can keep building while people migrate.

The release history matches that structure. v1.6.1 is dated 2025-12-09, v2.0.0-RC on 2026-06-13, and v2.0.1 on 2026-08-14, so the release candidate sat on the main branch for a while before the final version.

Gradle wiring, and the three exports the iOS framework needs

KMPNotifier is published on Maven Central, and the setup is described in the standard Kotlin Multiplatform sequence.

First, add mavenCentral() to repositories in the root build.gradle.kts or settings.gradle, and if you use push notifications also add the google-services plugin:

kotlin
repositories { 
  mavenCentral()
}

Then declare the dependencies in the shared module, with the local module for local notifications on all targets with no Firebase, and the push module for Firebase push, which delivers on android and ios and is a no-op mock on desktop and web:

kotlin
sourceSets {
  commonMain.dependencies {
    api("io.github.mirzemehdi:kmpnotifier-local:<version>")
    api("io.github.mirzemehdi:kmpnotifier-push-firebase:<version>")
  }
}

The iOS side is the part that catches people out, because the modules have to be exported from the framework, not just depended on. The README shows the export block for the three iOS targets, iosX64, iosArm64 and iosSimulatorArm64:

kotlin
listOf(iosX64(),iosArm64(),iosSimulatorArm64()).forEach { iosTarget ->
  iosTarget.binaries.framework {
    export("io.github.mirzemehdi:kmpnotifier-core:<version>")
    export("io.github.mirzemehdi:kmpnotifier-local:<version>")
    export("io.github.mirzemehdi:kmpnotifier-push-firebase:<version>") // if using push
  }
}

Export the core module as well as the ones you use. A missing export shows up on the Swift side as a symbol the framework does not contain, which is a confusing error the first time you meet it.

Push notifications cost you a Firebase project

The installation section is where the cost of push becomes concrete, and it is worth reading before choosing the push module.

For push notifications you need the basic Firebase setup, following the official guideline, which means initializing a project in Firebase, adding google-services.json to the Android app, and adding GoogleService-Info.plist to the iOS app. Local-only usage needs no Firebase setup at all.

On the Android side, the plugin has to be applied in the androidApp build.gradle.kts:

kotlin
plugins {
  id("com.android.application")
  id("com.google.gms.google-services")
}

The minimum requirements section adds the version detail that catches teams out. Kotlin 2.4.0 or newer, and the library consumes Firebase through Swift Package Manager rather than the old CocoaPods route, which is a change for iOS projects that are still on the older integration. Android needs minSdkVersion 23. For iOS, iOS 16.0 is required for push with the kmpnotifier-push-firebase module, while the local-only modules work on lower targets.

So the decision is fairly clean. If your app only shows local notifications, you take the local module and skip all of the above. If you need push, you are committing to Firebase, to two configuration files, to the google-services plugin and to a raised iOS deployment target.

One initialize call installs the extensions you ask for

At application start you initialize the library once, passing the platform configuration and the capabilities you want, described in the README as extensions.

kotlin
// Local notifications only:
KMPNotifier.initialize(configuration, LocalNotifications)

// Local + Firebase push (android/ios; FirebasePush installs LocalNotifications automatically):
KMPNotifier.initialize(configuration, FirebasePush)

The extension model is the API's main idea. LocalNotifications gives you local delivery, and FirebasePush gives you push and installs LocalNotifications for you, so you do not have to list both. Adding an extension later is a second call with the new extension, since calling initialize again is a no-op for the configuration but does install any extension not yet installed.

There is one platform rule that is easy to miss. On iOS, initialize must be called from the main thread, because the notification delegate is installed during init and is required to receive cold-start clicks. Cold start is the case where the app was not running when the notification arrived, so a user tapping it launches the app, and the payload has to reach your code. If the delegate is installed on a background thread or too late, the click arrives and nothing handles it.

The repository shows the Android application class in the setup details, with the README warning that on iOS the call belongs in application start, and the shared/ and platform app directories make the placement easy to find.

Requirements, sample apps and where the documentation lives

The last pieces of the picture are the sample applications and the release record.

The repository root holds androidApp, iosApp, desktopApp and webApp as separate sample applications, plus shared/ for the shared code, and the module directories kmpnotifier-core, kmpnotifier-local and kmpnotifier-push-firebase at the top level rather than nested. There is a build-logic/ directory for the Gradle convention plugins, a kotlin-js-store/ directory for the JavaScript dependencies, a deprecated/ directory for the old umbrella, and a firebase_push_notification_sender.py script at the root, which is presumably how the push side is exercised locally.

The full API documentation is at mirzemehdi.github.io/KMPNotifier, and there are two related blog posts linked from the README on ProAndroidDev, one on implementing push notifications in Kotlin Multiplatform and one on the update that added web and desktop support. For a library whose hardest parts are the platform setup steps, a walkthrough article is often more useful than reference documentation, and both are here.

On maintenance, the release list shows v2.0.1 from 2026-08-14 as the newest tag, and the last push to the repository was on 2026-10-18, so the branch is ahead of the release. The Apache-2.0 licence and the Maven Central publication mean there is no licensing friction for commercial use, and the versioning scheme gives the usual guidance: 1.6.1 is the last of the old line, 2.0.0-RC was a preview of the modular rewrite, and 2.0.1 is the current artifact set.

Editorial conclusion

KMPNotifier suits Kotlin Multiplatform teams that want one notification implementation across their mobile, desktop and web targets and would rather share the call sites than write expect and actual declarations per platform. It does not suit an Android-only or iOS-only product, where the platform APIs cost you nothing, and it does not suit a team that needs scheduled notifications, rich images, action buttons or a custom sound on desktop and web, because those are marked as unsupported there. Check three things before you adopt it: that your Kotlin version is 2.4.0 or newer and your Android minSdkVersion is at least 23, that you accept the no-op behaviour of push calls on desktop and web, and that you read the platform support table row by row rather than assuming parity. The last push to the repository was on 2026-10-18 and the newest release is v2.0.1 from 2026-08-14.

Frequently asked questions

Which platforms does KMPNotifier support?

Local notifications target android, iOS, desktop, JS and wasm, and push notifications through Firebase Cloud Messaging target android and iOS only. On desktop and web the push call is a no-op mock where the API compiles, the token is null and calls do nothing, so shared code needs no expect/actual.

What are the minimum requirements for KMPNotifier?

Kotlin 2.4.0 or newer, Android minSdkVersion 23, and iOS 16.0 for the push module, while the local-only modules work on lower iOS targets. Firebase is consumed through Swift Package Manager.

How do I add KMPNotifier to a Gradle project?

Add mavenCentral() to repositories, declare the kmpnotifier-local or kmpnotifier-push-firebase dependency in commonMain, and export the modules from the iOS framework for the iosX64, iosArm64 and iosSimulatorArm64 targets. For push on Android, also apply the com.google.gms.google-services plugin in the androidApp build file.

What is the difference between the four KMPNotifier modules?

kmpnotifier-core holds shared configuration, permissions and events and is pulled in automatically, kmpnotifier-local is for local notifications with no Firebase, kmpnotifier-push-firebase adds Firebase push and includes local, and kmpnotifier is a deprecated 1.x compatibility umbrella that includes everything.

Where must KMPNotifier.initialize be called on iOS?

On the main thread, because the notification delegate is installed during init and is required to receive cold-start clicks. Calling initialize again is a no-op for the configuration, but it does install any extension that is not yet installed.

Official sources

  1. License: Apache-2.0
  2. mirzemehdi/KMPNotifier on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mirzemehdi-kmpnotifier.svg)](https://hysenlabs.com/projects/mirzemehdi-kmpnotifier)

Was this useful?