Repository navigation
9.4.0
Fixed
-
Android: Foreground service notifications on Android 12+ (API 31+) were subject to a system-imposed display delay of up to 10 seconds because the library never called
setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE). This is now set by default whenasForegroundService: true, eliminating the delay. Upstream issues: invertase/notifee#272, invertase/notifee#1242. Opt out by settingforegroundServiceBehavior: AndroidForegroundServiceBehavior.DEFERREDon the notification'sandroidconfig. -
Android: ANR-proofed the foreground service STOP path. When a
ForegroundServiceinstance receives a STOP intent (or null intent) withoutstartForeground()having been called on that instance — possible when Android recreates the service after a process kill, or when a cancellation alarm races with the display path — the service now calls a defensivestartForeground()with a minimal placeholder notification beforestopSelf(), preventingForegroundServiceDidNotStartInTimeException(ANR). The placeholder is immediately torn down viastopForeground(STOP_FOREGROUND_REMOVE)and is not visible to the user. -
Android: Reset stale
mCurrentNotification,mCurrentHashCode, andmCurrentForegroundServiceTypeon the foreground service NONE early return path (API 34+, noforegroundServiceTypedeclared in manifest). Previously, these fields retained values from a prior invocation when the same service instance was reused, potentially causing incorrect notification updates or mismatched hash codes on subsequent valid invocations. -
Android: Empty
foregroundServiceTypesarrays that bypass the TypeScript validator (e.g., trigger notifications restored from the Room database after a library upgrade) are now treated as absent at the native layer inNotificationAndroidModel.getForegroundServiceType(), with a diagnostic log message. The TypeScript validator already rejects empty arrays at validation time. -
TypeScript: Numeric enum validators now correctly reject reverse-mapped string keys across all numeric enum types (
AndroidBadgeIconType,AndroidDefaults,AndroidFlags,AndroidGroupAlertBehavior,AndroidImportance,AndroidVisibility,AndroidForegroundServiceType,AndroidForegroundServiceBehavior,RepeatFrequency,AlarmType). Previously, passing a string likeimportance: "HIGH"instead ofimportance: AndroidImportance.HIGHpassed validation but was silently ignored by the nativeBundle.getInt()call, causing the default value to be used. TypeScript consumers were not affected because the type system already rejected this at compile time. JavaScript consumers passing string keys will now receive a clear validation error and should use the numeric enum values instead. -
Tests:
setPlatformtest helper no longer fails silently on repeated calls within a single test block. Addedconfigurable: trueandwritable: trueto theObject.definePropertycalls. -
Android:
ForegroundServicedefensive STOP path now fails fast with a clear error message and documentation URL when the consumer's manifest is missing requiredforegroundServiceTypedeclarations on API 34+, instead of silently ANRing with a cryptic framework stack trace. -
Android:
ForegroundServiceNONE early return path now honors Android's 5-secondstartForeground()contract via the same tracked-boolean defensive pattern as the STOP path, preventing edge-case ANRs during service recreation after process kill. -
Android:
ForegroundService.stop()now logs a warning (viaLogger.w) whenstartService()throwsIllegalStateExceptionand falls back tostopService(), instead of silently swallowing the exception. The fallback behavior is unchanged. -
Android:
getLights()andcancelAllNotificationsWithIds()now preserve exception stack traces in their error logs, making production failures diagnosable in bugreports. -
iOS:
didReceiveNotificationResponse:withCompletionHandler:now calls the completion handler immediately after emitting the PRESS/ACTION_PRESS event, instead of delaying it by 15 seconds viadispatch_after. The delay was a placeholder from 2020 (upstream Invertase) that was never revisited. Apple's contract requires prompt invocation; the 15-second delay could cause iOS to consider the handler lost, block subsequent notification tap deliveries, or trigger process termination if the app was suspended during the wait. -
iOS:
requestPermission:now propagates theNSErrorfromrequestAuthorizationWithOptions:completionHandler:to the JS promise. Previously, the error was silently swallowed (with a TODO comment acknowledging the gap since day 1), andgetNotificationSettings:was called unconditionally — resolving the promise with settings data even when the authorization request failed. Apps under MDM restrictions or system-level notification policies could not detect or report the failure. -
iOS:
displayNotification:andcreateTriggerNotification:now capture and log errors fromcontentByUpdatingWithProvider:error:(communication notification intent) instead of passingnilfor the error parameter. On failure, the notification is displayed with the original content (without communication styling) rather than potentially receiving nil content. This mirrors the error-handling pattern already present in the Notification Service Extension helper (NotifeeCoreExtensionHelper). -
iOS:
getBadgeCount:now calls the completion block with(nil, 0)when running in an app extension, instead of returning without calling the block. This matches the fallthrough pattern used bysetBadgeCount:,incrementBadgeCount:, anddecrementBadgeCount:, and prevents the JS promise from hanging indefinitely ifgetBadgeCount()is called from a Notification Service Extension. -
iOS: Fixed a race condition in
NotifeeCoreDelegateHolder.didReceiveNotifeeCoreEvent:where a notification event could be silently dropped (neither delivered nor buffered) if the weak delegate reference was zeroed (e.g., during JS reload in dev mode) while thedelegateRespondsTobitfield still indicated the delegate was available. The locked path now checks the resolved delegate for nil and falls back to buffering, ensuring events are preserved for the nextsetDelegate:call to flush. -
iOS: Added
@synchronized(self)guards around all access tohasListenersandpendingCoreEventsinNotifeeApiModule.mm. Previously,didReceiveNotifeeCoreEvent:(called from arbitrary UNUserNotificationCenter callback threads) could mutatependingCoreEventsconcurrently withstartObserving(main thread), risking an NSMutableArray concurrent-mutation crash or silent event loss. The synchronization pattern matchesNotifeeCoreDelegateHolder. -
iOS: Replaced placeholder
// update me with logiccomment on the emptymessaging_didReceiveRemoteNotification:handler (NotifeeCore+NSNotificationCenter.m) with documentation explaining that remote notifications are handled viaUNUserNotificationCenterDelegateand the observer registration is preserved intentionally. -
iOS: Notification Service Extension attachment downloads now cap the
NSURLSessionrequest and resource timeouts at 25 seconds, leaving a 5-second margin before iOS's ~30-second NSE budget expires. Previously, the default 60-second timeout could cause the extension process to be killed mid-download byserviceExtensionTimeWillExpirebeforecontentHandlerwas called, resulting in the notification being lost entirely. This is a defense-in-depth fix — consumers should still implement their ownserviceExtensionTimeWillExpireas a safety net, per the README's NSE guidance.
Added
-
Android: New
AndroidForegroundServiceBehaviorenum (DEFAULT,IMMEDIATE,DEFERRED) andforegroundServiceBehaviorproperty onNotificationAndroid. Controls whether foreground service notifications are shown immediately or deferred on Android 12+. Defaults toIMMEDIATEwhenasForegroundService: trueand the property is omitted. -
Android:
android.os.Traceinstrumentation on the foreground service notification path. Custom trace sections (notifee:displayNotification,notifee:buildNotification,notifee:startForegroundService,notifee:ForegroundService.onStartCommand,notifee:startForeground,notifee:InitProvider.onCreate,notifee:warmup) are visible in Perfetto traces for profiling notification display latency. -
Android:
notifee.prewarmForegroundService()opt-in API for manual warmup control. Pre-warms the foreground service notification path by eagerly loading critical classes and warming theINotificationManagerBinder proxy on a background thread. Most apps do not need this — the library handles warmup automatically viaInitProvider. This is an escape hatch for edge cases: lazy-loaded libraries, post-splash-screen warmup, or low-end devices where the automatic warmup hasn't completed by the first notification. Idempotent, safe to call multiple times. Resolves immediately as a no-op on iOS. -
Android: Baseline Profile shipped in the library AAR covering the foreground service notification cold-start path. Instructs ART to AOT-compile notification hot-path methods at install time, eliminating JIT penalty on first invocation (estimated 20-40% reduction in JIT cost on first
displayNotification()call). Fully transparent to consumers — AGP 8.3+ automatically merges the profile at APK build time.
Changed
-
Android:
InitProvidernow pre-loads critical foreground service classes (ForegroundService,NotificationManager,NotificationCompat.Builder, etc.) and pre-warms theINotificationManagerBinder proxy on a low-priority background thread during app startup. This moves ~50–100 ms of ART class loading and verification cost from the firstdisplayNotification()call to app initialization, where it has zero UI impact. Opt out via<meta-data android:name="notifee_init_warmup_enabled" android:value="false" />in your app'sAndroidManifest.xml. -
Android:
androidx.profileinstaller:profileinstaller:1.4.1is now a transitive (api) dependency of the library. Consumers do not need to add it manually, but apps with custom dependency resolution may need to be aware of the new transitive dependency. This enables baseline profile installation on non-Play-Store distributions.
📦 npm: https://www.npmjs.com/package/react-native-notify-kit/v/9.4.0