Как обрабатывать несколько щелчков удаленных уведомлений в IOS

Я пытаюсь открыть разные контроллеры представлений на основе notification щелчка, но когда push notification получен, он автоматически запускает код контроллера в фоновом режиме, поэтому, когда я открываю приложение через значок приложения / уведомление от notification centre, он сразу загружает view controller, но при получении нескольких уведомлений он загружает первый контроллер уведомлений независимо от того, какое уведомление было задействовано.

Допустим, у меня есть уведомление с заголовками «Вечер», «Утро» и «Ночь», оно должно открывать «Контроллер вечернего обзора» при нажатии на уведомление «Вечер», но загружается «Контроллер ночного видения», когда я возвращаюсь назад, он загружается «Контроллер утреннего просмотра» и, наконец, он загружает «Контроллер вечернего просмотра». До того, как пришло уведомление, я был на главном контроллере, а затем переместил приложение в фоновый режим.

Когда я нажимаю на «Утро» notification, теперь ничего не происходит.

Раньше я пробовал использовать addObserver, но результат тот же, поэтому я перехожу к делегату приложения.

Вот код делегата приложения

@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

person moDev    schedule 22.02.2016    source источник
comment
Вы можете использовать логические значения, чтобы справиться с этим.   -  person Santo    schedule 01.03.2016


Ответы (5)


Во-первых, ваш Remote Notification фоновый режим включен, поэтому уведомления обрабатываются в фоновом режиме. Теперь ошибка в том, что всякий раз, когда приходит уведомление, вы нажимаете viewController в стек viewController, поэтому вы видите три сложенных в стеке viewController.

Во-вторых, didReceiveRemoteNotification можно вызывать / обрабатывать в разных состояниях. StateBackground, если приложение работает в фоновом режиме, StateInactive, если пользователь коснется уведомления в Центре уведомлений, StateForeground, если приложение находится на переднем плане. Вы добавляете проверку, только нажимайте viewController в StateBackground, поэтому вы не видите никаких изменений при нажатии на уведомление.

person dichen    schedule 01.03.2016
comment
Означает ли это, что если я добавлю код навигации в StateInactive, он будет работать? @dichen - person moDev; 01.03.2016
comment
Добавление проверки StateInactive будет обрабатывать случай, когда вы нажимаете на уведомление. Но будьте осторожны, обрабатывая его несколько раз: один раз для фона, а второй - для неактивного. Вам может понадобиться флаг, который проверяет, отображается ли целевой viewController уже или нет. - person dichen; 01.03.2016
comment
Я проверю это, но раньше я использовал StateBackground, и он не работал, когда нажималось уведомление. Я попробую с StateInactive и дам вам знать. Должен ли я использовать код контроллера прямого просмотра или добавить наблюдателя и попробовать? - person moDev; 01.03.2016
comment
Извините, я не слежу за этим, мне использовать код контроллера прямого просмотра или добавить наблюдателя и попробовать? - person dichen; 01.03.2016
comment
Должен ли я использовать контроллер навигации в делегате приложения внутри StateInActive или addObserver вместо контроллера навигации - person moDev; 01.03.2016
comment
Просто сделайте это в приложении Delegate. Я не знаю, каков был код вашего addObserver. Есть обновления? - person dichen; 01.03.2016
comment
Я сделаю это завтра и дам тебе знать - person moDev; 01.03.2016
comment
Похоже, ваша идея работает. Я буду тестировать больше и приму это. Спасибо друг! - person moDev; 02.03.2016

Для этого необходимо проверить состояние приложения в didReceiveRemoteNotification методе. Вот код:

- (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];
        }
    }
}

в HomeVC вам нужно создать уведомление getUpdatedServiceData и внутри этого метода нажать на контроллер представления.

*Примечание -

Используйте этот метод для обработки входящих удаленных уведомлений для вашего приложения. В отличие от метода application: didReceiveRemoteNotification:, который вызывается только тогда, когда ваше приложение работает на переднем плане, система вызывает этот метод, когда ваше приложение работает на переднем плане или в фоновом режиме. Кроме того, если вы включили фоновый режим удаленных уведомлений, система запускает ваше приложение (или выводит его из приостановленного состояния) и переводит его в фоновое состояние при поступлении push-уведомления. Однако система не запускает ваше приложение автоматически, если пользователь принудительно закрывает его. В этой ситуации пользователь должен перезапустить ваше приложение или перезапустить устройство, прежде чем система снова попытается запустить ваше приложение автоматически.

Спасибо.

person Shobhakar Tiwari    schedule 23.02.2016
comment
У нас возникают проблемы с получением push-уведомлений в фоновом режиме. - person moDev; 23.02.2016
comment
На переднем плане отображается UIAlertView, поэтому для нас это не проблема. - person moDev; 23.02.2016

Я не пробовал этого, но думаю, этот метод делегирования приложения решит вашу проблему.

application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:

Этот метод вызывается, когда ваше приложение было активировано путем выбора действия из удаленного уведомления.

Параметр словаря userInfo содержит информацию, относящуюся к удаленному уведомлению.

Для получения дополнительной информации см. Документацию Apple.

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

person Ronald    schedule 29.02.2016
comment
Этот метод доступен начиная с 8.0, наша минимальная версия - 7.0. - person moDev; 29.02.2016

Эта проблема неизбежна, поскольку вы обрабатываете уведомления в фоновом режиме, поэтому к тому времени, когда вы попытаетесь запустить приложение из центра уведомлений, ваше приложение уже будет готово к отображению ранее полученных уведомлений.

Итак, чтобы исправить вашу проблему, вы должны

  1. Проверить и принять решение о последних или нажатых уведомлениях или о том, что когда-либо имеет смысл отображать в - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler или - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions, поскольку в зависимости от состояния приложения этот делегат вызывается для уведомления
  2. Проверяйте уже отображаемый viewController при запуске приложения. Если да, удалите их и покажите последнее или нажатое уведомление.

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

// запускаем соответствующее уведомление ВК после.

person aman.sood    schedule 01.03.2016

Вы можете использовать разные состояния приложения.

    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.
        }

    }        
person Amit Tandel    schedule 02.03.2016