iOS 8 个推送通知操作按钮 - 当应用程序处于后台时,handleActionWithIdentifier 中的代码并不总是 运行

iOS 8 push notification action buttons - code in handleActionWithIdentifier does not always run when app is in background

我在 iOS 8 上的推送通知中添加了两个操作按钮:一个 Accept 按钮和一个 Deny 按钮。这两个按钮都不会打开应用程序,但会根据按下的按钮发出不同的服务器请求。这是我的设置:

+ (void)requestForPushNotificationToken {
    UIApplication *application = [UIApplication sharedApplication];
    // if ios 8 or greater
    if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
        UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];
        [acceptAction setActivationMode:UIUserNotificationActivationModeBackground];
        [acceptAction setTitle:@"Accept"];
        [acceptAction setIdentifier:@"ACCEPT_ACTION"];
        [acceptAction setDestructive:NO];
        [acceptAction setAuthenticationRequired:NO];

        UIMutableUserNotificationAction *denyAction = [[UIMutableUserNotificationAction alloc] init];
        [denyAction setActivationMode:UIUserNotificationActivationModeBackground];
        [denyAction setTitle:@"Deny"];
        [denyAction setIdentifier:@"DENY_ACTION"];
        [denyAction setDestructive:NO];
        [denyAction setAuthenticationRequired:NO];

        UIMutableUserNotificationCategory *actionCategory = [[UIMutableUserNotificationCategory alloc] init];
        [actionCategory setIdentifier:@"ACTIONABLE"];
        [actionCategory setActions:@[acceptAction, denyAction]
                        forContext:UIUserNotificationActionContextDefault];

        NSSet *categories = [NSSet setWithObject:actionCategory];

        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert) categories:categories];
        [application registerUserNotificationSettings:settings];
    } else if ([application respondsToSelector:@selector(registerForRemoteNotificationTypes:)]) { // ios 7 or lesser
        UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
        [application registerForRemoteNotificationTypes:myTypes];
    }
}

然后,在我的委托方法中,我指定了当用户按下其中一个操作按钮时要执行的操作:

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler {
    if ([identifier isEqualToString:@"ACCEPT_ACTION"]) {
        // Sending a request to the server here
    }
    else if ([identifier isEqualToString:@"DENY_ACTION"]) {
        // Sending a request to the server here
    }

    if (completionHandler) {
        completionHandler();
    }
}

理想的场景是用户全程不需要启动应用;按 AcceptDeny 将对服务器进行不同的调用。使用上面的代码,我看到按钮操作的行为非常不稳定:

谁能帮我弄清楚是什么导致了这种不稳定的行为?提前致谢。

在 iOS8 您需要致电

[application registerForRemoteNotifications];

来自 application:didRegisterUserNotificationSettings::

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    [application registerForRemoteNotifications];
}

终于找到原因了。它有时有效有时无效的事实应该更早地给我提示。

根据 application:handleActionWithIdentifier:forRemoteNotification:completionHandler: 的 Apple 文档:

Your implementation of this method should perform the action associated with the specified identifier and execute the block in the completionHandler parameter as soon as you are done. Failure to execute the completion handler block at the end of your implementation will cause your app to be terminated.

我在 application:handleActionWithIdentifier:forRemoteNotification:completionHandler 方法的末尾调用完成处理程序。但是,我在我的处理程序代码中向服务器发送请求这一事实意味着我的 end of implementation 不只是在方法的末尾;我真正的目的在于我的请求的回调。我的编码方式是,完成处理程序和回调在两个不同的线程上,当完成处理程序在到达回调之前运行时,它将失败。

因此解决方案是将完成处理程序移动到请求的回调方法中,即真正的 "end of the implementation"。像这样:

[MyClient sendRequest:userInfo withSuccessBlock:^(id responseObject){
    NSLog(@"Accept - Success");
    if (completionHandler) {
        completionHandler();
    }
} withFailureBlock:^(NSError *error, NSString *responseString) {
    NSLog(@"Accept - Failure: %@",[error description]);
    if (completionHandler) {
        completionHandler();
    }
}];