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();
}
}
理想的场景是用户全程不需要启动应用;按 Accept
或 Deny
将对服务器进行不同的调用。使用上面的代码,我看到按钮操作的行为非常不稳定:
- 很多时候,当应用程序处于后台并且根本没有进行服务器调用时,操作处理程序中的代码不会执行;发生这种情况时,如果我点击我的应用程序图标并启动我的应用程序,处理程序代码将在启动应用程序时立即变为 运行。
- 偶尔会触发处理程序代码,但一切正常。只要我按下其中一个操作按钮,就会发出服务器请求。
- 如果我在我的 Xcode 中放置断点并单步执行处理程序代码,成功率也是 100%。我不需要启动我的应用程序,并且在按下按钮时执行处理程序代码。
谁能帮我弄清楚是什么导致了这种不稳定的行为?提前致谢。
在 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();
}
}];
我在 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();
}
}
理想的场景是用户全程不需要启动应用;按 Accept
或 Deny
将对服务器进行不同的调用。使用上面的代码,我看到按钮操作的行为非常不稳定:
- 很多时候,当应用程序处于后台并且根本没有进行服务器调用时,操作处理程序中的代码不会执行;发生这种情况时,如果我点击我的应用程序图标并启动我的应用程序,处理程序代码将在启动应用程序时立即变为 运行。
- 偶尔会触发处理程序代码,但一切正常。只要我按下其中一个操作按钮,就会发出服务器请求。
- 如果我在我的 Xcode 中放置断点并单步执行处理程序代码,成功率也是 100%。我不需要启动我的应用程序,并且在按下按钮时执行处理程序代码。
谁能帮我弄清楚是什么导致了这种不稳定的行为?提前致谢。
在 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();
}
}];