KMPNotifier

repository·main·Indexed 20 days ago

https://github.com/mirzemehdi/kmpnotifier

A Kotlin Multiplatform library providing a unified API for local and push notifications (via FCM) across Android, iOS, Desktop, and Web (JS/Wasm). It features a modular architecture with kmpnotifier-core, kmpnotifier-local, and kmpnotifier-push-firebase. Supported capabilities include scheduled notifications, action buttons with text input, and custom sounds on Android and iOS, with a builder DSL for configuring notification content and payloads.

Tokens
6.7K
Snippets
17
Records
21
Agent score
21%

What's inside KMPNotifier

  1. Overview of KMPNotifier features and platform support

    main

    KMPNotifier is a Kotlin Multiplatform library for local and push notifications.

    Supported Features by Platform

    FeatureAndroidiOSDesktopJS / wasmJs
    Local notification (title/body/payload)
    Remove / remove all
    Scheduled notification (scheduledAt)⛔️ (shows now)⛔️ (shows now)
    Image (NotificationImage.Url / .File)⛔️⛔️
    Action buttons⛔️⛔️
    Action with text input⛔️⛔️
    Click & action listener
    Custom sound✅ (channel)✅ (bundle)⛔️⛔️
    Push notification (FCM)⛔️ (no-op)⛔️ (no-op)

    Note: ⛔️ shows now means the schedule is ignored and it shows immediately. no-op mock means the API exists for compilation but does nothing.

  2. Choose the right KMPNotifier module

    main

    The library is modularized to allow using local notifications without pulling in Firebase dependencies.

    ArtifactUse it forTargets
    kmpnotifier-coreShared core (configuration, permissions, events). Pulled in automatically.android, ios, jvm, js, wasmJs
    kmpnotifier-localLocal notifications only (no Firebase).android, ios, jvm, js, wasmJs
    kmpnotifier-push-firebaseFirebase push notifications (includes kmpnotifier-local).android, ios, jvm, js, wasmJs
    kmpnotifierDeprecated 1.x compatibility umbrella.all
  3. How KMPNotifier works: Facades and Initialization

    main

    KMPNotifier uses a facade pattern to provide access to different notification types. You interact with the library through the KMPNotifier object, which exposes two primary notifiers:

    1. KMPNotifier.localNotifier: Handles local notifications across all platforms.
    2. KMPNotifier.firebasePushNotifier: Handles push token and topic management (real on Android/iOS, no-op on others).

    CRITICAL: You must call the platform-specific initialize method (see Platform Setup) before accessing any notifier. Accessing a notifier before initialization throws an IllegalStateException. You can check the status using KMPNotifier.isInitialized.

    if (KMPNotifier.isInitialized) {
        KMPNotifier.localNotifier.notify { ... }
    }
  4. Initialize KMPNotifier on Android

    main

    Initialize the library once in your Application.onCreate().

    Requirements:

    • minSdkVersion 23
    • For Android 13 (API 33+), you must manually request the POST_NOTIFICATIONS runtime permission in an Activity.

    Configuration Options:

    • notificationIconResId: Resource ID for the notification icon.
    • showPushNotification: If false, foreground push notifications will not be shown to the user (though content can still be accessed via PushListener).
    • notificationChannelData: Optional customization for the notification channel.
    class MyApplication : Application() {
        override fun onCreate() {
            super.onCreate()
            KMPNotifier.initialize(
                configuration = NotificationPlatformConfiguration.Android(
                    notificationIconResId = R.drawable.ic_launcher_foreground,
                    showPushNotification = true,
                    notificationChannelData = NotificationPlatformConfiguration.Android.NotificationChannelData(),
                ),
                FirebasePush, // Use LocalNotifications if you don't want push
            )
        }
    }
  5. Schedule and cancel local notifications

    main

    To schedule a notification, set the scheduledAt property to the desired epoch milliseconds.

    • Android: Uses AlarmManager.
    • iOS: Uses UNTimeIntervalNotificationTrigger.
    • Desktop/Web: The value is ignored and the notification shows immediately.

    To cancel a pending scheduled notification or remove a currently visible one, use remove(id). This also cancels the underlying alarm on Android.

    // Schedule for 1 minute from now
    KMPNotifier.localNotifier.notify {
      id = 777
      title = "Reminder"
      body = "Stand up and stretch 🧘"
      scheduledAt = Clock.System.now().toEpochMilliseconds() + 60_000
    }
    
    // Cancel the notification
    KMPNotifier.localNotifier.remove(777)
  6. Initialize KMPNotifier on Web (JS/Wasm)

    main

    Initialize using NotificationPlatformConfiguration.Web.

    Note: On macOS, you must also allow notifications for the specific browser in the system settings to see web notifications.

    fun main()  {
        KMPNotifier.initialize(
            NotificationPlatformConfiguration.Web(
                askNotificationPermissionOnStart = true,
                notificationIconPath = null
            ),
            LocalNotifications,
        )
    }
  7. Initialize KMPNotifier on iOS

    main

    For push notifications, follow these steps in Xcode:

    1. Add firebase-ios-sdk via Swift Package Manager (specifically the FirebaseMessaging product, version 12.14.0 exact).
    2. Add Push Notifications and Background Modes (Remote Notifications) capabilities.
    3. Call FirebaseApp.configure().
    4. Set the apnsToken in didRegisterForRemoteNotificationsWithDeviceToken.

    Important: KMPNotifier.shared.initialize must be called from the main thread to ensure the notification delegate is installed correctly for cold-start clicks.

    Configuration Options:

    • showPushNotification: Enables/disables push UI.
    • askNotificationPermissionOnStart: Automatically prompts for permission.
    • notificationSoundName: Optional custom sound bundle name.
    import SwiftUI
    import shared
    import FirebaseCore
    import FirebaseMessaging
    
    class AppDelegate: NSObject, UIApplicationDelegate {
    
      func application(_ application: UIApplication,
                       didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    
          FirebaseApp.configure() // Required
    
          KMPNotifier.shared.initialize(
              configuration: NotificationPlatformConfigurationIos(
                  showPushNotification: true,
                  askNotificationPermissionOnStart: true,
                  notificationSoundName: nil
              ),
              extensions: [FirebasePush.shared]
          )
    
        return true
      }
    
      func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
            Messaging.messaging().apnsToken = deviceToken
      }
        
    }
    
    @main
    struct iOSApp: App {
        @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
  8. Initialize KMPNotifier on Desktop

    main

    Place a notification icon in your resources/common folder. Use the absolute path to this icon in the configuration.

    fun main() = application {
        KMPNotifier.initialize(
            NotificationPlatformConfiguration.Desktop(
                showPushNotification = true,
                notificationIconPath = composeDesktopResourcesPath() + File.separator + "ic_notification.png"
            ),
            LocalNotifications,
        )
        
        Window(onCloseRequest = ::exitApplication, title = "KMPNotifier Desktop") {
            App()
        }
    }
  9. Migrate to KMPNotifier 2.0 Artifacts

    main

    KMPNotifier 2.0 is modularized. Instead of a single umbrella dependency, you can now pick specific modules based on your needs.

    Requirements: Your project must use Kotlin 2.4.0+ to consume 2.0.0 artifacts.

    Available Modules:

    • kmpnotifier-core: Configuration, permissions, logging, and shared events.
    • kmpnotifier-local: Local notifications (depends on core).
    • kmpnotifier-push-firebase: Firebase Cloud Messaging push (depends on local; provides a no-op mock on desktop/web).
    • kmpnotifier: The deprecated umbrella module that pulls in all the above (behaves like 1.x).

    Handling Duplicate Class Errors: If a transitive dependency pulls in kmpnotifier:1.x while you are using the new modules, Android builds will fail due to duplicate classes. You must force-upgrade or exclude the old artifact: implementation("...") { exclude(group = "io.github.mirzemehdi", module = "kmpnotifier") }

    commonMain.dependencies {
        // Local notifications only — no Firebase anywhere:
        api("io.github.mirzemehdi:kmpnotifier-local:2.0.0")
    
        // Push: add the push module too:
        api("io.github.mirzemehdi:kmpnotifier-push-firebase:2.0.0")
    }
  10. Install KMPNotifier via Gradle

    main
    1. Add mavenCentral() to your root build.gradle.kts or settings.gradle.
    2. Add the desired module to your shared module's commonMain dependencies. Use api to ensure visibility in the iOS framework.
    3. If using push notifications, apply the com.google.gms.google-services plugin in your androidApp/build.gradle.kts.
    4. For iOS, export the modules in your framework configuration to make them accessible to Swift.
    // 1. Repositories
    repositories {
      mavenCentral()
    }
    
    // 2. Shared Module Dependencies
    sourceSets {
      commonMain.dependencies {
        // For local notifications only:
        api("io.github.mirzemehdi:kmpnotifier-local:<version>")
        
        // For Firebase push (includes local):
        api("io.github.mirzemehdi:kmpnotifier-push-firebase:<version>")
      }
    }
    
    // 3. iOS Framework Export
    listOf(iosX64(), iosArm64(), iosSimulatorArm64()).forEach {
      it.binaries.framework {
        export("io.github.mirzemehdi:kmpnotifier-core:<version>")
        export("io.github.mirzemehdi:kmpnotifier-local:<version>")
        export("io.github.mirzemehdi:kmpnotifier-push-firebase:<version>")
      }
    }
  11. Manage Push Notifications with Firebase

    main

    Push notifications are supported on Android and iOS via the kmpnotifier-push-firebase module. On desktop and web, this module acts as a no-op mock.

    1. Listen for Push Events

    Register a PushListener using KMPNotifier.addPushListener to handle token updates and incoming messages. Available callbacks include onNewToken, onPushNotification, onPayloadData, and onPushNotificationWithPayloadData.

    2. Platform Integration (Required)

    You must call platform-specific hooks to ensure payloads are delivered correctly:

    • Android: Call KMPNotifier.onCreateOrOnNewIntent(intent) in your Activity's onCreate and onNewIntent.
    • iOS: Call KMPNotifier.onApplicationDidReceiveRemoteNotification(userInfo:) in your app's didReceiveRemoteNotification method.

    3. Token and Topic Management

    All push notifier methods are suspend functions and must be called from a coroutine:

    • getToken(): Retrieves the current push token.
    • deleteMyToken(): Deletes the current token (e.g., on logout).
    • subscribeToTopic(topic): Subscribes to a Firebase topic.
    • unSubscribeFromTopic(topic): Unsubscribes from a Firebase topic.
    // Listening for push events
    KMPNotifier.addPushListener(object : PushListener {
      override fun onNewToken(token: String) {
        println("onNewToken: $token")
      }
    
      override fun onPushNotification(title: String?, body: String?) {
        println("Push received — title: $title, body: $body")
      }
    })
    
    // Token management (inside a coroutine)
    val token = KMPNotifier.firebasePushNotifier.getToken()
    KMPNotifier.firebasePushNotifier.subscribeToTopic("new_users")
  12. Configure iOS Dependencies for KMPNotifier 2.0

    main

    KMPNotifier 2.0 uses Swift Package Manager (SPM) for Firebase instead of the CocoaPods Gradle plugin.

    Required Actions:

    1. Add Firebase via SPM: In your Xcode project, go to FileAdd Package Dependencies and add the firebase-ios-sdk.
    2. Update Deployment Target: The minimum iOS deployment target for the library is now 16.0.
    3. Cleanup CocoaPods: If your shared module previously used the CocoaPods plugin specifically for KMPNotifier, you can now remove the pod("FirebaseMessaging") declaration from your build.gradle.kts.