iOS 8推送通知操作按钮 - 当app在后台时,handleActionWithIdentifier中的代码并不总是运行

时间:2015-04-25 18:56:08

标签: ios objective-c ios8 apple-push-notifications

我在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将对服务器进行不同的调用。使用上面的代码,我看到按钮操作的行为非常不稳定:

  • 很多时候,当app在后台并且根本没有进行任何服务器调用时,动作处理程序中的代码不会执行;当发生这种情况时,如果我点击我的应用程序图标并启动我的应用程序,处理程序代码将立即在启动应用程序时运行。
  • 偶尔会触发处理程序代码并且一切正常。按下其中一个操作按钮后立即发出服务器请求。
  • 如果我在Xcode中放置断点并逐步执行处理程序代码,则成功率也是100%。我不需要启动我的应用程序,并在按下按钮时执行处理程序代码。

有人可以帮我弄清楚导致这种不稳定行为的原因是什么吗?提前谢谢。

2 个答案:

答案 0 :(得分:12)

我终于找到了原因。事实上,它有时是有效的,有时是不可能的,应该早点给我提示。

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

  

此方法的实现应执行与指定标识符关联的操作,并在完成后立即在completionHandler参数中执行该块。在实施结束时未能执行完成处理程序块将导致您的应用程序被终止。

我在application:handleActionWithIdentifier:forRemoteNotification:completionHandler方法的末尾调用了完成处理程序。但是,我在处理程序代码中向服务器发送请求的事实意味着我的end of 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();
    }
}];

答案 1 :(得分:0)

在iOS8上,您需要致电

[application registerForRemoteNotifications];

来自application:didRegisterUserNotificationSettings:

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