Skip to content
This repository has been archived by the owner on Jan 14, 2025. It is now read-only.

iOS - Notifications not showing up when app is in background #69

Closed
hyperh opened this issue May 13, 2016 · 15 comments
Closed

iOS - Notifications not showing up when app is in background #69

hyperh opened this issue May 13, 2016 · 15 comments

Comments

@hyperh
Copy link

hyperh commented May 13, 2016

I can trigger notifications when the app is in the foreground (i.e. they appear in the dropdown notifications area) but they don't seem to appear once the app is in the background.

// AppDelegate.m
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#import "AppDelegate.h"

#import "RCTRootView.h"

#import "RCTPushNotificationManager.h"


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  /**
   * Loading JavaScript code - uncomment the one you want.
   *
   * OPTION 1
   * Load from development server. Start the server from the repository root:
   *
   * $ npm start
   *
   * To run on device, change `localhost` to the IP address of your computer
   * (you can get this by typing `ifconfig` into the terminal and selecting the
   * `inet` value under `en0:`) and make sure your computer and iOS device are
   * on the same Wi-Fi network.
   */

  jsCodeLocation = [NSURL URLWithString:@"http://192.168.1.101:8081/index.ios.bundle?platform=ios&dev=true"];

  /**
   * OPTION 2
   * Load from pre-bundled file on disk. The static bundle is automatically
   * generated by the "Bundle React Native code and images" build step when
   * running the project on an actual device or running the project on the
   * simulator in the "Release" build configuration.
   */

//   jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"olisReactNative"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[RCTPushNotificationManager didRegisterUserNotificationSettings:notificationSettings];
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event.
// https://facebook.github.io/react-native/docs/pushnotificationios.html#content
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
{
NSLog(@"didReceiveRemoteNotification");
[RCTPushNotificationManager didReceiveRemoteNotification:notification];
}

// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RCTPushNotificationManager didReceiveLocalNotification:notification];
}

@end

    PushNotification.localNotification({
      // No idea why, but seems like I need the Android only properties as well to trigger a notification in the dropdown

      /* Android Only Properties */
      id: 0, // (optional) default: Autogenerated Unique ID
      title: "My Notification Title", // (optional)
      ticker: "My Notification Ticker", // (optional)
      largeIcon: "ic_launcher", // (optional) default: "ic_launcher"
      smallIcon: "ic_notification", // (optional) default: "ic_notification" with fallback for "ic_launcher"

      /* iOS and Android properties */
      message: "My Notification Message" // (required)
    });
@yonahforst
Copy link

related to #17.

Have a look at my last comment in the thread. Also, don't forget to add remote-notification to your list of background modes

@sibelius
Copy link

sibelius commented Jul 1, 2016

// fetch notifications in the background and foreground
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification 
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

[RCTPushNotificationManager didReceiveRemoteNotification:notification];
completionHandler(UIBackgroundFetchResultNewData);
NSLog(@"Notification Body %@", notification);

}

based on http://stackoverflow.com/a/36750318/2628278

In order for notifications to hit your app in the background you need to also define a fetchCompletionHandler, with a completion handler function like below. The aps:{content-available:1} payload should wake up you application and trigger this code in your AppDelegate, and in turn hit your JavaScript in RN.

@npomfret
Copy link
Contributor

Is there a way to give my RN app a few seconds to deal with the push notification? It needs to download some data basically and it looks like it's not been given enough time to do it.

@varungupta85
Copy link
Contributor

@npomfret Do you mean some time before the remote notification is shown or some time for it to complete the remote fetch task?

@npomfret
Copy link
Contributor

It just needs time to do some work

@varungupta85
Copy link
Contributor

I think it is possible in iOS by enabling remote fetch for the notifications for which you want to do some work. You will need to set content-available to 1 for such notifications, enable background fetch mode in the capabilities and also provide didReceiveRemoteNotification method in the AppDelegate file. If you search for remote fetch in iOS, I think you will find ample information on what needs to be done. It requires use to call the completion handler in the didReceiveRemoteNotification method when the work is done and since it is a bit tricky to know when the work is done, what you can do is emit an event from the native side which will be caught by the JS side to perform the work and just sleep for a few seconds and then call the completion handler. The maximum amount of time you are given to complete the remote fetch work is 30 seconds so you have to call the completion handler before that time. It is a bit hacky but just wanted to share how I am solving it. For Android, it is a bit tricky. Android always wakes up your app when a remote notification is received if it is running in the background and you can again send an event from the native Java side which will be caught by the JS side to do some work but it doesn't provide any sort of completion handler that you have to call to sleep the app again and does it on its own. In my experience, it has been able to finish up some websocket requests in the background.

@npomfret
Copy link
Contributor

I've done all that stuff you've mentioned. The problem seems to be that the app gets suspended after a while and then killed by the OS. At this point if a push notification arrives (with content-available=1) it will try to launch the app, which it does. But it doesn't seem to give it long enough to get going. My app takes a few seconds to start, but i seems like the OS is only giving it a very short time to start. To make things more complicated I don't know how to get my app to tell my app delegate that it's finished processing the push notification and that it should call the completion handler. For the moment I'm just trying a [NSThread sleep...] in order to give the RN app a few seconds to do some work...

Is there a more sensible way to do this? How do I get my RN app to tell me app delegate that it has processed the push notification?

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  NSLog(@"notification-body => %@", notification);

  if(application.applicationState == UIApplicationStateInactive) {
    NSLog(@"Inactive");
  } else if (application.applicationState == UIApplicationStateBackground) {
    NSLog(@"Background");
  } else {
    NSLog(@"Active");
  }

  [RCTPushNotificationManager didReceiveRemoteNotification:notification];

  if(application.applicationState != UIApplicationStateInactive && application.applicationState == UIApplicationStateBackground) {
    self.bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{
      [application endBackgroundTask:self.bgTask];
      self.bgTask = UIBackgroundTaskInvalid;
    }];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      NSLog(@"%@ starting background task %lu", self, (unsigned long)self.bgTask);

      [NSThread sleepForTimeInterval:15.0f];

      [application endBackgroundTask:self.bgTask];
      self.bgTask = UIBackgroundTaskInvalid;

      NSLog(@"%@ ended background task %lu", self, (unsigned long)self.bgTask);

      completionHandler(UIBackgroundFetchResultNewData);
    });
  } else {
      completionHandler(UIBackgroundFetchResultNoData);
  }
}

@npomfret
Copy link
Contributor

The answer to this will be in react-native 0.38 I think

@danawhite
Copy link

Is it possible to display a local notification while the app is in the background?

@npomfret
Copy link
Contributor

Yes its possible to display a local notification while the app is in the background.

@npomfret
Copy link
Contributor

@hyperh do you still have this issue? if so, have you tried the latest release? if not, can we close it?

@guns2410
Copy link

guns2410 commented Dec 17, 2016

@npomfret I am on RN 0.39.0, however, the app does not wake up if app closed by the user.

On remote notification, this is what I see in the logs of the device.

Notification received

Received message for enabled topic '<app bundle identifier>' with payload '{
    aps =     {
        alert =         {
            body = "<null>";
            title = "Test Heading";
        };
        "content-available" = 1;
    };
    custom =     {
        a =         {
            content = "This is a test Content";
            subTitle = "Test SubTitle123";
            title = "Test Title123";
            type = alert;
        };
        i = "e9bf6a4a-94ef-4d9e-95d8-80870d0db905";
    };
}' onInterface: NonCellular  for device token: NO  with priority 10

The next logs are

High Priority Push: <app bundle identifier> - App killed
Ignoring notification with no content (<app bundle identifier>): BDD8-38AC

The AppDelegate.m has the below code:

// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
  [RCTPushNotificationManager didRegisterUserNotificationSettings:notificationSettings];
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
  [RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
  {
    [RCTPushNotificationManager didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
  }
  // Optionally implement this method over the previous to receive remote notifications. However
  // implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible.
  // If your delegate implements both methods, the app object calls the `application:didReceiveRemoteNotification:fetchCompletionHandler:` method
  // Either this method or `application:didReceiveRemoteNotification:fetchCompletionHandler:` is required in order to receive remote notifications.
  //
  // Required for the registrationError event.
  - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
  {
    [RCTPushNotificationManager didFailToRegisterForRemoteNotificationsWithError:error];
  }
  // Required for the notification event.
  - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
  {
    NSLog(@"push-notification received: %@", notification);
    [RCTPushNotificationManager didReceiveRemoteNotification:notification];
  }
  // Required for the localNotification event.
  - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
  {
    [RCTPushNotificationManager didReceiveLocalNotification:notification];
  }

I am not sure if there is something to do with the completionHandler but I have also tried

  - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  
    [RCTPushNotificationManager didReceiveRemoteNotification:notification];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      completionHandler(UIBackgroundFetchResultNewData);
    });
  }

without any success!

What I am trying to do is receive a silent remote notification, do my stuff and push a local notification.

The app seems to receive the silent notification, however, the RN thread does not get called.

Not a problem with this package, but something to do with the correct configuration of the PushNotifications

Update

I do not receive Remote silent notification when the device is locked or inactive (screen off), but if I wake the device and unlock, I see the messages received in the console app.

Guess, I will have to live with remote noisy notification for now.

P.S. I am using one signal for remote notifications and have also installed react-native-onesignal package.

Here is the gist of how the Notifications are configured within the app

@npomfret
Copy link
Contributor

Yes that's expected behaviour and described in the trouble shooting guide. On iOS the app os will never relaunch the app if the user killed it.

@npomfret
Copy link
Contributor

Can we close this issue?

@hyperh
Copy link
Author

hyperh commented Jan 31, 2017

@npomfret I stopped working on the project which used this library. Feel free to close it if you like.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants