Boost App Engagement with Rails + Hotwire Push Notifications
Learn how to integrate push notifications into your Hotwire Native + Rails app and drive user engagement without the complexity of native code. A step-by-step guide for teams building modern, high-performance mobile apps.
Place it inside your Android module’s app/ directory.
Modify android/app/build.gradle:
Get the hotwire-native-app-firebase-adminsdk-**.json file from the Firebase, by following these steps. Go to Firebase > your project > Service account > Generate new Private key. Once you generate it, add this file to your Rails project in the config folder.
Step 2: Rails FCM Setup
Add the FCM gem to Gemfile and configure:
# Gemfile gem 'fcm'
2. Create the device_token model
2.a. Generate the Model
Run these commands:
rails generate model DeviceToken token:string user:references platform:string rails db:migrate
package com.example.yourapp # import all dependencies classMyFirebaseService : FirebaseMessagingService() { // Called when a new FCM token is generated overridefunonNewToken(token: String) { super.onNewToken(token) sendTokenToBackend(token) } // Sends the FCM token to your Rails backend funsendTokenToBackend(token: String) { val url = "your API url" val mediaType = "application/json".toMediaTypeOrNull() val body = json.toString().toRequestBody(mediaType) val request = Request.Builder() .url(url) .post(body) .addHeader("Content-Type", "application/json") .build() Thread {
val response = OkHttpClient().newCall(request).execute() }.start() } // Called when a push notification is received while app is in foreground overridefunonMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) showNotification(remoteMessage.notification?.title, remoteMessage.notification?.body) } privatefunshowNotification(title: String, message: String) { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = "default_channel" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "Default Channel", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(this, channelId) .setContentTitle(title) .setContentText(message) .setSmallIcon(R.drawable.ic_notification) .build() notificationManager.notify(0, notification) } }
package com.example.hotwirenativeblog # import your packages here
classMainActivity : HotwireActivity() { overridefunonCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // ✅ Step 1: Initialize Firebase FirebaseApp.initializeApp(this) // ✅ Apply IME insets findViewById<View>(R.id.main_nav_host).applyDefaultImeWindowInsets() // ✅ Request location permissions requestLocationPermissions() // ✅ Fetch FCM token manually and send it to backend FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (task.isSuccessful) { val token = task.result MyFirebaseService().sendTokenToBackend(token) }
} privatefunrequestLocationPermissions() { val permissions = arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) val permissionsToRequest = permissions.filter { permission -> ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED } if (permissionsToRequest.isNotEmpty()) { locationPermissionLauncher.launch(permissionsToRequest.toTypedArray()) } else { Log.d("Location", "Location permissions already granted") } } overridefunnavigatorConfigurations() = listOf( NavigatorConfiguration( name = "main", startLocation = "your root path API url", // Your local network IP address navigatorHostId = R.id.main_nav_host ) ) }
3.f Create a notification icon in the res/drawableIc_notification.xml and also add the required permission.
Once you set up this, we are good to go live!
At Humive, we help businesses launch high-performance mobile apps using Rails + Hotwire Native with features like push notifications, real-time updates, and native navigation—all from a single codebase.
👉 Let’s talk about how we can turn your idea into a launch-ready mobile experience. Please contact here