didReceiveRemoteNotification 或 didFinishLaunchingWithOptions 不会在 APN 后在后台调用

didReceiveRemoteNotification or didFinishLaunchingWithOptions do not get called in background after APN

我正在发送以下 APN

{"aps":{"alert":{"body":"Hello sir","action-loc-key":"Caption of the second Button"},"badge" :1,"sound":"default","content-available":1},"Key1":"Value1","Key2":"Value2"}

如您所见,我将 "content-available":1 设置为负载的一部分

当我的应用程序 运行 在前台时,一切正常。但是当我的应用程序在后台 运行 并且 APN 通知出现时。通知在屏幕上正确显示。当我单击该图标时,它不会调用 didReceiveRemoteNotification 或 didFinishLaunchingWithOptions

我不知道我错过了什么。

我的代码如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
    UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
    navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
    splitViewController.delegate = self;


    // Register for Remote Push Notification 
    if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
    {
        UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
        UIUserNotificationSettings *mySetings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:mySetings];
        [application registerForRemoteNotifications];
        NSLog(@"didFinishLaunchingWithOptions called");
    }



    //Accept push Notification when app is not open
    NSLog(@"Accept push Notification when app is not open");
    NSDictionary *remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if (remoteNotif)
    {
        NSLog(@"Accept push Notification when app is not open if stat ");
        [self processRemoteNotificationApplicationStateActive:remoteNotif];
    }
    return YES;
}




-(void) sendDeviceToken:(NSString *) DeviceToken
{
    NSLog(@"sendDeviceToken called");

}


-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSString *deviceTokenString = [[[NSString stringWithFormat:@"%@", deviceToken]      //convert NSData to NSString with stringWithFormat
                                    stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]stringByReplacingOccurrencesOfString:@" " withString:@""]; // trim the "<>" then remove the spaces
    NSLog(@"deviceTokenString : %@", deviceTokenString);
    [self sendDeviceToken:deviceTokenString];
    NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken called");
}

-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    NSLog(@"Error in registration. Error: %@", error);
}


- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
     NSLog(@"didReceiveRemoteNotification called");

    if ( application.applicationState == UIApplicationStateActive)
    {
        //App is already in the foreground
        NSLog(@"App is already in the foreground");
        [self processRemoteNotificationApplicationStateActive:userInfo];
    }
    else
    {
        NSLog(@"App was just brought from background to foreground");
        //App was just brought from background to foreground
        [self processRemoteNotificationApplicationStateActive:userInfo];
    }

}

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
     NSLog(@"didReceiveRemoteNotification fetchCompletionHandler called");

    if ( application.applicationState == UIApplicationStateActive)
    {
        //App is already in the foreground
        NSLog(@"App is already in the foreground");
        [self processRemoteNotificationApplicationStateActive:userInfo];
    }
    else
    {
        NSLog(@"App was just brought from background to foreground");
        //App was just brought from background to foreground
        [self processRemoteNotificationApplicationStateActive:userInfo];
    }

    completionHandler(UIBackgroundFetchResultNewData);
}



-(void)processRemoteNotificationApplicationStateActive:(NSDictionary *)userInfo
{


    [self.myDetailViewController performAPNUpdate];

}



- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    NSLog(@"applicationWillResignActive called");

}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    NSLog(@"applicationDidEnterBackground called");
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
     NSLog(@"applicationWillEnterForeground called");
}

- (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.
    NSLog(@"applicationDidBecomeActive called");
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
     NSLog(@"applicationWillTerminate called");
}

#pragma mark - Split view

- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
    if ([secondaryViewController isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]] && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {
        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;
    } else {
        return NO;
    }
}

当您通过点击应用程序图标打开应用程序时,您不会获得有关推送通知的任何信息。在这种情况下,Apple 希望您与自己的服务器同步。

因此永远不会调用 didReceiveRemoteNotification。如果您的应用终止,将调用 didFinishLaunchingWithOptions 但您不会获得有关推送通知的任何信息。

这种行为对我个人来说没有多大意义,但为了解决这个问题,我们创建了一个每当应用程序打开时通知同步的网络服务。

当您的应用程序在后台 运行 并且收到带有 content-available 的推送时,将立即调用回调方法 application:didReceiveRemoteNotification:fetchCompletionHandler:

此时当您点击提醒并打开应用时,不会调用其他回调方法。这是预期的行为,因为 application:didReceiveRemoteNotification:fetchCompletionHandler: 已被调用。

如果您的日志根本不显示 application:didReceiveRemoteNotification:fetchCompletionHandler:,那将很奇怪。

此外,确保 UIBackgroundModes 包括 remote-notification 用于 静默推送