5

I am trying to open different view controllers based on notification click but when push notification is received it automatically runs the controller code in background so when I open the app through app icon/notification from notification centre it loads the view controller immediately but when multiple notifications are received it loads first notification controller irrespective of whichever notification was tapped.

Let's say I've got notification with titles "Evening","Morning" and "Night", it should open "Evening View Controller" when "Evening" notification is tapped but it loads "Night View Controller" when I go back it loads "Morning View Controller" and at last it loads "Evening View Controller". Before notification came, I was on Main Controller and then I moved the app into background.

When I tap on "Morning" notification it doesn't do anything now.

Earlier I tried using addObserver but result is the same so I move to app delegate.

Here is the app delegate 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
4

5 回答 5

4

First, your Remote Notification background mode is enabled, so the notifications get handled in the background. Now the bug is, whenever a notification comes, you push a viewController to the viewController stack, that's why you see three viewControllers stacked.

Second, didReceiveRemoteNotification can be called/handled in different states. StateBackground if the app is in background, StateInactive if user tap the notification from Notification Center, StateForeground if the app is in foreground. You add a check only push the viewController in StateBackground, that's why you don't see any change when tap the notification.

于 2016-03-01T18:56:16.930 回答
1

For this it is needed to check applicationstate in didReceiveRemoteNotification method . Here is 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 and within this method push view controller .

*note -

Use this method to process incoming remote notifications for your app. Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running in the foreground, the system calls this method when your app is running in the foreground or background. In addition, if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a push notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.

Thanks.

于 2016-02-23T08:06:15.547 回答
0

I have not tried this but I guess this app delegate method will solve your problem.

application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:

This method is called when your app has been activated by selecting an action from a remote notification.

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

For more information see apple documentation

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

于 2016-02-29T08:37:57.937 回答
0

This issue is bound to happen as you are handle notification in background, so by the time you try to launch your app from notification centre your app is already prepared to show previously received notifications.

So to fix your issue you should

  1. Check and decide for latest or tapped notification or which ever make sense to show in - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler or - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions as depending on app state these delegate get called for notification
  2. Check for already shown viewController on app launch. If yes remove them and show latest or tapped notification.

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

// launch relevant notification VC after.

于 2016-03-01T10:14:47.867 回答
0

You can use different app 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.
        }

    }        
于 2016-03-02T14:51:22.683 回答