notifee icon indicating copy to clipboard operation
notifee copied to clipboard

notifee onBackgroundEvent no listen when notification is clicked

Open kabitacode opened this issue 1 year ago • 8 comments

I have a problem using notifee & firebase. When the notification appears in the background (app is closed) when clicked the notification is not handled in onBackgroundEvent.

so the case is if the pressAction is changed to something other than the default, when clicked the log is in onBackgroundEvent, but the problem is the application does not open, while the pressAction is the default, when clicked the log is not handled in onBackgroundEvent the application also opens directly.

only type 3 is the same as DELIVERED which is heard in onBackgroundEvent.

Thank you.

My package.json :

"dependencies": { "@notifee/react-native": "^9.1.2", "@react-native-firebase/app": "^21.3.0", "@react-native-firebase/messaging": "^21.3.0", "react": "18.3.1", "react-native": "0.76.1" }

Screenshot 2024-11-15 at 10 16 29 Screenshot 2024-11-15 at 10 17 17

kabitacode avatar Nov 15 '24 03:11 kabitacode

Same problem here!

In my case, I'm using trigger notifications, local, without firebase, closed application, the notification is shown, but when clicking the App it doesn't open.

lean098 avatar Nov 17 '24 02:11 lean098

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

github-actions[bot] avatar Dec 15 '24 03:12 github-actions[bot]

I am no Android expert, but seems it might be related to PendingIntent changes in Android 14 here or here?

If anybody can give me some pointers where to look, I can have a go at resolving this issue

stefanab avatar Dec 18 '24 12:12 stefanab

There is the same issue with iOS. We receive the notification, but the PRESS function is not being called.

spaceod avatar Jan 04 '25 07:01 spaceod

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

github-actions[bot] avatar Feb 22 '25 00:02 github-actions[bot]

This is still an issue for me. Background events are never triggered on iOS.

Thanaen avatar Feb 23 '25 14:02 Thanaen

Same problem, since @notifee/[email protected] in the older versions all work fine

evd1ser avatar Mar 03 '25 09:03 evd1ser

 * Copyright (c) 2016-present Invertase Limited & Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this library except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#import "NotifeeCore+UNUserNotificationCenter.h"

#import "NotifeeCoreDelegateHolder.h"
#import "NotifeeCoreUtil.h"

@implementation NotifeeCoreUNUserNotificationCenter

struct {
  unsigned int willPresentNotification : 1;
  unsigned int didReceiveNotificationResponse : 1;
  unsigned int openSettingsForNotification : 1;
} originalUNCDelegateRespondsTo;

+ (instancetype)instance {
  static dispatch_once_t once;
  __strong static NotifeeCoreUNUserNotificationCenter *sharedInstance;
  dispatch_once(&once, ^{
    sharedInstance = [[NotifeeCoreUNUserNotificationCenter alloc] init];
    sharedInstance.initialNotification = nil;
    sharedInstance.initialNotificationGathered = false;
    sharedInstance.initialNotificationBlock = nil;
  });
  return sharedInstance;
}

- (void)observe {
  static dispatch_once_t once;
  __weak NotifeeCoreUNUserNotificationCenter *weakSelf = self;
  dispatch_once(&once, ^{
    NotifeeCoreUNUserNotificationCenter *strongSelf = weakSelf;
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    if (center.delegate != nil) {
      _originalDelegate = center.delegate;
      originalUNCDelegateRespondsTo.openSettingsForNotification = (unsigned int)[_originalDelegate
          respondsToSelector:@selector(userNotificationCenter:openSettingsForNotification:)];
      originalUNCDelegateRespondsTo.willPresentNotification = (unsigned int)[_originalDelegate
          respondsToSelector:@selector(userNotificationCenter:
                                      willPresentNotification:withCompletionHandler:)];
      originalUNCDelegateRespondsTo.didReceiveNotificationResponse =
          (unsigned int)[_originalDelegate
              respondsToSelector:@selector(userNotificationCenter:
                                     didReceiveNotificationResponse:withCompletionHandler:)];
    }
    center.delegate = strongSelf;
    NSLog(@"[NOTIFEE] Notification center delegate set to Notifee");
  });
}

- (void)onDidFinishLaunchingNotification:(nonnull NSDictionary *)notifUserInfo {
  NSLog(@"[NOTIFEE] onDidFinishLaunchingNotification: %@", notifUserInfo);
  
  if (notifUserInfo != nil) {
    NSDictionary *notifeeNotification = notifUserInfo[kNotifeeUserInfoNotification];
    _initialNoticationID = notifeeNotification[@"id"];
    NSLog(@"[NOTIFEE] Initial notification ID: %@", _initialNoticationID);
  }

  _initialNotificationGathered = YES;
}

- (nullable NSDictionary *)getInitialNotification {
  NSLog(@"[NOTIFEE] getInitialNotification called, gathered: %@, block: %@", 
        _initialNotificationGathered ? @"YES" : @"NO", 
        _initialNotificationBlock != nil ? @"EXISTS" : @"NIL");
        
  if (_initialNotificationGathered && _initialNotificationBlock != nil) {
    // copying initial notification
    if (_initialNotification != nil &&
        [_initialNoticationID isEqualToString:_notificationOpenedAppID]) {
      NSDictionary *initialNotificationCopy = [_initialNotification copy];
      NSLog(@"[NOTIFEE] Returning initial notification: %@", initialNotificationCopy);
      _initialNotification = nil;
      _initialNotificationBlock(nil, initialNotificationCopy);
    } else {
      NSLog(@"[NOTIFEE] No initial notification to return");
      _initialNotificationBlock(nil, nil);
    }

    _initialNotificationBlock = nil;
  }

  return nil;
}

#pragma mark - UNUserNotificationCenter Delegate Methods

// The method will be called on the delegate only if the application is in the
// foreground. If the the handler is not called in a timely manner then the
// notification will not be presented. The application can choose to have the
// notification presented as a sound, badge, alert and/or in the notification
// list. This decision should be based on whether the information in the
// notification is otherwise visible to the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:
             (void (^)(UNNotificationPresentationOptions options))completionHandler {
  NSLog(@"[NOTIFEE] willPresentNotification: %@", notification.request.content.userInfo);
  
  NSDictionary *notifeeNotification =
      notification.request.content.userInfo[kNotifeeUserInfoNotification];

  // Handle both Notifee and non-Notifee notifications
  if (notifeeNotification != nil) {
    NSLog(@"[NOTIFEE] Processing Notifee notification");
    UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionNone;
    NSDictionary *foregroundPresentationOptions =
        notifeeNotification[@"ios"][@"foregroundPresentationOptions"];

    BOOL alert = [foregroundPresentationOptions[@"alert"] boolValue];
    BOOL badge = [foregroundPresentationOptions[@"badge"] boolValue];
    BOOL sound = [foregroundPresentationOptions[@"sound"] boolValue];
    BOOL banner = [foregroundPresentationOptions[@"banner"] boolValue];
    BOOL list = [foregroundPresentationOptions[@"list"] boolValue];

    if (badge) {
      presentationOptions |= UNNotificationPresentationOptionBadge;
    }

    if (sound) {
      presentationOptions |= UNNotificationPresentationOptionSound;
    }

    // if list or banner is true, ignore alert property
    if (banner || list) {
      if (banner) {
        if (@available(iOS 14, *)) {
          presentationOptions |= UNNotificationPresentationOptionBanner;
        } else {
          // for iOS 13 we need to set alert
          presentationOptions |= UNNotificationPresentationOptionAlert;
        }
      }

      if (list) {
        if (@available(iOS 14, *)) {
          presentationOptions |= UNNotificationPresentationOptionList;
        } else {
          // for iOS 13 we need to set alert
          presentationOptions |= UNNotificationPresentationOptionAlert;
        }
      }
    } else if (alert) {
      // TODO: remove alert once it has been fully removed from the notifee API
      presentationOptions |= UNNotificationPresentationOptionAlert;
    }

    NSDictionary *notifeeTrigger = notification.request.content.userInfo[kNotifeeUserInfoTrigger];
    if (notifeeTrigger != nil) {
      // post DELIVERED event
      [[NotifeeCoreDelegateHolder instance] didReceiveNotifeeCoreEvent:@{
        @"type" : @(NotifeeCoreEventTypeDelivered),
        @"detail" : @{
          @"notification" : notifeeNotification,
        }
      }];
      NSLog(@"[NOTIFEE] Posted DELIVERED event for notification with trigger");
    }

    completionHandler(presentationOptions);
  } else {
    NSLog(@"[NOTIFEE] Processing non-Notifee notification");
    // For non-Notifee notifications, show them with all options
    UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionBadge | 
                                                            UNNotificationPresentationOptionSound;
    
    if (@available(iOS 14, *)) {
      presentationOptions |= UNNotificationPresentationOptionBanner | UNNotificationPresentationOptionList;
    } else {
      presentationOptions |= UNNotificationPresentationOptionAlert;
    }
    
    // Pass to original delegate if available
    if (_originalDelegate != nil && originalUNCDelegateRespondsTo.willPresentNotification) {
      NSLog(@"[NOTIFEE] Passing to original delegate");
      [_originalDelegate userNotificationCenter:center
                        willPresentNotification:notification
                          withCompletionHandler:completionHandler];
    } else {
      NSLog(@"[NOTIFEE] No original delegate, handling ourselves");
      completionHandler(presentationOptions);
    }
  }
}

// The method will be called when the user responded to the notification by
// opening the application, dismissing the notification or choosing a
// UNNotificationAction. The delegate must be set before the application returns
// from application:didFinishLaunchingWithOptions:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
    didReceiveNotificationResponse:(UNNotificationResponse *)response
             withCompletionHandler:(void (^)(void))completionHandler {
  NSLog(@"[NOTIFEE] didReceiveNotificationResponse: %@", response.notification.request.content.userInfo);
  
  NSDictionary *notifeeNotification =
      response.notification.request.content.userInfo[kNotifeeUserInfoNotification];
      
  NSDictionary *notificationData = response.notification.request.content.userInfo;
  
  BOOL isNotifeeNotification = (notifeeNotification != nil);
  NSLog(@"[NOTIFEE] Is Notifee notification: %@", isNotifeeNotification ? @"YES" : @"NO");

  // Keep track of notification ID for both Notifee and non-Notifee notifications
  if (isNotifeeNotification) {
    _notificationOpenedAppID = notifeeNotification[@"id"];
    NSLog(@"[NOTIFEE] Notification opened app ID: %@", _notificationOpenedAppID);
  } else {
    // For non-Notifee notifications, try to create a unique ID
    _notificationOpenedAppID = response.notification.request.identifier;
    NSLog(@"[NOTIFEE] Using request identifier as notification ID: %@", _notificationOpenedAppID);
  }

  // If it's not a Notifee notification but we should handle it as one
  if (!isNotifeeNotification) {
    NSLog(@"[NOTIFEE] Converting non-Notifee notification to Notifee format");
    // First pass to original delegate if available
    if (_originalDelegate != nil && originalUNCDelegateRespondsTo.didReceiveNotificationResponse) {
      NSLog(@"[NOTIFEE] Passing to original delegate first");
      [_originalDelegate userNotificationCenter:center
                 didReceiveNotificationResponse:response
                          withCompletionHandler:^{
        // Continue processing as a Notifee notification after original delegate completes
        [self processAsNotifeeNotification:response withCompletionHandler:completionHandler];
      }];
      return;
    } else {
      // No original delegate, process as Notifee notification directly
      [self processAsNotifeeNotification:response withCompletionHandler:completionHandler];
      return;
    }
  }
  
  // For actual Notifee notifications or when we want to process all as Notifee
  [self processNotifeeNotification:notifeeNotification 
                          response:response 
              withCompletionHandler:completionHandler];
}

// Process any notification as if it were a Notifee notification
- (void)processAsNotifeeNotification:(UNNotificationResponse *)response
               withCompletionHandler:(void (^)(void))completionHandler {
  NSLog(@"[NOTIFEE] Processing as Notifee notification");
  
  // Create a Notifee-compatible notification structure from the original notification
  NSDictionary *userInfo = response.notification.request.content.userInfo;
  NSMutableDictionary *notifeeNotification = [NSMutableDictionary dictionary];
  
  // Generate an ID for the notification
  NSString *notificationId = response.notification.request.identifier;
  notifeeNotification[@"id"] = notificationId;
  
  // Copy notification content
  notifeeNotification[@"title"] = response.notification.request.content.title;
  notifeeNotification[@"body"] = response.notification.request.content.body;
  
  // Copy data payload if available
  if (userInfo[@"data"]) {
    notifeeNotification[@"data"] = userInfo[@"data"];
  } else {
    // Use the entire userInfo as data
    NSMutableDictionary *dataDict = [userInfo mutableCopy];
    [dataDict removeObjectForKey:@"aps"]; // Remove APNs specific content
    notifeeNotification[@"data"] = dataDict;
  }
  
  // Set platform-specific options
  notifeeNotification[@"ios"] = @{
    @"foregroundPresentationOptions": @{
      @"alert": @YES,
      @"badge": @YES,
      @"sound": @YES,
      @"banner": @YES,
      @"list": @YES,
    }
  };
  
  NSLog(@"[NOTIFEE] Created Notifee notification: %@", notifeeNotification);
  
  // Process the constructed Notifee notification
  [self processNotifeeNotification:notifeeNotification 
                          response:response 
              withCompletionHandler:completionHandler];
}

- (void)processNotifeeNotification:(NSDictionary *)notifeeNotification
                         response:(UNNotificationResponse *)response
             withCompletionHandler:(void (^)(void))completionHandler {
  NSLog(@"[NOTIFEE] Processing notification: %@", notifeeNotification);
  
  if ([response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
    // post DISMISSED event, only triggers if notification has a categoryId
    [[NotifeeCoreDelegateHolder instance] didReceiveNotifeeCoreEvent:@{
      @"type" : @(NotifeeCoreEventTypeDismissed),
      @"detail" : @{
        @"notification" : notifeeNotification,
      }
    }];
    NSLog(@"[NOTIFEE] Posted DISMISSED event");
    completionHandler();
    return;
  }

  NSNumber *eventType;
  NSMutableDictionary *event = [NSMutableDictionary dictionary];
  NSMutableDictionary *eventDetail = [NSMutableDictionary dictionary];
  NSMutableDictionary *eventDetailPressAction = [NSMutableDictionary dictionary];

  if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {
    eventType = @1;  // PRESS
    // event.detail.pressAction.id
    eventDetailPressAction[@"id"] = @"default";
    NSLog(@"[NOTIFEE] Detected DEFAULT action press");
  } else {
    eventType = @2;  // ACTION_PRESS
    // event.detail.pressAction.id
    eventDetailPressAction[@"id"] = response.actionIdentifier;
    NSLog(@"[NOTIFEE] Detected CUSTOM action press: %@", response.actionIdentifier);
  }

  if ([response isKindOfClass:UNTextInputNotificationResponse.class]) {
    // event.detail.input
    eventDetail[@"input"] = [(UNTextInputNotificationResponse *)response userText];
    NSLog(@"[NOTIFEE] Detected text input: %@", eventDetail[@"input"]);
  }

  // event.type
  event[@"type"] = eventType;

  // event.detail.notification
  eventDetail[@"notification"] = notifeeNotification;

  // event.detail.pressAction
  eventDetail[@"pressAction"] = eventDetailPressAction;

  // event.detail
  event[@"detail"] = eventDetail;

  // store notification for getInitialNotification
  _initialNotification = [eventDetail copy];
  NSLog(@"[NOTIFEE] Stored notification for getInitialNotification");

  // Set is initial notification flag
  if (_notificationOpenedAppID != nil &&
      [_initialNoticationID isEqualToString:_notificationOpenedAppID]) {
    eventDetail[@"initialNotification"] = @1;
    NSLog(@"[NOTIFEE] Marked as initial notification");
  }

  // post PRESS/ACTION_PRESS event
  [[NotifeeCoreDelegateHolder instance] didReceiveNotifeeCoreEvent:event];
  NSLog(@"[NOTIFEE] Posted %@ event", eventType.intValue == 1 ? @"PRESS" : @"ACTION_PRESS");

  // Complete after a short delay to ensure event is processed
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
                 dispatch_get_main_queue(), ^{
                   NSLog(@"[NOTIFEE] Calling completion handler");
                   completionHandler();
                 });
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
    openSettingsForNotification:(nullable UNNotification *)notification {
  NSLog(@"[NOTIFEE] openSettingsForNotification: %@", notification.request.content.userInfo);
  
  if (_originalDelegate != nil && originalUNCDelegateRespondsTo.openSettingsForNotification) {
    if (@available(iOS 12.0, macOS 10.14, macCatalyst 13.0, *)) {
      [_originalDelegate userNotificationCenter:center openSettingsForNotification:notification];
    } else {
      // Fallback on earlier versions
    }
  }
}

@end

Here is a update to NotifeeCore+UNUserNotificationCenter.m It treats all notifications as Notifee notifications and it fixed my onPress notification issues for iOS you can update the file with patch-package

jquezada19 avatar Mar 20 '25 18:03 jquezada19

Can you send the patch triple quoted or fork the repo and put in. fix?

billnbell3 avatar Apr 03 '25 05:04 billnbell3

this patches what version of Notifee?

billnbell3 avatar Apr 03 '25 05:04 billnbell3

this patches what version of Notifee?

this is the latest version

jquezada19 avatar Apr 16 '25 18:04 jquezada19

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

github-actions[bot] avatar May 15 '25 00:05 github-actions[bot]

Not stale

Thanaen avatar May 15 '25 06:05 Thanaen

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

github-actions[bot] avatar Jun 13 '25 00:06 github-actions[bot]

Not stale

Thanaen avatar Jun 13 '25 06:06 Thanaen

i am getting error when I click notification with onBackgorundEvent :

Error: Failed to call into JavaScript module method AppRegistry.startHeadlessTask(). Module has not been registered as callable. Registered callable JavaScript modules (n = 8): GlobalPerformanceLogger, RCTNativeAppEventEmitter, SamplingProfiler, RCTDeviceEventEmitter, HMRClient, RCTLog, HeapCapture, Systrace. Did you forget to call registerCallableModule?

anyone has experience ? Thank you.

m3hm3tcan avatar Jun 15 '25 10:06 m3hm3tcan

@m3hm3tcan same issue I get notification created by notifee in foreground state, but when I click it in background state I get error. Just get issue with Android, IOS work fine My project is react-native 0.75.5 and notifee 9.1.8/9.1.4 same @mikehardy

Anyone resolve it

phobo99 avatar Jul 01 '25 19:07 phobo99

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

github-actions[bot] avatar Jul 30 '25 00:07 github-actions[bot]

Not stale

Thanaen avatar Jul 30 '25 06:07 Thanaen

@mikehardy Please check this one

Andrija00 avatar Aug 26 '25 07:08 Andrija00

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

github-actions[bot] avatar Sep 24 '25 00:09 github-actions[bot]