【问题标题】:didReceiveRemoteNotification when in background在后台时 didReceiveRemoteNotification
【发布时间】:2011-02-20 11:21:03
【问题描述】:

这类问题已被问过很多次,但我有一些具体情况。

当我的应用程序处于活动状态并收到 PUSH 消息时,我能够成功解析自定义负载等。

但是,当我的应用程序在后台并且 PUSH 到达时,用户必须单击“查看/打开”按钮才能调用 didReceiveRemoteNotification,然后调用 didFinishLaunchingWithOptions

我需要让我的应用程序决定是否必须在后台使用 UIAlert 提示用户,或者根据某些本地设置禁止推送消息。

任何帮助将不胜感激,

【问题讨论】:

    标签: iphone ios4 apple-push-notifications


    【解决方案1】:

    您的应用需要处理所有可能的推送通知传递状态:

    • 您的应用刚刚启动

    • 您的应用刚刚从后台转到前台

    • 您的应用已经在前台运行

    您无法在交付时选择使用何种呈现方法来呈现推送通知,即在通知本身中编码(可选警报、徽章编号、声音)。但是由于您可能同时控制了应用程序和推送通知的有效负载,您可以在有效负载中指定是否有警报视图和消息已经呈现给用户。只有在应用程序已经在前台运行的情况下,您才知道用户不只是通过警报或定期从主屏幕启动您的应用程序。

    您可以使用以下代码在 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
        ...
    }
    

    【讨论】:

    • 没有“在后台运行时检查应用程序”的情况,可用的情况在我的答案中列出(和diwup的答案,用户点击“取消”)。
    • 如果你的应用在bg中,一旦pn来了,你就会被系统注意到。如果您忽略通知并稍后将您的应用程序带到 fg(因为徽章仍然存在),则您无需更改即可检索 pn。我的意思是:当应用程序在 bg 中时,没有调用 didReceiveRemoteNotification
    • @neil:您对“您”的使用是模棱两可的,您需要区分以下操作:用户、系统和应用程序。如果应用程序在后台或未启动并且用户点击“取消”,则不会向应用程序发送通知。如果用户点击“查看”,则有两种情况:应用程序在后台,应用程序未启动。如果应用程序在前台,这构成了第 3 个交付案例,因此在我的回答中是 3 个交付案例。显然,如果应用程序在后台,并且用户点击“取消”,则什么都没有传递,也没有什么可处理的。
    • 在 iOS 7 中,使用 remote-notification 后台模式,我认为这种技术不再适用,因为应用程序可以在后台处理远程通知而不将其带到前台。那么如何判断它是否是从后台带到前台的呢?
    • 随着新的 iOS 版本,事情发生变化也就不足为奇了。
    【解决方案2】:

    当应用程序处于后台时,您必须做几件事才能管理收到的推送通知。

    首先,在您的服务器端,您必须在推送通知负载中设置{"aps":{"content-available" : 1... / $body['aps']['content-available'] =1;

    其次,在你的 Xcode 项目中,你必须适应“远程通知”。它是通过转到项目的目标 -> 能力,然后启用能力开关,并选中远程通知复选框来完成的。

    第三,你必须调用application:didReceiveRemoteNotification:fetchCompletionHandler:,而不是使用didReceiveRemoteNotification,这将允许你在收到通知的那一刻在后台执行你想要的任务:

    - (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 中进行管理,并且如果用户点击推送通知:这样做的方式是:

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

    当您通过点击推送通知启动应用程序时,launchOptions 为 != nil,如果您通过点击图标访问它,launchOptions 将为 == nil。

    我希望它会有用。 Here it is explained by Apple.

    【讨论】:

      【解决方案3】:

      通过你的有效载荷传递content-available = 1,即使在后台也会调用didReceiveRemoteNotification。例如

      {
          "alert" : "",
          "badge" : "0",
          "content-available" : "1",
          "sound" : ""
      }
      

      【讨论】:

      • 不,它没有。我已经测试了不止一次,“内容可用”与后台提取一起使用,它不是 100% 可靠的,因为 iOS 是决定何时开始后台提取的人
      • content-available 与后台提取无关。它用于在后台启动应用程序的推送通知。
      • 其实 content-available 是无关紧要的
      【解决方案4】:

      要记住的一点是,当您的推送消息到达用户的 iPhone 并且他们单击“取消”时,除了图标徽章编号(它们将由操作系统处理),没有办法让您的后台应用了解此推送事件并采取任何进一步的措施。

      【讨论】:

      • 向 Apple 发送电子邮件并要求他们提供 API 以支持您的需求 :-)
      【解决方案5】:

      警告词

      我认为您的应用逻辑是基于推送通知中的自定义数据的行为。这不是推送通知的用途。您应该在应用程序中对didbecomeactive 执行的替代操作只是向您的服务器询问您需要的数据并且无论如何都作为有效负载发送,并依靠它而不是您的有效负载。

      因为the documentation also states 这是最佳实践。因为 Apple 不保证无论如何都能 100% 地收到您的推送通知。

      重要提示:发送通知是“尽力而为”,而不是 保证。它并非旨在向您的应用程序提供数据,仅用于 通知用户有新数据可用。

      但是,如果您想获得一些指示,例如徽章是否已更改,而不依赖于用户通过单击徽章打开应用程序,您可以这样做:

      A.您将(正确的)徽章编号添加到服务器发送的推送通知的有效负载中。例如,它可能看起来像这样:

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

      B.您会在应用中持续跟踪该徽章编号,例如将其存储在 NSUserDefaults 中。

      然后在applicationDidBecomeActive 中可以将UIApplicationapplicationIconBadgeNumber 属性与您之前存储的徽章计数进行比较,看看它是增加还是减少,并在此基础上进行一些更新。

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

      【讨论】:

        【解决方案6】:

        最近的 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
            }
        }
        

        【讨论】:

        • 更好地检查application.applicationState,如@Botatyr 的另一个答案中所述
        猜你喜欢
        • 2015-10-05
        • 2014-12-17
        • 2015-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-13
        相关资源
        最近更新 更多