Skip to content

9.4.0

Choose a tag to compare

@marcocrupi marcocrupi released this 10 Apr 14:30
· 442 commits to main since this release
Immutable release. Only release title and notes can be modified.

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 when asForegroundService: true, eliminating the delay. Upstream issues: invertase/notifee#272, invertase/notifee#1242. Opt out by setting foregroundServiceBehavior: AndroidForegroundServiceBehavior.DEFERRED on the notification's android config.

  • Android: ANR-proofed the foreground service STOP path. When a ForegroundService instance receives a STOP intent (or null intent) without startForeground() 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 defensive startForeground() with a minimal placeholder notification before stopSelf(), preventing ForegroundServiceDidNotStartInTimeException (ANR). The placeholder is immediately torn down via stopForeground(STOP_FOREGROUND_REMOVE) and is not visible to the user.

  • Android: Reset stale mCurrentNotification, mCurrentHashCode, and mCurrentForegroundServiceType on the foreground service NONE early return path (API 34+, no foregroundServiceType declared 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 foregroundServiceTypes arrays 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 in NotificationAndroidModel.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 like importance: "HIGH" instead of importance: AndroidImportance.HIGH passed validation but was silently ignored by the native Bundle.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: setPlatform test helper no longer fails silently on repeated calls within a single test block. Added configurable: true and writable: true to the Object.defineProperty calls.

  • Android: ForegroundService defensive STOP path now fails fast with a clear error message and documentation URL when the consumer's manifest is missing required foregroundServiceType declarations on API 34+, instead of silently ANRing with a cryptic framework stack trace.

  • Android: ForegroundService NONE early return path now honors Android's 5-second startForeground() 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 (via Logger.w) when startService() throws IllegalStateException and falls back to stopService(), instead of silently swallowing the exception. The fallback behavior is unchanged.

  • Android: getLights() and cancelAllNotificationsWithIds() 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 via dispatch_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 the NSError from requestAuthorizationWithOptions:completionHandler: to the JS promise. Previously, the error was silently swallowed (with a TODO comment acknowledging the gap since day 1), and getNotificationSettings: 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: and createTriggerNotification: now capture and log errors from contentByUpdatingWithProvider:error: (communication notification intent) instead of passing nil for 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 by setBadgeCount:, incrementBadgeCount:, and decrementBadgeCount:, and prevents the JS promise from hanging indefinitely if getBadgeCount() 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 the delegateRespondsTo bitfield 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 next setDelegate: call to flush.

  • iOS: Added @synchronized(self) guards around all access to hasListeners and pendingCoreEvents in NotifeeApiModule.mm. Previously, didReceiveNotifeeCoreEvent: (called from arbitrary UNUserNotificationCenter callback threads) could mutate pendingCoreEvents concurrently with startObserving (main thread), risking an NSMutableArray concurrent-mutation crash or silent event loss. The synchronization pattern matches NotifeeCoreDelegateHolder.

  • iOS: Replaced placeholder // update me with logic comment on the empty messaging_didReceiveRemoteNotification: handler (NotifeeCore+NSNotificationCenter.m) with documentation explaining that remote notifications are handled via UNUserNotificationCenterDelegate and the observer registration is preserved intentionally.

  • iOS: Notification Service Extension attachment downloads now cap the NSURLSession request 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 by serviceExtensionTimeWillExpire before contentHandler was called, resulting in the notification being lost entirely. This is a defense-in-depth fix — consumers should still implement their own serviceExtensionTimeWillExpire as a safety net, per the README's NSE guidance.

Added

  • Android: New AndroidForegroundServiceBehavior enum (DEFAULT, IMMEDIATE, DEFERRED) and foregroundServiceBehavior property on NotificationAndroid. Controls whether foreground service notifications are shown immediately or deferred on Android 12+. Defaults to IMMEDIATE when asForegroundService: true and the property is omitted.

  • Android: android.os.Trace instrumentation 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 the INotificationManager Binder proxy on a background thread. Most apps do not need this — the library handles warmup automatically via InitProvider. 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: InitProvider now pre-loads critical foreground service classes (ForegroundService, NotificationManager, NotificationCompat.Builder, etc.) and pre-warms the INotificationManager Binder proxy on a low-priority background thread during app startup. This moves ~50–100 ms of ART class loading and verification cost from the first displayNotification() 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's AndroidManifest.xml.

  • Android: androidx.profileinstaller:profileinstaller:1.4.1 is 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