DOCS LLMs

RailsFast Native - Notifications

RailsFast Native push is a three-part contract:

  • Rails owns notification events, device storage, and the authenticated token endpoint.
  • iOS owns APNs permission, token registration, and notification-tap routing.
  • Android owns FCM setup, token registration, runtime notification permission, and notification-tap routing.
IMPORTANT

Native push is opt-in. The native shells can advertise and implement the native-push bridge component safely, but a downstream app should only render the Rails bridge tag once the matching server delivery credentials are configured for that platform. In particular, do not let Android register FCM tokens while the Rails server still has placeholder Google/FCM credentials.

The Contract

The web bridge component name is:

native-push

Rails renders a hidden bridge element on authenticated native pages. The web side sends:

{
  "event": "register"
}

Native replies:

{
  "token": "device-token-from-apns-or-fcm",
  "platform": "ios"
}

Android replies with "platform": "android". Rails then POSTs that token from inside the WebView to the app's authenticated token endpoint, usually:

POST /notification_tokens

The POST should ride the existing Rails session cookie and CSRF token. Do not add a parallel mobile auth token just for push registration.

The server stores tokens against the current user. If you use action_push_native, translate the OS names that native sends into the transport names the gem stores:

  • ios -> apple
  • android -> google

Notification payload routing uses two app-defined data keys:

  • path for app-owned Rails paths, such as /notifications or /rides/123
  • url for absolute URLs when a downstream app needs them

url wins over path. Native should route internal URLs in-app and open external URLs through the system browser.

Rails Server

The recommended Rails stack is:

  • noticed for event orchestration and in-app feed rows
  • action_push_native for APNs/FCM delivery and push-device records
  • a small app-owned NotificationTokensController
  • a RailsFast-owned Stimulus bridge controller under app/javascript/controllers/railsfast/native

The token controller is intentionally tiny because the POST originates inside the authenticated WebView:

class NotificationTokensController < ApplicationController
  before_action :authenticate_user!

  PLATFORMS = {
    "ios" => "apple", "apple" => "apple",
    "android" => "google", "google" => "google"
  }.freeze

  def create
    platform = PLATFORMS[params.require(:platform).to_s.downcase]
    head :unprocessable_entity and return if platform.blank?

    device = ApplicationPushDevice.find_or_initialize_by(token: params.require(:token))
    device.owner = current_user
    device.platform = platform
    device.name = params[:name].presence || "Mobile device"
    device.save!
    head :created
  end

  def destroy
    current_user.application_push_devices.where(token: params.require(:token)).destroy_all
    head :no_content
  end
end

Server-side delivery credentials are platform-specific:

  • APNs needs an Apple Developer account, an APNs .p8 key, key ID, team ID, and the app bundle ID as the APNs topic.
  • FCM needs a Firebase project, the Firebase project ID, and a service-account JSON private key for FCM v1 server auth.

Render the native bridge only when the native app advertises native-push. If Android FCM server credentials are missing, either do not render the bridge for Android or reject Android token registration so no google devices are stored until delivery can actually succeed.

iOS

railsfast-ios includes the native side:

  • NativePushComponent answers the register bridge event.
  • PushRegistrationService requests an APNs token and caches it for repeated WebView bridge reconnects.
  • PushNotificationRouter routes path / url payloads after a notification tap.
  • AppDelegate implements APNs registration callbacks and UNUserNotificationCenterDelegate.
  • SceneDelegate consumes notification responses on cold launch and while the app is already running.
  • RailsFast.entitlements declares aps-environment through RAILSFAST_APS_ENVIRONMENT.
  • project.yml declares UIBackgroundModes: remote-notification.

Before a downstream iOS app ships push:

  1. Register the explicit App ID for the production bundle identifier.
  2. Enable Push Notifications on that App ID.
  3. Keep Associated Domains enabled if notification taps route to Universal Link URLs.
  4. Set RAILSFAST_APS_ENVIRONMENT = development for Debug and production for Release.
  5. Configure Rails APNs credentials and set the APNs topic to the bundle identifier.
  6. Regenerate the project with xcodegen generate.
  7. Test on a real device. Simulator behavior is not enough for final APNs verification.

TestFlight and App Store builds use production APNs tokens. Xcode-installed development builds use development APNs tokens unless you explicitly configure the server to talk to APNs development for that environment.

Android

Android push uses Firebase Cloud Messaging. Play Console distribution and Firebase push are separate Google products; a Play account does not replace Firebase.

railsfast-android includes the native side:

  • NativePushComponent answers the web native-push bridge register event.
  • NativePushPermission requests Android 13+ notification permission and resumes the pending token request after the system dialog returns.
  • RailsFastFirebaseMessagingService receives FCM messages, builds foreground notifications, and logs token refreshes.
  • PushNotificationRouter routes path / url payloads into the app or out to the system browser.
  • The Gradle build adds Firebase Messaging through the Firebase BoM and applies the Google Services plugin only when a local google-services.json is present.

Before a downstream Android app ships push:

  1. Create a Firebase project.
  2. Add the Android app using the exact package/application ID.
  3. Download google-services.json.
  4. Put it in app/google-services.json, or in a build-type/flavor-specific location if the app uses separate Firebase projects per environment.
  5. Add Firebase Messaging through the Firebase BoM.
  6. Apply the Google Services Gradle plugin for builds that have a google-services.json.
  7. Add a FirebaseMessagingService in the manifest for com.google.firebase.MESSAGING_EVENT.
  8. Request android.permission.POST_NOTIFICATIONS at runtime on Android 13+ before showing notifications.
  9. Configure Rails FCM credentials before allowing Android tokens to register.

The Android native-push component should get the current FCM token, reply to the WebView bridge with {token, platform: "android"}, and let the WebView POST to Rails. The native service should handle foreground data messages by creating a notification with a PendingIntent back into the app. For background notification messages, FCM can display the notification through the system tray; the tap launches the app and data payload keys are delivered in the launcher intent extras.

Verification

Use this checklist before a release:

  • Rails logs show successful token registration for the signed-in user.
  • The stored device platform is apple for iOS and google for Android.
  • A notification with path: "/notifications" opens the app to /notifications.
  • An internal absolute app URL routes in-app.
  • An external absolute URL opens in the system browser.
  • iOS foreground notifications are presented with alert, sound, and badge.
  • Android foreground data messages produce a notification channel notification.
  • Deleting/signing out removes the current token when the shell supports sign-out cleanup.
  • Invalid/dead tokens self-clean through the push delivery layer or are explicitly removed on provider token errors.

References