I am adding two action buttons to my push notifications on iOS 8: an Accept
button and a Deny
button. Neither button will open the app, but different server requests will be made depending on which button is pressed. Here's my setup:
+ (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];
}
}
Then, in my delegate method, I am specifying actions to be taken when user pressed one of the action buttons:
- (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();
}
}
The ideal scenario is that the user does not need to launch the app in the whole process; pressing Accept
or Deny
will make different calls to the server. With the code above, I am seeing very unstable behaviors with the button actions:
Could anyone please help me figure out what's causing such unstable behavior? Thanks in advance.
Apple does not offer a way to handle a notification that arrives when your app is closed (i.e. when the user has fully quit the application or the OS had decided to kill it while it is in the background). If this happens, the only way to handle the notification is to wait until it is opened by the user.
You can fix an iPhone that's not getting notifications by restarting it or making sure notifications are turned on. You should also make sure your iPhone is connected to the internet so apps can receive notifications. If all else fails, you should try resetting the iPhone — just make sure to back it up first.
iOS enables you to both disable push notifications entirely, or turn them off for individual apps. To access iOS notifications settings, go into the Settings > Notifications menu. From that list, you can configure each app's notifications settings or disable them.
I have finally figured out the reason. The fact that it sometimes works and sometimes doesn't should have given me the hint much sooner.
According to the Apple documentation of application:handleActionWithIdentifier:forRemoteNotification:completionHandler:
:
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.
I am calling the completion handler at the end of the application:handleActionWithIdentifier:forRemoteNotification:completionHandler
method. However, the fact that I am sending requests to the server in my handler code means that my end of implementation
is not simply at the end of the method; my real end lies within the callback of my requests. The way I code it, completion handler and callback are on two different threads, and when completion handler runs before it reaches callback, it'll fail.
So the solution is to move the completion handler into the callback methods of the request, i.e., the real "end of the implementation". Something like this:
[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();
}
}];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With