didReceiveRemoteNotification в фоновом режиме

Подобные вопросы задавались несколько раз, но у меня возникла конкретная ситуация.

Когда мое приложение активно и я получаю PUSH-сообщение, я успешно могу анализировать пользовательские полезные нагрузки и тому подобное.

Однако, когда мое приложение находится в фоновом режиме и поступает PUSH, пользователь должен нажать кнопку «Просмотр/Открыть», чтобы вызвать didReceiveRemoteNotification, а после этого вызывается didFinishLaunchingWithOptions.

Мне нужно, чтобы мое приложение решало, должны ли они запрашивать пользователя с помощью UIAlert в фоновом режиме или подавлять push-сообщение на основе некоторых локальных настроек.

Любая помощь будет оценена по достоинству,


person Erwin    schedule 20.02.2011    source источник


Ответы (6)


Ваше приложение должно обрабатывать все возможные состояния доставки push-уведомлений:

  • Ваше приложение только что запущено

  • Ваше приложение только что было переведено с фона на передний план

  • Ваше приложение уже работало на переднем плане

Вы не можете выбрать во время доставки, какой метод презентации используется для представления push-уведомления, который закодирован в самом уведомлении (дополнительное предупреждение, номер значка, звук). Но поскольку вы, по-видимому, контролируете как приложение, так и полезную нагрузку push-уведомления, вы можете указать в полезной нагрузке, было ли представление предупреждения и сообщение уже представлено пользователю. Только в случае, если приложение уже запущено на переднем плане, вы знаете, что пользователь не просто запускал ваше приложение через предупреждение или регулярно с главного экрана.

Вы можете определить, было ли ваше приложение только что переведено на передний план или нет, в didReceiveRemoteNotification с помощью этого фрагмента кода:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    if ( application.applicationState == UIApplicationStateActive )
        // app was already in the foreground
    else
        // app was just brought from background to foreground
    ...
}
person Bogatyr    schedule 20.02.2011
comment
Нет проверки с приложением при работе в фоновом режиме, доступные случаи перечислены в моем ответе (и ответе diwup, где пользователь нажимает «Отмена»). - person Bogatyr; 20.02.2011
comment
Если ваше приложение находится в bg, как только оно придет, вы будете замечены системой. Если вы проигнорируете уведомление и перенесете свое приложение в fg позже (поскольку значок все еще там), у вас не будет никаких изменений для получения pn. Я имею в виду: нет вызова didReceiveRemoteNotification, пока приложение находится в bg - person decades; 17.04.2012
comment
@neil: ваше использование вас неоднозначно, вам нужно различать действия: пользователя, системы и приложения. Если приложение находится в фоновом режиме или не запущено, а пользователь нажимает «Отмена», уведомление в приложение не доставляется. Если пользователь нажимает на просмотр, возможны два случая: приложение в фоновом режиме, приложение не запущено. Если приложение находится на переднем плане, это составляет 3-й случай доставки, таким образом, 3 случая доставки в моем ответе. Очевидно, что если приложение находится в фоновом режиме, а пользователь нажимает отмену, ничего не доставляется и обрабатывать нечего. - person Bogatyr; 17.04.2012
comment
В iOS 7 с фоновым режимом remote-notification я не думаю, что этот метод больше работает, потому что приложение может обрабатывать удаленные уведомления в фоновом режиме, не выводя их на передний план. Так как же тогда узнать, перенесено ли оно с заднего плана на передний план? - person user102008; 26.10.2013
comment
с новыми версиями iOS неудивительно, что все меняется. - person Bogatyr; 27.10.2013

Вы должны сделать несколько вещей, чтобы управлять полученным push-уведомлением, когда приложение находится в фоновом режиме.

Во-первых, на стороне вашего сервера вы должны установить {"aps":{"content-available" : 1... / $body['aps']['content-available'] =1; в полезной нагрузке push-уведомления.

Во-вторых, в вашем проекте Xcode вам нужно настроить «удаленные уведомления». Это делается путем перехода к цели проекта -> возможности, затем включите переключатель возможностей и установите флажок удаленных уведомлений.

В-третьих, вместо использования didReceiveRemoteNotification вы должны вызывать application:didReceiveRemoteNotification:fetchCompletionHandler:, это позволит вам выполнять нужные вам задачи в фоновом режиме, в момент получения уведомления:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 
{

 if(application.applicationState == UIApplicationStateInactive) {

     NSLog(@"Inactive - the user has tapped in the notification when app was closed or in background");
     //do some tasks
    [self manageRemoteNotification:userInfo];
     completionHandler(UIBackgroundFetchResultNewData);
 }
 else if (application.applicationState == UIApplicationStateBackground) {

     NSLog(@"application Background - notification has arrived when app was in background");
     NSString* contentAvailable = [NSString stringWithFormat:@"%@", [[userInfo valueForKey:@"aps"] valueForKey:@"content-available"]];

     if([contentAvailable isEqualToString:@"1"]) {
         // do tasks
         [self manageRemoteNotification:userInfo];
         NSLog(@"content-available is equal to 1");
         completionHandler(UIBackgroundFetchResultNewData);
     }
 }
 else {
     NSLog(@"application Active - notication has arrived while app was opened");
        //Show an in-app banner
         //do tasks
        [self manageRemoteNotification:userInfo];
         completionHandler(UIBackgroundFetchResultNewData);
     }
 }

Наконец, вы должны добавить этот тип уведомления: UIRemoteNotificationTypeNewsstandContentAvailability в настройки уведомлений, когда вы его устанавливаете.

Кроме того, если ваше приложение было закрыто, когда пришло уведомление, вы должны управлять этим в didFinishLaunchingWithOptions , и только если пользователь нажимает на push-уведомление: способ сделать это:

if (launchOptions != nil)
{
    NSDictionary *dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

    if (dictionary != nil)
    {
        NSLog(@"Launched from push notification: %@", dictionary);
        [self manageRemoteNotification:dictionary];
    }
}

launchOptions имеет значение != nil, когда вы запускаете приложение, нажимая на push-уведомление, если вы получаете к нему доступ, нажимая на значок, launchOptions будет == nil.

Я надеюсь, что это будет полезно. Здесь это объясняется Apple.

person AlbertoC    schedule 15.01.2016

Передайте content-available = 1 со своей полезной нагрузкой и вызовите didReceiveRemoteNotification даже в фоновом режиме. например

{
    "alert" : "",
    "badge" : "0",
    "content-available" : "1",
    "sound" : ""
}
person Sandeep Kumar    schedule 30.12.2014
comment
Нет, это не так. Я тестировал это несколько раз, доступный контент используется с фоновой выборкой, и он не на 100% надежен, потому что iOS решает, когда начинать фоновую выборку. - person KarimIhab; 24.03.2015
comment
Доступное содержимое не связано с фоновой загрузкой. Это для push-уведомлений, которые запускают приложение в фоновом режиме. - person Collin; 06.11.2015
comment
на самом деле доступный контент не имеет значения - person Rafael Ruiz Muñoz; 09.10.2016

Следует иметь в виду одну вещь: когда ваше push-сообщение поступает на iPhone пользователя и он нажимает «отмена», за исключением номера значка значка (о нем позаботится ОС), у вас не будет возможности -фоновое приложение, чтобы знать об этом push-событии и предпринимать дальнейшие действия.

person Di Wu    schedule 20.02.2011
comment
Напишите Apple и попросите их предоставить API для поддержки того, что вы хотите :-) - person Di Wu; 20.02.2011

Предупреждение

Я думаю, что логика вашего приложения основывается на пользовательских данных в вашем push-уведомлении. Это не то, для чего предназначены push-уведомления. В качестве альтернативы вы должны сделать didbecomeactive в своем приложении, это просто запросить у вашего сервера данные, которые вам нужны и которые все равно отправляются в качестве полезной нагрузки, и полагаться на них, а не на вашу полезную нагрузку.

Поскольку в документации также указано, что это является наилучшей практикой. Потому что Apple в любом случае не гарантирует, что ваше push-уведомление будет получено в 100% случаев.

Важно: доставка уведомлений является «максимальным усилием», а не гарантируется. Он не предназначен для доставки данных в ваше приложение, а только для уведомления пользователя о наличии новых доступных данных.

Однако, если вы хотите иметь некоторое представление о том, например, был ли изменен значок, не полагаясь на то, что пользователь открывает приложение, щелкнув значок, вы можете сделать что-то вроде этого:

A. вы добавляете (правильный) номер значка в полезную нагрузку push-уведомления, отправляемого вашим сервером. Например, это может выглядеть так:

{
    "aps" : {
        "alert" : "You got your emails.",
        "badge" : 9
    }
}

B. вы постоянно отслеживаете этот номер значка в своем приложении, например, сохраняя его в NSUserDefaults.

Затем в applicationDidBecomeActive можно сравнить свойство applicationIconBadgeNumber UIApplication с вашим ранее сохраненным количеством значков и посмотреть, было ли оно увеличено или уменьшено, и сделать некоторые обновления на основе этого.

 - (void)applicationDidBecomeActive:(UIApplication *)application
    {

        NSNumber *badgecount = [[NSUserDefaults standardUserDefaults] objectForKey:@"badgecount"];
        if (!badgecount) badgecount = @(0);

        if ([UIApplication sharedApplication].applicationIconBadgeNumber != [badgecount integerValue]) {
            //store the new badge count
            badgecount = [NSNumber numberWithInteger:[UIApplication sharedApplication].applicationIconBadgeNumber];
            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
            [defaults setObject:badgecount forKey:@"badgecount"];
            [defaults synchronize];

            // do some stuff here because it's different
        }

    }
person morksinaanab    schedule 05.08.2014

Что касается недавней iOS — я думаю, 8 — если у вас включены удаленные уведомления в качестве фонового режима, одна хитрость заключается в том, чтобы отслеживать, выходите ли вы на передний план в качестве флага.

@interface AppDelegate ()

@property (assign, atomic, getter=isEnteringForeground) BOOL enteringForeground;

@end

- (void) applicationWillEnterForeground: (UIApplication *) application
{
    self.enteringForeground = YES;
}

- (void) applicationDidBecomeActive: (UIApplication *) application
{
    self.enteringForeground = NO;
}

- (void) application: (UIApplication *) application didReceiveRemoteNotification: (NSDictionary *) userInfo fetchCompletionHandler: (void (^) (UIBackgroundFetchResult)) completionHandler
{
    const BOOL launchedFromBackground = !(application.applicationState == UIApplicationStateActive);
    const BOOL enteringForeground = self.enteringForeground;

    if (launchedFromBackground && enteringForeground) {
        // The user clicked a push while the app was in the BG
    }
}
person mszaro    schedule 06.04.2016
comment
лучше проверьте application.applicationState как описано в другом ответе от @Botatyr - person Bastian; 13.07.2016