In this part, we'll explore how to send Firebase Cloud Messaging (FCM) notifications using the Flareon library in a
Kotlin Multiplatform environment.
Overview
The notification system in this project follows the Service pattern with a clean interface-implementation structure:
-
NotificationService: Interface defining the contract -
NotificationServiceImpl: Implementation using Flareon library
This design follows clean code principles by separating the interface from the implementation, making it easy to test
and swap implementations if needed.
Notification Service Interface
File: src/commonMain/kotlin/me/nathanfallet/ktornativeworkertutorial/services/NotificationService.kt
interface NotificationService {
suspend fun sendNotification(token: String, title: String, body: String)
}
This simple interface defines a single suspend function for sending notifications. It accepts:
-
token: The FCM device token -
title: The notification title -
body: The notification body/message
Notification Service Implementation
File: src/commonMain/kotlin/me/nathanfallet/ktornativeworkertutorial/services/NotificationServiceImpl.kt
class NotificationServiceImpl(
serviceAccountPath: String,
) : NotificationService {
private val serviceAccountJson = readFile(serviceAccountPath)
private val credentials = GoogleCredentials.fromJson(serviceAccountJson)
private val messaging = FcmService(credentials)
override suspend fun sendNotification(token: String, title: String, body: String) {
messaging.sendNotification(token, title, body)
}
}
How It Works
-
Service Account Loading:
- The constructor accepts a
serviceAccountPathparameter - Uses the platform-specific
readFile()function to load the JSON file - Creates
GoogleCredentialsfrom the JSON content
- The constructor accepts a
-
FCM Service Initialization:
- Creates an
FcmServiceinstance using the credentials - This service is reused for all notification sending operations
- Creates an
-
Sending Notifications:
- The
sendNotification()method delegates to the Flareon library'sFcmService - The method is marked as
suspend, enabling coroutine-based async operations
- The
Using the Flareon Library
The project uses the Flareon library (
digital.guimauve.flareon:messaging), which provides:
- Kotlin Multiplatform support for FCM
- Native compatibility (works on JVM and Native targets)
- Simple API for sending notifications
- Google service account authentication
Dependency Configuration
In build.gradle.kts, Flareon is included in the common dependencies:
sourceSets {
commonMain.dependencies {
implementation(libs.flareon.messaging)
// ... other dependencies
}
}
And in gradle/libs.versions.toml:
[versions]
flareon = "0.1.1"
[libraries]
flareon-messaging = { module = "digital.guimauve.flareon:messaging", version.ref = "flareon" }
Firebase Service Account Setup
To use FCM, you need a Firebase service account JSON file:
- Go to the Firebase Console
- Select your project
- Navigate to Project Settings > Service Accounts
- Click "Generate New Private Key"
- Save the JSON file (commonly named
firebase-admin-sdk.json) - Place it in your project root or specify its path via environment variable
The service account JSON contains:
- Project ID
- Private key for authentication
- Client email
- Other Firebase project metadata
Platform-Specific File Reading
Notice how the implementation uses readFile(), which is an expect function with platform-specific implementations:
-
JVM: Uses
java.io.File.readText() -
Native: Uses POSIX file operations (
fopen,fread,fclose)
This demonstrates how Kotlin Multiplatform allows you to write common business logic while using platform-specific APIs
when necessary.
Error Handling Considerations
The current implementation is straightforward and doesn't include explicit error handling. In a production environment,
you might want to add:
- Error handling for missing or invalid service account files
- Retry logic for failed notification sends
- Logging for debugging
- Validation of notification parameters
However, this tutorial focuses on the clean, minimal implementation to demonstrate the core concepts.
Integration with Dependency Injection
The NotificationService is registered in the Koin dependency injection container (covered in Part 5), making it
available throughout the application:
single<NotificationService> {
NotificationServiceImpl(
serviceAccountPath = getEnv("SERVICE_ACCOUNT_PATH") ?: "firebase-admin-sdk.json",
)
}
This allows the service account path to be configured via environment variable, with a sensible default fallback.
Summary
The notification service demonstrates:
- Clean separation of interface and implementation
- Use of Kotlin Multiplatform libraries (Flareon)
- Platform-specific code integration (file reading)
- Coroutine-based async operations
- Dependency injection compatibility
In the next part, we'll explore how to set up AMQP message brokering with RabbitMQ to enable asynchronous notification
processing.
Top comments (0)