# Adding notification actions

iOS 8 introduced actionnable notifications: action buttons can be attached to notifications. Batch includes support for them: this documentation describes how to make the best of Batch's features and how to integrate them with iOS' native features, including how to work with the new iOS 10 APIs without breaking backwards compatibility.

This guide has no intention of being a full replacement of [Apple's official documentations of notification actions](https://developer.apple.com/reference/usernotifications/unnotificationaction).

![Push example with actions and image](https://38998153-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCL8wF0y1T2vLnm3yR2MW%2Fuploads%2Fe7ArFkIKIldCCxuWJPRW%2Fios_notification_action.jpg?alt=media\&token=1651b905-2d44-4639-ace0-4eaad5fb4574)

### Registering with the framework

In order to be able to use action buttons in your notifications, you will have to register them to the framework in the application itself.

To do so, you'll create **Actions** and group sets of them into **Categories**. **Categories** are what iOS will use to display a set of predefined buttons in local or remote notifications.\
It is important that you figure out what **categories** you will want to associate to your notifications as updating them will require you to submit an updated build to the App Store and have your users update your app before you can push with your new actions.

Actions are defined by:

* An identifier. Your app will be called back with this identifier once the user tapped on the action so you can perform the action.
* A label. It is the text that the user will see.
* Optional behavior options such as:
  * Destructive (the button will appear red)
  * Touch ID/Passcode requirement
  * Foreground/Background action (whether tapping the button will open your app or perform the action in the background)
  * ...

You can delcare these actions using the standard Apple UIKit documentation [iOS8/9](https://developer.apple.com/reference/uikit/uimutableusernotificationaction) / [iOS10](https://developer.apple.com/reference/usernotifications/unnotificationaction).

Once you've declared your actions and put them into categories, you can register them to Batch:

{% tabs %}
{% tab title="Swift" %}

```swift
BatchPush.setNotificationsCategories(<your categories Set>)
BatchPush.registerForRemoteNotifications()
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
[BatchPush setNotificationsCategories:<your categories NSSet>];
[BatchPush registerForRemoteNotifications];
```

{% endtab %}
{% endtabs %}

You can also use the native UIKit APIs to register them, as per Apple's documentation.

{% hint style="info" %}
You'll find an example implementation in Swift [here in our Batchcasts sample project](https://github.com/BatchLabs/batchcast-ios/blob/master/ios-swift/Batchcast/PushManager.swift#L40). It handles iOS 8/9/10 API changes and registers multiple actions in a category.
{% endhint %}

### Responding to actions

**iOS 10 (UserNotifications)**

If you're using the UserNotifications framework, iOS will call you back in your `UNUserNotificationCenterDelegate` instance you've set as the UNUserNotificationCenter delegate.

For that, you'll need to implement [userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:](https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter)

You'll also need to make sure you don't accidentally try to run your own code for actions that are not linked to action buttons:

{% tabs %}
{% tab title="Swift" %}

```swift
import UserNotifications
import Batch

@available(iOS 10.0, *)
@objc
class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {

    func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
        
        if response.actionIdentifier != UNNotificationDefaultActionIdentifier &&
            response.actionIdentifier != UNNotificationDismissActionIdentifier {
            // Perform your action here
        }
        
        BatchPush.handle(userNotificationCenter: center, didReceive: response)
        completionHandler()
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
@import UserNotifications;
@import Batch;

@interface NotificationCenterDelegateObjc : NSObject <UNUserNotificationCenterDelegate>
@end

@implementation NotificationCenterDelegateObjc

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)())completionHandler
{
    if (![UNNotificationDefaultActionIdentifier isEqualToString:response.actionIdentifier] &&
        ![UNNotificationDismissActionIdentifier isEqualToString:response.actionIdentifier]) {
        // Handle your action here
    }
    
    [BatchPush handleUserNotificationCenter:center didReceiveNotificationResponse:response];
    completionHandler();
}

@end
```

{% endtab %}
{% endtabs %}

**Earlier iOS versions**

As most legacy notifications callbacks, iOS will call you back on your app delegate.

You'll need to implement [application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:](https://developer.apple.com/reference/uikit/uiapplicationdelegate/1623021-application).

{% tabs %}
{% tab title="Swift" %}

```swift
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
    // Handle the action here
    completionHandler()
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler
{
    // Handle your action here
    completionHandler();
}
```

{% endtab %}
{% endtabs %}

#### Link with Custom Actions

A great way to clean up your code is to use [Custom Actions](https://doc.batch.com/developer/sdk/ios/advanced/custom-actions) and link them to your action buttons.\
It allows you to share code between the legacy and new callbacks, but also make your actions available to [Mobile Landings](https://doc.batch.com/developer/sdk/ios/mobile-landings) and any future feature that will support calling actions you've defined remotely.

Once you've created your [Custom Actions](https://doc.batch.com/developer/sdk/ios/custom-actions#registering-an-action), make sure you register your `UNNotificationAction` instances with the same identifier as the ones you've picked for your Batch Custom Actions.

Then, you can trigger them manually from the callbacks:

{% tabs %}
{% tab title="Swift" %}

```swift
// iOS 8/9
// In your application delegate

func application(_ application: UIApplication,
                 handleActionWithIdentifier identifier: String?,
                 forRemoteNotification userInfo: [AnyHashable : Any],
                 completionHandler: @escaping () -> Void) {
    if let identifier = identifier {
        var args: [String : AnyObject] = [:]
        if let payloadArgs = userInfo["action_args"] as? [String : AnyObject] {
            args = payloadArgs
        }
        BatchActions.perform(actionIdentifier: identifier, arguments: args)
    }
    
    completionHandler()
}

// iOS 10
import UserNotifications
import Batch

@available(iOS 10.0, *)
@objc
class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {

    func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
        
        if response.actionIdentifier != UNNotificationDefaultActionIdentifier &&
            response.actionIdentifier != UNNotificationDismissActionIdentifier {
            // If you want to add arguments to your action, this sample code will read from the "action_args" key of your custom payload
            var args: [String : AnyObject] = [:]
            if let payloadArgs = response.notification.request.content.userInfo["action_args"] as? [String : AnyObject] {
                args = payloadArgs
            }
            BatchActions.perform(actionIdentifier: response.actionIdentifier, arguments: args)
        }
        
        BatchPush.handle(userNotificationCenter: center, didReceive: response)
        completionHandler()
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
// iOS 8/9
// In your application delegate

- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
forRemoteNotification:(NSDictionary *)userInfo
  completionHandler:(void (^)())completionHandler
{
    if (identifier) {
        // If you want to add arguments to your action, this sample code will read from the "action_args" key of your custom payload
        NSDictionary *args = @{};
        NSObject *payloadArgs = userInfo[@"action_args"];
        if ([payloadArgs isKindOfClass:[NSDictionary class]]) {
            args = (NSDictionary*)payloadArgs;
        }
        
        [BatchActions performActionIdentifiedBy:identifier withArguments:args];
    }
    
    completionHandler();
}

// iOS 10
@import UserNotifications;
@import Batch;

@interface NotificationCenterDelegateObjc : NSObject <UNUserNotificationCenterDelegate>
@end

@implementation NotificationCenterDelegateObjc

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)())completionHandler
{
    if (![UNNotificationDefaultActionIdentifier isEqualToString:response.actionIdentifier] &&
        ![UNNotificationDismissActionIdentifier isEqualToString:response.actionIdentifier]) {
        // If you want to add arguments to your action, this sample code will read from the "action_args" key of your custom payload
        NSDictionary *args = @{};
        NSObject *payloadArgs = response.notification.request.content.userInfo[@"action_args"];
        if ([payloadArgs isKindOfClass:[NSDictionary class]]) {
            args = (NSDictionary*)payloadArgs;
        }
        
        [BatchActions performActionIdentifiedBy:response.actionIdentifier withArguments:args];
    }
    
    [BatchPush handleUserNotificationCenter:center didReceiveNotificationResponse:response];
    completionHandler();
}

@end
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
While Apple also calls action buttons added to notifications "Custom Actions", they are not directly linked to Batch's Custom Actions
{% endhint %}

### Handling both notification APIs at the same time

Starting with iOS 10, Apple has deprecated `UIUserNotificationAction` and related classes in favor of `UNNotificationAction`. Applications compatible with iOS version lower than iOS 10 will have to deal with compatibility.

There are three ways of dealing with this:

#### Using the right API depending on the OS version your app is running on.

The way is the recommended one. You can use Objective-C/Swift feature detection and register accordingly:

{% tabs %}
{% tab title="Swift" %}

```swift
if #available(iOS 10, *) {
    // We're on iOS 10
    registerActions()
} else {
    // We're on iOS 8/9
    registerLegacyActions()
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
if ([UNNotificationAction class]) {
    // We're on iOS 10
    [self registerActions];
} else {
    // We're on iOS 8/9
    [self registerLegacyActions];
}
```

{% endtab %}
{% endtabs %}

> Note: A full example of this implementation can be found in our [BatchCasts iOS Sample](https://github.com/BatchLabs/batchcast-ios/blob/master/ios-swift/Batchcast/PushManager.swift#L40)

#### Using UIUserNotificationAction regardless of the OS version using Batch.

If you're using Batch to register for notifications rather than UIKit methods, switching to the new `UserNotifications` framework has already been done for you.

Calling `BatchPush.registerForRemoteNotifications(<Categories set>)` with legacy (UI\*UserNotification) actions and categories will cause Batch to convert them to `UNNotificationAction` and `UNNotificationCategory` instances and use the new APIs transparently.

If you define categories with both `Default` and `Minimal` contexts for your action sets, Batch will complain in the logs, as this doesn't exist anymore with the new APIs. A long term fix is to migrate to the right API according to the iOS version you're running on, as described earlier.

#### Using UIUserNotificationAction regardless of the OS version using UIKit methods.

Registering notifications this way is supported by iOS, but should not be used: You should use the `UserNotifications` framework on iOS 10, since the old APIs have been deprecated.\
You'll also have better callback support for anything related to notifications in your app.

### Adding actions to your notification

You can now add actions to your iOS notifications by adding a category key to the payload of your push. Here is how it looks:

```json
{"aps":{"category":"CATEGORY_NAME"}}
```
