Local notification does not show custom action button

2.8k views Asked by At

To test local notifications, I wrote a test app with a single view controller.
In viewDidLoad, I set up the custom action, the notification category, and the userNotificationCenter delegate.
In viewDidAppear, I set the notification content, setup a trigger that fires after 5 sec, create the notification request, and add it to the notification center.

I expect the following:

Foreground mode:
When the app is launched, it should present after 5 sec the notification in foreground. Before, the delegate function „willPresent notification“ should be called.

Background mode:
If, however, the app is put into background by pressing the home button before the trigger fires, the notification should be presented in the home screen, and the delegate function „willPresent notification“ is not called.
After the notification has been presented, the user can tap the action button.
This should bring the app into foreground, and trigger the „didReceive response“ delegate function.

What happens is:
The action button in never shown, only title and body.
When I tap the body, the delegate function „didReceive response“ is triggered using the default action identifier.

The problem:
Why is the custom action button not shown?

Here is my code:

import UIKit
import UserNotifications

class ViewController: UIViewController, UNUserNotificationCenterDelegate {

    let userNotificationCenter = UNUserNotificationCenter.current()
    let categotyId = "categoryID"
    let actionID = "actionID"

    override func viewDidLoad() {
        super.viewDidLoad()

        userNotificationCenter.requestAuthorization(options: [.alert]) { (granted, error) in
            if granted {
                let okAction = UNNotificationAction(identifier: self.actionID, 
                                                    title: "OK", 
                                                    options: [])
                let category = UNNotificationCategory(identifier: self.categotyId,
                                                              actions: [okAction],
                                                              intentIdentifiers: [], 
                                                              options: [.customDismissAction])
                self.userNotificationCenter.setNotificationCategories([category])
                self.userNotificationCenter.delegate = self
            } else {
                print("local notifications not granted")
            }
        }
        userNotificationCenter.removeAllPendingNotificationRequests()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let content = UNMutableNotificationContent()
        content.title = NSString.localizedUserNotificationString(forKey: "Title", arguments: nil)
        content.body = NSString.localizedUserNotificationString(forKey: "Body", arguments: nil)
        content.categoryIdentifier = categotyId

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: (5), repeats: false)
        let request = UNNotificationRequest.init(identifier: "requestID", 
                                                 content: content, 
                                                 trigger: trigger)

        userNotificationCenter.add(request, withCompletionHandler: { (error) in
            if let error = error {
                print("Could not add notification request. Error: \(error)")
            }
        })
    }

    // MARK: - Notification Delegate

    // Will be called while app is in the foreground 
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification, 
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // Show alert to the user
        print("App in foreground. Show alert.")
        completionHandler([.alert])
    }

    // Should be called after the user tapped the action button
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let request = response.notification.request
        let requestID = request.identifier

        switch response.actionIdentifier {
        case actionID:
            print("Custom OK action triggered in background")
        case UNNotificationDefaultActionIdentifier:
            print("Default action triggered in background")
        default:
            print("Unknown action triggered in background, action identifier: \(response.actionIdentifier)")
        }
        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [requestID])

        completionHandler()
    }
}
2

There are 2 answers

3
Reinhard Männer On BEST ANSWER

Sorry for my question, but maybe somebody else has the same problem:
I simply did not know that first, only title/body is displayed:

enter image description here

However, I was not aware of the thin grey bar below the body. If this bar is pulled down, the custom action button appears:

enter image description here

1
Ved Rauniyar On

Update: As of iOS 10 beta 2, rich notifications are also available on pre-3D touch devices. Pull down on the regular notification to see it.

Make sure you are testing on a iPhone6s/iPhone6s plus simulator/device, it doesn't seem to work on pre-3D touch devices.

On a iPhone6 simulator, try to click and drag down on the stock notification you get and you should see your custom UI appear.