feat(android): emit tokenReceived event via FCMMessagingService subclass#191
Open
eljass wants to merge 2 commits into
Open
feat(android): emit tokenReceived event via FCMMessagingService subclass#191eljass wants to merge 2 commits into
eljass wants to merge 2 commits into
Conversation
Adds a `tokenReceived` event JS clients can subscribe to for live FCM registration token updates on iOS. Addresses the long-standing request in capacitor-community#177. Implementation: - Plugin.swift `messaging(_:didReceiveRegistrationToken:)` now calls `notifyListeners("tokenReceived", ["token": token])` - Gated on `Messaging.messaging().apnsToken != nil` so the pre-APNs registration token Firebase mints on first launch (which gets immediately replaced) is not emitted to consumers - Deduped against `lastNotifiedToken` so foreground/background transitions do not re-emit the same token JS API: FCM.addListener('tokenReceived', ({ token }) => { ... }); `getToken()` behaviour is unchanged; this PR is purely additive. No new dependencies. The Android side will arrive in a follow-up PR.
Adds the Android side of the `tokenReceived` event introduced for iOS in the previous commit. Closes capacitor-community#177. Implementation: - FCMMessagingService extends @capacitor/push-notifications' MessagingService, calls super first to preserve PushNotifications' registration handling, then forwards onNewToken to FCMPlugin. - AndroidManifest.xml registers FCMMessagingService and uses tools:node="remove" to drop the PushNotifications default service so Android FCM routes MESSAGING_EVENT to ours (FCM only routes to a single FirebaseMessagingService). - FCMPlugin static instance + single-slot pending token buffer + dedup in synchronized dispatchTokenReceived(). Tokens minted before the plugin loads are buffered and dispatched once load() runs. @capacitor/push-notifications is added as a peerDependency. The plugin already coexists with PushNotifications in most apps, and Android cannot route FCM messages to two services independently. Subclassing keeps both working. Maintenance contract: MessagingServiceContractTest asserts that @capacitor/push-notifications' MessagingService still exposes onNewToken(String) and still extends FirebaseMessagingService. CI fails if either changes, preventing silent breakage on push-notifications version bumps.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the Android side of the
tokenReceivedevent. Pairs with #190 (iOS) — together they close #177.Implementation:
FCMMessagingServiceextendscom.capacitorjs.plugins.pushnotifications.MessagingService. Callssuper.onNewToken()first (preserving PushNotifications' existing registration handling) then forwards toFCMPlugin.onNewTokenReceived(token), which callsnotifyListeners("tokenReceived", { token }).AndroidManifest.xmlregistersFCMMessagingServiceand usestools:node="remove"to drop@capacitor/push-notifications' default service entry. Android FCM only routesMESSAGING_EVENTto a singleFirebaseMessagingService, so we cannot simply add ours alongside.FCMPluginholds a staticinstance+ single-slotpendingTokenbuffer so cold-start tokens (those emitted before the plugin'sload()runs) are not lost. Dispatch issynchronizedand dedupes againstlastNotifiedToken.JS API (same as iOS, registered in
definitions.ts):Why
Issue #177 has been open since May 2025 asking for a way to monitor token refresh without polling
getToken(). On Android specifically, this event is the only signal for asynchronous token rotations after initial registration.Dependency:
@capacitor/push-notificationsas peerAdded as a
peerDependency(not a hard dependency) at>=8.0.0. Rationale:FirebaseMessagingServiceper APK; subclassing PushNotifications' service keeps both plugins functional simultaneously.@capacitor/push-notificationstoday must add it. Open to discussing alternative architectures (e.g. extendFirebaseMessagingServicedirectly and lose PushNotifications interop) if the maintainers prefer.Maintenance contract
MessagingServiceContractTest(inandroid/src/test/) asserts that@capacitor/push-notifications'MessagingServicestill exposesonNewToken(String)and still extendsFirebaseMessagingService. If either changes in a future PushNotifications release,./gradlew testfails — preventing the silent regression that the manifest comment warns about.Test plan
cd android && ./gradlew test—MessagingServiceContractTestpasses against@capacitor/push-notifications8.x.refreshToken(), and is properly deduped on subsequent re-emissions from FCM../gradlew :app:processDebugManifest— onlyFCMMessagingServiceis registered post-merge.Notes
getToken(),refreshToken(),subscribeTo,unsubscribeFrom, etc. are unchanged. Purely additive.dependencieschange — onlypeerDependencies.Stacking
This PR includes the iOS commit from #190 since both share
definitions.ts(theaddListener('tokenReceived', ...)overload andTokenReceivedEventtype). When #190 merges, this PR's diff narrows to Android-only. If you'd prefer I rebase offmasterand duplicate the TS changes here, happy to do that — let me know.