Skip to content

fix(messaging, ios): emit onMessage for foreground pushes with content-available: 0 - #9229

Open
lnuvy wants to merge 1 commit into
invertase:mainfrom
lnuvy:fix/messaging-ios-content-available-zero
Open

fix(messaging, ios): emit onMessage for foreground pushes with content-available: 0#9229
lnuvy wants to merge 1 commit into
invertase:mainfrom
lnuvy:fix/messaging-ios-content-available-zero

Conversation

@lnuvy

@lnuvy lnuvy commented Aug 27, 2026

Copy link
Copy Markdown

Description

On iOS, onMessage is never emitted for a foreground push whose payload sets
aps.content-available: 0. willPresentNotification guards emission on key presence
(!notificationDict[@"contentAvailable"]), but the serializer stores @(NO) for 0, which is
non-nil — so the event is skipped as if it were a silent push. The comment above the guard
describes the intent in terms of the value ("if contentAvailable is true").

Compare the value instead:

-    if (!notificationDict[@"contentAvailable"]) {
+    if (![notificationDict[@"contentAvailable"] boolValue]) {

Genuine silent pushes (content-available: 1) are still skipped, preserving de-duplication
with application:didReceiveRemoteNotification:.

Full analysis, reproduction payloads and history in #9228.

Related issues

Fixes #9228

Release Summary

iOS: onMessage is now emitted for foreground pushes that explicitly set content-available: 0.

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
    • Yes
  • My change supports the following platforms;
    • Android
    • iOS
    • Other (macOS, web)
  • My change includes tests;
    • e2e tests added or updated in packages/**/e2e
    • jest tests added or updated in packages/**/__tests__
  • I have updated TypeScript types that are affected by my change.
  • This is a breaking change;
    • Yes
    • No

Test Plan

Manual on a physical iOS 26.5.1 device (@react-native-firebase/messaging 19.3.0), native
delegate streamed via xcrun devicectl device process launch --console.

  • Before: content-available: 0 payload reaches willPresentNotification, no
    messaging_message_received emitted, JS onMessage never called. Same payload without the
    key fires normally.
  • After: the same payload fires onMessage; serialized message carries "contentAvailable": false.

No ObjC test harness exists for this delegate path; happy to add a payload-variant e2e case if
maintainers point at the preferred shape.

@CLAassistant

CLAassistant commented Aug 27, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@russellwheatley russellwheatley left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed against main: the serializer sets contentAvailable to @(NO) whenever content-available is present at all, and the emit guard was checking key presence instead of value, so content-available: 0 got treated as a silent push and swallowed. The comment above the guard already states the intent in terms of the value, so this is a straightforward mismatch, not deliberate behavior.

Also checked the nil case: when content-available is absent, notificationDict[@"contentAvailable"] is nil, and [nil boolValue] returns NO per Objective-C's nil-messaging semantics (verified this by compiling and running it standalone). So onMessage still fires for pushes without the key, same as before. No crash risk there.

We don't have an XCTest harness for this delegate path yet, so this is verified by code review plus your manual device testing rather than an automated test. Given how small and low-risk the change is, that's enough for me here.

Approving, want to get this into the next release.

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 68.49%. Comparing base (e5feb9e) to head (938c81d).

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #9229      +/-   ##
============================================
+ Coverage     68.48%   68.49%   +0.01%     
  Complexity     1914     1914              
============================================
  Files           522      522              
  Lines         38116    38116              
  Branches       5189     5189              
============================================
+ Hits          26100    26102       +2     
+ Misses        10226    10225       -1     
+ Partials       1790     1789       -1     
Flag Coverage Δ
android-native 63.75% <ø> (ø)
e2e-ts-android 53.94% <ø> (ø)
e2e-ts-ios 53.50% <ø> (ø)
e2e-ts-macos 49.66% <ø> (ø)
ios-native 68.78% <0.00%> (+0.01%) ⬆️
ios-ruby 100.00% <ø> (ø)
jest 46.27% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lnuvy

lnuvy commented Aug 28, 2026

Copy link
Copy Markdown
Author

@russellwheatley
Thanks for the quick review !
I've signed the CLA, so that check is green now.
The PR still shows as blocked. From the outside it looks like it might need a second approval, but I can't see the branch protection rules so I may be misreading it. Is there anything else I can do on my end?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🐛] iOS: onMessage never fires in foreground when APNs payload has content-available: 0

3 participants