How to handle multiple remote notifications in iOS

I try to open different view controllers based on notification click, but when I receive push notification it automatically runs the controller code in the background, so when I open the application through the application icon / notification from the notification centre , it loads the view controller immediately, but when several of notifications, it loads the first notification controller, regardless of which of the notifications was listened to.

Say I have a notification with the headings “Evening”, “Morning” and “Night”, it should open “Evening View Controller” when “Evening”, the notification is listened, but it loads the “Night View Controller” when I go back loads the “Morning View Controller” and finally loads the “Evening” View Controller. Before the notification, I was on the Main Controller and then I moved the application to the background.

When I click the "Morning" notification , it does nothing now.

I used to try using addObserver, but the result is the same, so I move on to the application delegate.

Here is the application delegation code

 @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[UIApplication sharedApplication]setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; // Override point for customization after application launch. // Register for Push Notitications, if running iOS 8 or More if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) { UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound); UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes categories:nil]; [application registerUserNotificationSettings:settings]; [application registerForRemoteNotifications]; } else { // Register for Push Notifications before iOS 8 [application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)]; } [application setStatusBarHidden:YES]; return YES; } // Handle remote notification registration. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSLog(@"Device Token %@",[self stringWithDeviceToken:deviceToken]); [[NSUserDefaults standardUserDefaults]setObject:[self stringWithDeviceToken:deviceToken] forKey:@"registration_id"]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { NSLog(@"Error in registration. Error: %@", err); } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSDictionary *alertInfo = (NSDictionary *) userInfo[@"aps"][@"alert"]; NSLog(@"%@",alertInfo); NSString *alertID = [userInfo valueForKey:@"alertid"]; if (application.applicationState == UIApplicationStateBackground) { [UIApplication sharedApplication].applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber + 1; NSLog(@"Background Mode"); NSString *title = alertInfo[@"title"]; [self openViewController:title]; } if(application.applicationState == UIApplicationStateActive){ NSLog(@"Active Mode"); } completionHandler(UIBackgroundFetchResultNewData); [self performSelector:@selector(sendAckRequest:) withObject:alertID]; } - (void)openViewController:(NSString *)notificationTitle{ NSDictionary * userDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"loginUser"]; if([notificationTitle isEqualToString:@"Exercise"]){ UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController; UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil]; Excercise *excerciseController = (Excercise*)[mainStoryboard instantiateViewControllerWithIdentifier: @"Excercise"]; [navigationController pushViewController:excerciseController animated:YES]; //[navigationController presentViewController:excerciseController animated:YES completion:nil]; }else if([notificationTitle isEqualToString:@"Weight"]){ UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController; UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil]; Weight *weightController = (Weight*)[mainStoryboard instantiateViewControllerWithIdentifier: @"Weight"]; [navigationController pushViewController:weightController animated:YES]; //[navigationController presentViewController:weightController animated:YES completion:nil]; }else if([notificationTitle isEqualToString:@"MCQ"]){ UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController; UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil]; QuestionOfTheDay *questionController = (QuestionOfTheDay*)[mainStoryboard instantiateViewControllerWithIdentifier: @"QuestionOfTheDay"]; questionController.self.dictUser = userDict; [navigationController pushViewController:questionController animated:YES] } } -(void)sendAckRequest:(NSString *)alertID { AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.requestSerializer = [AFHTTPRequestSerializer serializer]; manager.responseSerializer = [AFHTTPResponseSerializer serializer]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *userID =[[defaults objectForKey:@"loginUser"]objectForKey:@"UserId"]; NSString *serverRegistrationID = [defaults objectForKey:@"server_registration_id"]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init]; [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"]; NSString *currentDateString = [dateFormatter stringFromDate:[NSDate date]]; //NSDate *currentDate = [dateFormatter dateFromString:currentDateString]; //NSDictionary *parameter = @{@"ReminderDateTime":currentDateString}; NSString *url = [NSString stringWithFormat:@"%@%@/%@/%@/?ReminderDateTime=%@",sendNotificationAck,userID,serverRegistrationID,alertID,currentDateString]; NSLog(@"url: %@",url); [manager GET:url parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) { if(operation.response.statusCode == 200) NSLog(@"Notification Acknowledged"); else NSLog(@"Notification failed to acknowledge"); } failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) { NSLog(@"error: %@",[error localizedDescription]); }]; } - (NSString *)stringWithDeviceToken:(NSData *)deviceToken { const char *data = [deviceToken bytes]; NSMutableString *token = [NSMutableString string]; for (int i = 0; i < [deviceToken length]; i++) { [token appendFormat:@"%02.2hhX", data[i]]; } return token; } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. if([UIApplication sharedApplication].applicationIconBadgeNumber!=0) [UIApplication sharedApplication].applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber - 1; } @end 
+5
source share
5 answers

First, your background Remote Notification on, so notifications are processed in the background. Now the error is - whenever a notification arrives, you push the viewController onto the viewController stack, so you see three viewControllers.

Secondly, didReceiveRemoteNotification can be called / processed in different states. StateBackground If the application is in the background, StateInactive if the user clicks a notification from the Notification Center, StateForeground if the application is in the foreground. You add validation, just click viewController in the StateBackground , so you do not see any changes when you click the notification.

+4
source

To do this, check the applicationstate in the didReceiveRemoteNotification method. Here is the code:

 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { //recieved notification if ( application.applicationState == UIApplicationStateActive ){ // app was already in the foreground UIViewController* topViewController = [self.navigationController topViewController]; if ([topViewController isKindOfClass:[HomeVC class]]) { [[NSNotificationCenter defaultCenter] postNotificationName:@"getUpdatedServiceData" object:nil]; } } } 

in HomeVC you need to create notificate getUpdatedServiceData strong> and in this controller push view.

* note -

Use this method to handle incoming remote notifications for your application. Unlike an application: the didReceiveRemoteNotification method: a method that is called only when your application is running in the foreground, the system calls this method when your application is running in the foreground or background. In addition, if you enable remote notifications in the background, the system launches your application (or wakes it from a paused state) and puts it in the background when a notification is received. However, the system does not automatically launch your application, if the user has the power to quit it. In this situation, the user must restart the application or restart the device before the system again tries to start the application automatically.

Thanks.

+1
source

I have not tried this, but I think this method of delegating the application will solve your problem.

application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:

This method is called when your application is activated by selecting an action from the remote notification.

The userInfo dictionary userInfo contains information related to the remote notification.

See Apple documentation for more information.

https://developer.apple.com/library/ios//documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:handleActionWithIdentifier:forRemoteNotificationsewfflerpleplementpleflerplefple

0
source

This problem is inevitable because you are processing the notification in the background, so by the time you try to start the application from the notification center, your application is ready to display the previously received notifications.

So, to fix your problem, you must

  • Check and make a decision for the last or listened notification or ever meaningful to show in - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler or - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions , because depending on the state of the application this delegate is called for notification
  • Verify that viewController is already shown when the application starts. If so, delete them and show the last or listened notifications.

     UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController; UIViewController* topViewController = [navigationController visibleViewController]; if ([topViewController isKindOfClass:[Excercise class]]) { [topViewController dismissViewControllerAnimated:NO completion:nil]; } 

// run the corresponding VC notification after.

0
source

You can use different application states.

  func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { if ( application.applicationState == UIApplicationState.Active) { // App is foreground and notification is recieved, // Show a alert. } else if( application.applicationState == UIApplicationState.Background) { // App is in background and notification is received, // You can fetch required data here don't do anything with UI. } else if( application.applicationState == UIApplicationState.Inactive) { // App came in foreground by used clicking on notification, // Use userinfo for redirecting to specific view controller. } } 
0
source

Source: https://habr.com/ru/post/1243561/


All Articles